Is it possible to define default contexts for components, so that if the parent component does not pass down the context to the child, the child does not break? The only thing I can think of at the moment is
constructor() {
super();
if (!this.context.role) {
this.context.role = 'public';
}
}
Would that work?
Context is similar to props except that a change in context doesn't actually trigger a render. Usually context takes its value from a state or a store so that's usually not a problem. Another downside is unlike props, React doesn't provide a way to set a default value for it.
You can probably make a feature request in react's repo. A workaround would be to assign a default value to it before you use it. e.g:
class MyComponent {
// use this.getRole to access the role
getRole() {
return this.context.role || 'public';
}
}
Edit: your code snippet would not work because the context value could possibly get overriden by the parent during the render pipeline; plus it's not documented what would happen when you update context object directly.
Related
I'm reading through the Lifting Up State guide on the React docs.
It seems to me that this refactor creates a dependency in the TemperatureInput component on its parent, whatever it is, to provide data.
What if I wanted to use the TemperatureInput component somewhere else in my app where I don't need this kind of data binding? Wouldn't that dependency on the component's parent still exist, meaning that any component containing it would need to provide that state?
This seems to violate the idea that data should be held as close to where it's used as possible. The parent doesn't necessarily need to know the value of this component, but because of the component's design, the parent is forced to define and contain that value.
It seems to me that this refactor creates a dependency in the TemperatureInput component on its parent, whatever it is, to provide data.
It does. And the reason they did this for this case is because they want anything entered in one input to be mirrored in the other. Some component is going to need to manage the state to keep the two linked, and the parent component is the most natural place to do that.
As they said, this is called "lifting state up", but another description of what they've done is change TemperatureInput to be a controlled component. Controlled components are ones that are pretty dumb on their own, just taking their instructions from a parent component. This makes them very flexible, since that parent can implement whatever logic it likes. But it does mean that the parent has to implement that.
The opposite of a controlled component is an uncontrolled component. Uncontrolled components may take some initial values from the parent, but after that they handle things themselves. This makes them less flexible, since they have to already have the functionality inside them, but it means they can usually be used with less effort.
One place you may have encountered controlled vs uncontrolled components is in the standard dom elements, such as <input>s. An input can be used in a controlled manner by passing value and onChange props, which results it being controlled by the parent component. Or, you can pass in defaultValue, and let the input handle the rest.
Both styles (controlled and uncontrolled) have their uses, and it just depends on what the needs of your app are what you should use for this component. But you're not limited to one or the other: as the <input> component demonstrates, it's possible for you to support both modes of operation in a single component. So if you have one page where the temperature inputs need to be linked, and another where they don't, you could add some extra code to TemperatureInput so it can work with both:
const scaleNames = {
c: 'Celsius',
f: 'Fahrenheit'
};
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
temperature: props.defaultTemperature ?? ''
};
}
handleChange(e) {
if (this.props.onTemperatureChange) {
// Prop exists. We're in controlled component mode.
this.props.onTemperatureChange(e.target.value);
} else {
// Uncontrolled component
this.setState({temperature: e.target.value});
}
}
render() {
// If the prop exists, use it (ie, controlled component)
// Otherwise, use our state (uncontrolled component)
const temperature = this.props.temperature ?? this.state.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={temperature} onChange={this.handleChange} />
</fieldset>
);
}
}
I have a base component where i want to have an instance level variable that is updated by subclasses. The only way i can get this work is to add it to state which i don't want to do due to the fact that the value of this variable is NOT related to the UI so i don't want to trigger a re-render.
So take following component:
class BaseComponent extends React.Component {
constructor(props) {
super(props);
this.params = [];
}
addParam(name) {
this.params.push(name);
}
getUrl() {//this.params is always undefined
return format("{0}?", this.constructor.name, this.params.join("&");
}
}
So this.params is always undefined which makes sense since it is not part of state. This variable is related to the url and not the UI so when the value is updated i don't want to have to trigger a re-render by updating the state.
Not sure what the best approach here is?
Options i know of:
1) Add to state which will trigger re-render
2) Call forceUpdate() which will also trigger re-render
I don't like either of these options so i am hoping someone knows the correct way to do what i am trying to do.
I want to use connect for its performance optimizations and ease of use with mapStateToProps. I don't think I need to pass the store to the component from a <Provider> wrapper component to any child components, because I don't have any child components that will need the store; I want the store to be in one component, which is "Header.jsx". Mainly, I don't have any other components, other than the default React, and Material-UI, ones, that I want to use the store with.
How would I go about it? I tried to pass the store through defaultProps and used export default connect(mapStateToProps)(Header) but it keeps saying Uncaught Invariant Violation: Could not find "store" in the context of "Connect(Header)". I read that context is what's used to get props passed down the tree using the provider.
I'm new to Redux and React so if I'm going about this completely the wrong way, please let me know.
If using connect can't be done without a provider, how would I go about wrapping my class from the inside?
Example:
class componentName extends Component {
constructor(props) {
super(props);
};
render() {
return (
<h1>Hello World!</h1>
);
}
}
export default connect(mapStateToProps)(<Provider store={storeName}>componentName</Provider>); // Maybe something like this?
I think you simply cannot use connect() without the <Provider/> - it depends on that pattern being followed. If you want to use connect(), the connected component must be a descendant of the provider. The example you have suggested of including the <Provider/> in the call to connect() will not work, as:
a) That method takes a react component class, not an already instantiated react element, and
b) Even then, it creates a component class that, upon being instantiated/mounted, checks the context for a store, and this happens both above (in terms of DOM-hierarchy) the Provider that would create the context and before it is mounted and has a chance to create that context.
What's the reason you are against using the <Provider/>? Are you trying to prematurely optimize because you think including the provider at the root of your app will have some performance impact? If so, I think you may find there is no appreciable impact from including it, or if you are experiencing one, I would suggest that the problem may be in the setup of your reducers, not simply in the use of <Provider/>.
If you are absolutely set on not using the reducer, you could take your Store object (returned from wherever you are calling createStore()), and, in the componentDidMount() of your one component that needs it you could store.subscribe() to listen to state changes, then use store.getState() to get those changes and load them into state. But eventually, you'll find you are just reimplementing <Provider/>, although maybe without the context part.
TL;DR: Sounds like an XY problem
I'm new to ReactJs and trying to follow best practices. From my research, I've come across a couple of contradicting articles discussing how implementation should be.
Should state rely on the properties being passed down from a parent component? In the comparisons below, they are both following SRP, but not sure which is best. Would like your advice, Thanks!
1. -- Best Practices for Component State in React.js
First, and probably the most important of all, the state of a component should not depend on the props passed in. (see below for example of what we should not do)
class UserWidget extends React.Component {
// ...
// BAD: set this.state.fullName with values received through props
constructor (props) {
this.state = {
fullName: `${props.firstName} ${props.lastName}`
};
}
// ...
}
2. -- 7 architectural attributes of a reliable React component
Let's refactor to have one responsibility: render form fields and attach event handlers. It shouldn't know how to use storage directly.....The component receives the stored input value from a prop initialValue, and saves the input value using a prop function saveValue(newValue). These props are provided by withPersistence() HOC using props proxy technique.
class PersistentForm extends Component {
constructor(props) {
super(props);
this.state = { inputValue: props.initialValue };
}
// ...
}
3. -- In my case, I have something like the following (wondering if this is an acceptable implementation?) - Should state be handled in Tasks, or in another TasksWithPersistence type of component that sits between TasksWithData and Tasks?
export default function TasksWithData(TasksComponent) {
return class withData extends React.Component {
render() {
const tasks = TaskAPI.getTasks();
return (
<TasksComponent
tasks={tasks}
{...this.props}
/>
)
}
}
}
export default class Tasks extends React.Component {
state = {
tasks: [],
addItemInput: null
};
// ...
componentDidMount() {
this.updateComponentState({tasks: this.props.tasks});
}
componentDidUpdate() {
this.prepUIForNextAddition();
}
// ...
}
The gist of your question seems to revolve around the anti-pattern that is to take some props and duplicate it into the state. This, mutating of props, isn't the purpose of the state. Props are immutable, duping them to the state defeats this design.
The purpose of the state is to manage things that are specific to the React Component, i.e. tightly scoped to only that React component. For instance a showHide switch for something to display within the React component. Think of the state as a locally scoped variable if it helps.
Most of the time this anti-pattern of duping the props can be satisfied by a function within the React object. For example, your state.full_name variable becomes a named function, fullName, bound to the React Component. (all code examples are assuming JSX syntax)
Note: in JavaScript camelcase is the naming structure for functions and variables, I'm assuming you're coming from ruby based on the underscore naming convention. IMO it's best to stick to the convention of the language with which you're writing the code. This is why I use camelcased naming.
...
fullName() {
return this.props.firstName + " " + this.props.lastName
}
...
That function can then be called within the render of the component
# in render() portion of your React component, assuming jsx syntax
<p>Hello, {this.fullName()}</p>
Note: Remember that in ES6 you have to bind the methods in your react class in the constructor or use => syntax so that you can call them with this.
...
constructor(props) {
super(props);
this.fullName = this.fullName.bind(this);
}
...
You could also decompose the relevant parts to a new Component called FullName if it will be utilized by multiple components.
<FullName firstName={this.props.firstName} lastName={this.props.lastName} />
Technically, "the react way" is, at least in this author's opinion, to decompose this into another component for reusability. However component reuse needs to be weighed against the complexity added, i.e. don't optimize prematurely. So you may not want to take that too far at first. The times when it's necessary will emerge naturally.
A very broad generalization of React's props is that they are guaranteed, are immutable, and they flow down like a waterfall from the topmost component. If you need to update them, update them at the highest level where it makes sense.
In a soley React based approach, if you have something that a parent needs to be aware of, "lift" that part of the code up to the parent and vice versa bind it down to the child as a props, e.g. an AJAX function that calls an API. I think of it as trying to keep the components as dumb as possible.
The parent becomes the "source of truth" for the item you "lifted". The parent handles the updates, and then passes the results to the children. So in the parent, it may exist as a state variable and then get passed as props to the child object, which then passes it along as props to it's child object, etc. The children would update as the state gets changed in their parent when it propagates down through the chain as props.
If your app is React only, i.e. no stores that manage objects such as in the flux pattern or redux pattern, you may have to store things in the topmost objet's state which technically could be viewed as bad. As your system becomes more complex, this functionality would be better handled by flux or redux's parts.
Hope this helps!
There is a huge difference between example 1 & 2.
In example #1, the reason it's bad to set state from the those props in that way is that if the props change, the widget will not update. Best practices or not, that is just wrong and bad in any framework. In that particular case, there really is no point in even using the state. Props alone will suffice.
In example #2 the prop is only being used to give the state an initial value (The prop is even named initialValue), implying that further changes to the state will be controlled by the component regardless of prop changes. It does not break single responsibility principle to use props for an initial state, especially when it's explicitly use for that purpose.
I really don't see those two examples as being contradictory because they are completely different. Further, there is no rule in single responsibility principle that you can't set state from props, you just need to pay attention to the context in which you are doing it.
I want to get the parent component name inside the child component to display a error message when validating properties of that child component.
I'm creating a reusable component, so anyone can use my component inside their components. When they are using my component I want to display warning messages with the name of the parent component.
Is there a specific method to get parent name in react. Any kind of help would be appreciated.
Children can see the context they are composed in through:
this._reactInternalInstance._currentElement._owner._instance.__proto__.constructor.name
For example:
import React, { Component } from 'react';
class Warning extends Component {
render() {
return (
<div>{
"WARNING: " + this._reactInternalInstance._currentElement._owner._instance.__proto__.constructor.name + " has an error."
}</div>
);
}
}
export default Warning;
Do not believe this is parent-child communication. It is like standing in a room and seeing the containing walls from where you are. This is not the container (parent) communicating to you (child) but rather the child being context-aware.
Use this if you must, however, do note that it is part of React's internal instance and subject to change (do believe it is stable enough as of today).
you can get parent component from this._reactInternalFiber._debugOwner.type.name
I'm using React 18 with functional components, and ReactCurrentOwner was null for me. Instead, I used:
const stack = React["__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED"].ReactDebugCurrentFrame.getCurrentStack()
Inside my component render function (actually inside my custom hook), and it captured the complete stack trace of that component. Super useful for debugging what components triggered certain actions, since event handlers can reference the stack function-local var.
You can access the parent node of a component using:
this._reactInternalFiber.return.stateNode
Using react#^17.0.1, this worked for me in debug mode:
const parentName = this._reactInternals?._debugOwner?.elementType?.name;
If you are using other version of react, you can try doing console.log(this) and navigate through the object until you find what you want.
If you are using Functional Components (in React 16+), you can access the parent this way:
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner.return.type
And you can keep going further in the nested object to hit each ancestor.
Note: Using props would of course be the proper approach. However, there might be some few debugging use-cases (or global plugin?) for doing this. For example, why-did-you-render uses this approach here.