I have heard of getDerivedStateFromProps but it does not work how I want it to.
I have this code:
class App extends Component {
static getDerivedStateFromProps(props, state) {
console.log("Get derived state from props called");
return null;
}
render() {
return (
<button onClick={() => this.setState({})}>Change State</button>
);
}
}
Clicking the button calls getDerivedStateFromProps. This is not what I need. I want a function that is called only when new props are received. When internal state changes, this function should not be called.
I intend to use this in a modal form scenario. When the component receives an object as a prop, the component should translate this object into a form, then place that form into the state. If I use getDerivedStateFromProps, since it is called in every this.setState(), it would not reflect the changes in the form since whenever the user types, a this.setState() is fired, and instead of the changes being set into the state, the initial object is.
Which React lifecycle method should I use?
There are a couple of ways to approach this problem depending on different use cases.
If new props are added, those new props can be checked in the Child component and user defined functions can be called in case the new object exists.
getDerivedStateFromProps(props, state, prevProps) is a static method and this does not exist in the same, which means that you'll have to return a state based on various conditions. That is, let it work on default on most cases. On cases where you want to alter its functionality, place checks and return the modified state.
Hope it answers :)
Related
What exactly componentWillReceiveProps and getDerivedStateFromProps are subtle question for me. Because, I just came across to an issue while using getDerivedStateFromProps:
// Component
state = {
myState: []
}
// Using this method works fine:
componentWillReceiveProps(nextProps) {
this.setState({
myState: nextProps.myPropsState
})
}
// But using this method will cause the checkboxes to be readonly:
static getDerivedStateFromProps(nextProps,prevProps) {
const { myPropsState: myState } = nextProps
return {
myState
}
}
// And here's checkbox
<input type="checkbox" id={`someid`}
onChange={(e) => this.handleMethod(e, comp.myState)}
checked={myState.indexOf(comp.myState) > -1} />
React version: 16.4.1
getDerivedStateFromProps is not a direct alternative to componentWillReceiveProps, purely because of the fact that its called after every update, whether its the change in state or change in props or re-render of parent.
However whatever is the case, simply returning the state from getDerivedStateFromProps is not the right way, you need to compare the state and props before returning the value. Else with every update the state is getting reset to props and the cycle continues
As per the docs
getDerivedStateFromProps is invoked right before calling the render
method, both on the initial mount and on subsequent updates. It should
return an object to update the state, or null to update nothing.
This method exists for rare use cases where the state depends on
changes in props over time. For example, it might be handy for
implementing a <Transition> component that compares its previous and
next children to decide which of them to animate in and out.
Deriving state leads to verbose code and makes your components
difficult to think about. Make sure you’re familiar with simpler
alternatives:
If you need to perform a side effect (for example, data fetching
or an animation) in response to a change in props, use
componentDidUpdate lifecycle instead.
If you want to re-compute some data only when a prop changes, use
a memoization helper instead.
If you want to “reset” some state when a prop changes, consider
either making a component fully controlled or fully uncontrolled
with a key instead.
P.S. Note that the arguments to getDerivedStateFromProps are props and state and not nextProps and prevProps
To get into more details,
In order to make changes based on props change, we need to store prevPropsState in state, in order to detect changes. A typical implementation would look like
static getDerivedStateFromProps(props, state) {
// Note we need to store prevPropsState to detect changes.
if (
props.myPropsState !== state.prevPropsState
) {
return {
prevPropsState: state.myState,
myState: props.myPropsState
};
}
return null;
}
Finally, I resolved my issue. It was a painful debugging:
// Child Component
// instead of this
// this.props.onMyDisptach([...myPropsState])
// dispatching true value since myPropsState contains only numbers
this.props.onMyDispatch([...myPropsState, true])
This is because, I have two conditions: 1) on checkbox change (component) 2) on reset button pressed (child component)
I was needing to reset the states when reset button is pressed. So, while dispatching state to the props for reset button, I used a boolean value to know it's a change from the reset. You may use anything you like but need to track that.
Now, here in the component, I found some hints to the differences between componentWillReceiveProps and getDerivedStateFromProps after debugging the console output.
// Component
static getDerivedStateFromProps(props, state) {
const { myPropsState: myState } = props
// if reset button is pressed
const true_myState = myState.some(id=>id===true)
// need to remove true value in the store
const filtered_myState = myState.filter(id=>id!==true)
if(true_myState) {
// we need to dispatch the changes to apply on its child component
// before we return the correct state
props.onMyDispatch([...filtered_myState])
return {
myState: filtered_myState
}
}
// obviously, we need to return null if no condition matches
return null
}
Here's what I found the results of the console output:
getDerivedStateFromProps logs immediately whenever props changes
componentWillReceiveProps logs only after child propagates props changes
getDerivedStateFromProps doesn't respond to the props changes ( I meant for the dispatch changes as in the example code)
componentWillReceiveProps responds to the props changes
Thus, we needed to supply the changes to child component while using getDerivedStateFromProps.
The process of pasting true value in the state I require because getDerivedStateFromProps handle all the changes unlike componentWillReceiveProps handles only the child component dispatches the changes to the props.
By the way, you may use custom property to check if it is changed and update the value if getDerivedStateFromProps but for some reason I have to tweak this technique.
There might be some confusion on my wording but I hope you'll get it.
From the react docs:
Note that this method is fired on every render, regardless of the cause. This is in contrast to UNSAFE_componentWillReceiveProps, which only fires when the parent causes a re-render and not as a result of a local setState.
You are effectively overriding your state with the current props every time after calling setState(). So when you check a box (e) => this.handleMethod(e, comp.myState) is called which is assume calls setState() to update the checked state of the checkbox. But after that getDerivedStateFromProps() will be called (before render) that reverts that change. This is why unconditionally updating state from props is considered an anti-pattern.
I have a global service widgetService which holds data for a number of widgets, each identified by a widgetID. Each widget's data can change at any time. I want to display a widget with a React component, say WidgetReactComponent.
The react component shall take a widget ID as property and get the information to display from the widget service. A widget's data can be queried from the widget service with the method getWidgetData(widgetID). And in order to be able to publish data changes, it also offers two methods: addListenerForWidget(widgetID, listener) and removeListenerForWidget(widgetID, listener).
When assuming that the property is set once and never changed, this can be achieved like this, following React's recommendations:
class WidgetReactComponent extends Component {
constructor() {
super();
this.state = {
data: widgetService.getWidgetData(this.props.widgetID)
};
this._onDataChange = this._onDataChange.bind(this);
}
_onDataChange(newData) {
this.setState({data: newData});
}
componentDidMount() {
// React documentation: "This method is a good place to set up any subscriptions."
widgetService.addListenerForWidget(this.props.widgetID, this._onDataChange);
}
componentWillUnmount() {
// React documentation: "Perform any necessary cleanup in this method, such as [...] cleaning up any subscriptions that were created in componentDidMount()."
widgetService.removeListenerForWidget(this.props.widgetID, this._onDataChange);
}
render() {
return <div className="Widget">{this.state.data.stuff}</div>;
}
}
The component may then be used like this:
<ReactWidgetComponent widgetID={17} />
However, the widgetID property may change at any time, and the component has to handle this in order to function properly under all circumstances. By react's recommendation, this should be handled by setting the state based on properties using the static getDerivedStateFromProps function. But since it is static, I do not have access to the component and cannot change the listeners accordingly.
One way to work around this would be to store the widgetID in the state, and then use the lifecycle method componentDidUpdate to detect the change, like this:
constructor() {
super();
this._onDataChange = this._onDataChange.bind(this);
}
static getDerivedStateFromProps(nextProps) {
return {
widgetID: nextProps.widgetID,
data: widgetService.getWidgetData(nextProps.widgetID)
};
}
componentDidUpdate(prevProps, prevState) {
if (prevState.widgetID !== this.state.widgetID) {
widgetService.removeListenerForWidget(prevState.widgetID, this._onDataChange);
widgetService.addListenerForWidget(this.state.widgetID, this._onDataChange);
}
}
However, componentDidUpdate won't be called when shouldComponentUpdate returns false. This doesn't feel like a safe way of doing this. Also I believe the listeners will be wrong for the entire timespan between the property change and the update's completion. How could I safely implement this?
You don't need to store widgetID in state, you can compare prevProps with this.props:
componentDidUpdate(prevProps, prevState) {
if (prevProps.widgetID !== this.props.widgetID) {
widgetService.removeListenerForWidget(prevProps.widgetID, this._onDataChange);
widgetService.addListenerForWidget(this.props.widgetID, this._onDataChange);
}
}
You will also need to add the listener in componentDidMount as componentDidUpdate is not called on first rendering:
componentDidMount() {
widgetService.addListenerForWidget(this.props.widgetID, this._onDataChange);
}
Regarding your concerns:
componentDidUpdate won't be called when shouldComponentUpdate returns false
From the docs:
Use shouldComponentUpdate() to let React know if a component’s output is not affected by the current change in state or props.
So if you decided to not update the component when this.props.widgetID changes, then you are violating the assumption/purpose of shouldComponentUpdate and should not expect your widget listener to be updated.
A lot of things will not work as intended if you misuse shouldComponentUpdate anyway (eg. component not updated to reflect new data), so relying on an API being used correctly as per official docs is a necessity to achieve simplicity, rather than something to be avoided.
the listeners will be wrong for the entire timespan between the property change and the update's completion
By this logic, when you update some displayed data in an event handler, you can also claim that the data displayed is wrong for the entire timespan between the event and the re-rendering. You can even claim that your text editor is displaying the wrong data between the time you press a keyboard key and rendering of the key on the screen.
I'm confused about the new lifecycle of react 16, getDerivedStateFromProps use case. Take below code for example, getDerivedStateFromProps is not needed at all since I can achieve what I want with componentDidUpdate.
export class ComponentName extends Component {
//what is this for?
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.filtered !== prevState.filtered && nextProps.filtered === 'updated') {
return {
updated: true //set state updated to true, can't do anything more?
};
}
return null;
}
componentDidUpdate(prevProps, prevState) {
if(prevProps.filtered !== this.state.filtered && this.state.filtered === 'updated'){
console.log('do something like fetch api call, redirect, etc..')
}
}
render() {
return (
<div></div>
);
}
}
From this article:
As componentWillReceiveProps gets removed, we need some means of updating the state based on props change — the community decided to introduce a new — static — method to handle this.
What’s a static method? A static method is a method / function that exists on the class not its instance. The easiest difference to think about is that static method does not have access to this and has the keyword static in front of it.
Ok, but if the function has no access to this how are we to call this.setState? The answer is — we don’t. Instead the function should return the updated state data, or null if no update is needed
The returned value behaves similarly to current setState value — you only need to return the part of state that changes, all other values will be preserved.
You still need to declare the initial state of the component (either in constructor or as a class field).
getDerivedStateFromProps is called both on initial mounting and on re-rendering of the component, so you can use it instead of creating state based on props in constructor.
If you declare both getDerivedStateFromProps and componentWillReceiveProps only getDerivedStateFromProps will be called, and you will see a warning in the console.
Usually, you would use a callback to make sure some code is called when the state was actually updated — in this case, please use componentDidUpdate instead.
With componentDidUpdate you can execute callbacks and other code that depends on the state being updated.
getDerivedStateFromProps is a static function and so has no access to the this keyword. Also you wouldn't have any callbacks placed here as this is not an instance based lifecycle method. Additionally triggering state changes from here could cause loops(e.g. with redux calls).
They both serve different fundamental purposes. If it helps getDerivedStateFromProps is replacing componentWillReceiveProps.
getDerivedStateFromProps basically can save you one render cycle. Let's say due to some props change, you have to update some state and the UI responds with the new state. Without getDerivedStateFromProps, you have to wait until componentDidUpdate is invoked to make the prop comparison and call setState to update the UI. After that, componentDidUpdate is invoked again and you have to pay attention to avoiding endless rendering. With getDerivedStateFromProps, the UI update just happens earlier.
I am developing a web page using React, and have some questions about the "Lifting Stat Up" example (and I paste the link for complete code of it here for quick reference), which is inconsistent with what I encountered in my own project.
My question is about this part:
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange} />
while user input new values, the celsius will be re-calculated (triggered by a callback function in TemperatureInput which calls handleCelsiusChange, and then render is triggered), and passed as one of props to TemperatureInput. But inside the definition of TemperatureInput, why componentWillReceiveProps is not defined? In my practice, if the props is assigned in constructor only, it will not be updated automatically if value changed. As the document said, it will not create a new element but just update the existing one for performance. If there is no new instance is created, the constructor would not be invoked, so the this.props will not be updated without a componentWillReceiveProps function.
All these above is consistent to my practice, but not to the example. Why componentWillReceiveProps is not required in the example to make this.props updated?
The constructor in this case is not doing anything special with the props, it is just calling the React.Component constructor with super() which is what it would do anyway. If it was setting some state from props for example, then you would need a componentWillReceiveProps, as you would need to update the state when the props change.
When the component gets new props, the constructor is not invoked again, as it is already an instance, instead react updates the props on the component instance, and calls the render() again, so the props in render() will be the updated props. If you had a componentWillReceiveProps defined, it will be called before the call to render();
I am studying the principles of react.
According to some reviews, some people says is better to keep your component stateless, what does it mean?
But other people says, that if you need to update your component, then you should learn how to set your state to the proper state.
I saw this.props / this.setProps and this.state / this.setState and I am confuse with that.
Something I am trying to figure is, how can I update a component by itself and not from a parent component? should I use props or state in this case?
I already read some docs about props and state, what I don't have clear, is: when to use one or another ?
Props vs. state comes down to "who owns this data?"
If data is managed by one component, but another component needs access to that data, you'd pass the data from the one component to the other component via props.
If a component manages the data itself, it should use state and setState to manage it.
So the answer to
how can I update a component by itself and not from a parent component? should I use props or state in this case?
is to use state.
Props should be considered immutable and should never be changed via mutation. setProps is only useful on a top-level component and generally should not be used at all. If a component passes another component a property, and the first component wants the second to be able to change it, it should also pass it a function property that the second component can call to ask the first component to update its state. For example:
var ComponentA = React.createClass({
getInitialState: function() {
return { count: 0 };
},
render: function() {
return <Clicker count={this.state.count} incrementCount={this.increment} />;
},
increment: function() {
this.setState({count: this.state.count + 1});
}
});
// Notice that Clicker is stateless! It's only job is to
// (1) render its `count` prop, and (2) call its
// `incrementCount` prop when the button is clicked.
var Clicker = React.createClass({
render: function() {
// clicker knows nothing about *how* to update the count
// only that it got passed a function that will do it for it
return (
<div>
Count: {this.props.count}
<button onClick={this.props.incrementCount}>+1</button>
</div>
);
}
});
(Working example: https://jsbin.com/rakate/edit?html,js,output)
For and object-oriented programming analogy, think of a class/object: state would be the properties you put on the class; the class is free to update those as it sees fit. Props would be like arguments to methods; you should never mutate arguments passed to you.
Keeping a component "stateless" means that it doesn't have any state, and all its rendering is based on its props. Of course, there has to be state somewhere or else your app won't do anything! So this guideline is basically saying to keep as many components as possible stateless, and only manage the state in as few top-level components as possible.
Keeping components stateless makes them easier to understand, reuse, and test.
See A brief interlude: props vs state in the React docs for more information.
Use state when you know the variable value is going to affect the view. This is particularly critical in react, because whenever the state variable changes there is a rerender(though this is optimized with the virtual DOM, you should minimize it if you can), but not when a prop is changed (You can force this, but not really needed).
You can use props for holding all other variables, which you think can be passed into the component during the component creation.
If you have want to make a multi-select dropdown called MyDropdown for example
state = {
show: true,
selected:[],
suggestions:this.props.suggestionArr.filter((i)=>{
return this.state.suggestions.indexOf(i)<0;
})
}
props={
eventNamespace:'mydropdown',
prefix : 'm_',
suggestionArr:[],
onItemSelect:aCallbackFn
}
As you can see, the objects in the state variable are going to affect the view some way or the other.
The objects in the props are mostly objects which should remain the same throughout the component life cycle. So these objects can be callback functions, strings used to namespace events or other holders.
So if you do want to update the component by itself, you need to have to look into how componentWillRecieveProps ,componentWillUpdate, componentDidUpdate and componentShouldUpdate works. More or less, this depends on the requirement and you can use these lifecycle methods to ensure that the rendering is within the component and not in the parent.