componentWillReceiveProps vs getDerivedStateFromProps - javascript

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.

Related

re-render component without setting state react js

I am deleting a record from db, for this I am calling an API. When I received an
API response of a successful deletion, I need to re-render all the component again like reload does. I tried it with this.forceUpdate and shouldComponentAgain but no luck.
I also tried with componentDidUpdate, it works but it is calling API infinite times. Below is my code how I used componentDidUpdate:
componentDidUpdate(){
let newThis = this;
getAccounts().then(function(response){
if(response.status===200){
newThis.setState({
Accounts:response.data
})
}
});
}
Please tell me the way to re-render like reload do, but without re-loading the whole page.
When using componentDidUpdate, you should always have a conditional setState which denotes that you need to perform something because the current state or current props is not equal to previous state or props.
componentDidUpdate always gets called whenever your component has updated. In your case what is happening is that you are calling setState without any condition which updates your component, and setState is called again causing an infinite loop in updating the component.
You should have something like this check here:
componentDidUpdate(prevProps, prevState){
let newThis = this;
if(newThis.props.{some-variable} !== prevProps.{some-variable}) {
getAccounts().then(function(response){
if(response.status===200){
newThis.setState({
Accounts:response.data
})
}
});
}
}
Adding conditional setState is very important here else you will end up in an infinite loop.
As per the official docs as well:
You may call setState() immediately in componentDidUpdate() but note
that it must be wrapped in a condition or you’ll cause an infinite
loop. It would also cause an extra re-rendering which, while not
visible to the user, can affect the component performance. If you’re
trying to “mirror” some state to a prop coming from above, consider
using the prop directly instead.
Hope it helps.
If you want to render the component again, then change the props from the parent. If props change then child component automatically going to render. And by this features, you can also render the selective component.

Which React lifecycle method is a good replacement for componentWillRecieveProps?

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 :)

Why use getDerivedStateFromProps when you have componentDidUpdate?

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.

Where to store values calculated from props in React

What is the best place to store the result of an expensive calculation from the React props which I use in render() but do not want to execute at each render() ?
constructor(props) {
super(props)
const result = this.doExpensiveCalculation(props)
}
componentWillReceiveProps(nextProps) {
// if nextProps differ from props
const result = this.doExpensiveCalculation(nextProps)
}
doExpensiveCalculation(props) {
// Some expensive stuff
}
render(){
// Use doExpensiveCalculation(this.props) here
}
The options are this and state but both I see rather unsatisfying. Is there a ready solution which uses memoisation?
On the other hand, should I worry about optimizing this ? I read that React can rerender component even if the props have not changed but does this happen often ?
You can handle the re-rendering in the lifecycle method of shouldComponentUpdate. Default value is always return true. By returning false there React will not re-render the component.
See the docs for more. Besides that, React only updates if a state change occurs since props are read-only.
Your options are to store it as you suggested or have a class with a static field to keep it there.
If all you want to do is perform the expensive calculation whenever you get new props, instead of on every render, you probably want componentWillReceiveProps:
componentWillReceiveProps() is invoked before a mounted component receives new props.
As far as where to store them, you can either store them in state, or as a property directly on the component instance. Either will work just as well.
You want to make sure compare values though, to avoid unnecessarily recomputing.
For example:
componentWillReceiveProps(nextProps) {
if (nextProps.someValue !== this.props.someValue) {
this.someResult = this.performExpensiveCalculation(nextProps.someValue);
}
}

ReactJS - Does render get called any time "setState" is called?

Does React re-render all components and sub components every time setState() is called?
If so, why? I thought the idea was that React only rendered as little as needed - when state changed.
In the following simple example, both classes render again when the text is clicked, despite the fact that the state doesn't change on subsequent clicks, as the onClick handler always sets the state to the same value:
this.setState({'test':'me'});
I would've expected that renders would only happen if state data had changed.
Here's the code of the example, as a JS Fiddle, and embedded snippet:
var TimeInChild = React.createClass({
render: function() {
var t = new Date().getTime();
return (
<p>Time in child:{t}</p>
);
}
});
var Main = React.createClass({
onTest: function() {
this.setState({'test':'me'});
},
render: function() {
var currentTime = new Date().getTime();
return (
<div onClick={this.onTest}>
<p>Time in main:{currentTime}</p>
<p>Click me to update time</p>
<TimeInChild/>
</div>
);
}
});
ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>
Does React re-render all components and sub-components every time setState is called?
By default - yes.
There is a method boolean shouldComponentUpdate(object nextProps, object nextState), each component has this method and it's responsible to determine "should component update (run render function)?" every time you change state or pass new props from parent component.
You can write your own implementation of shouldComponentUpdate method for your component, but default implementation always returns true - meaning always re-run render function.
Quote from official docs http://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate
By default, shouldComponentUpdate always returns true to prevent
subtle bugs when the state is mutated in place, but if you are careful to
always treat the state as immutable and to read-only from props and state
in render() then you can override shouldComponentUpdate with an
implementation that compares the old props and state to their
replacements.
Next part of your question:
If so, why? I thought the idea was that React only rendered as little as needed - when the state changed.
There are two steps of what we may call "render":
Virtual DOM renders: when render method is called it returns a new virtual dom structure of the component. As I mentioned before, this render method is called always when you call setState(), because shouldComponentUpdate always returns true by default. So, by default, there is no optimization here in React.
Native DOM renders: React changes real DOM nodes in your browser only if they were changed in the Virtual DOM and as little as needed - this is that great React's feature which optimizes real DOM mutation and makes React fast.
No, React doesn't render everything when the state changes.
Whenever a component is dirty (its state changed), that component and its children are re-rendered. This, to some extent, is to re-render as little as possible. The only time when render isn't called is when some branch is moved to another root, where theoretically we don't need to re-render anything. In your example, TimeInChild is a child component of Main, so it also gets re-rendered when the state of Main changes.
React doesn't compare state data. When setState is called, it marks the component as dirty (which means it needs to be re-rendered). The important thing to note is that although render method of the component is called, the real DOM is only updated if the output is different from the current DOM tree (a.k.a diffing between the Virtual DOM tree and document's DOM tree). In your example, even though the state data hasn't changed, the time of last change did, making Virtual DOM different from the document's DOM, hence why the HTML is updated.
Yes. It calls the render() method every time we call setState only except when shouldComponentUpdate returns false.
Even though it's stated in many of the other answers here, the component should either:
implement shouldComponentUpdate to render only when state or properties change
switch to extending a PureComponent, which already implements a shouldComponentUpdate method internally for shallow comparisons.
Here's an example that uses shouldComponentUpdate, which works only for this simple use case and demonstration purposes. When this is used, the component no longer re-renders itself on each click, and is rendered when first displayed, and after it's been clicked once.
var TimeInChild = React.createClass({
render: function() {
var t = new Date().getTime();
return (
<p>Time in child:{t}</p>
);
}
});
var Main = React.createClass({
onTest: function() {
this.setState({'test':'me'});
},
shouldComponentUpdate: function(nextProps, nextState) {
if (this.state == null)
return true;
if (this.state.test == nextState.test)
return false;
return true;
},
render: function() {
var currentTime = new Date().getTime();
return (
<div onClick={this.onTest}>
<p>Time in main:{currentTime}</p>
<p>Click me to update time</p>
<TimeInChild/>
</div>
);
}
});
ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>
It seems that the accepted answers are no longer the case when using React hooks (with primitive values, see comments on this answer for details). You can see in this code sandbox that the class component is rerendered when the state is set to the same value, while in the function component, setting the state to the same value doesn't cause a rerender.
https://codesandbox.io/s/still-wave-wouk2?file=/src/App.js
React 18 and beyond
Starting from React 18 all state updates are automatically batched. In this way, React groups multiple state updates into a single re-render for better performance.
So when you update your state, React always try to batch these updates in a group update, causing fewer render than setState calls. The behaviour is the same when using hooks.
You can read the very long explanation in the Automatic batching for React 18 announcement.
React 17 and below
In React 17 and below, only updates inside React event handlers are batched. Updates triggered from promises, setTimeout, native event handlers, or other events are not batched in React by default.
Another reason for "lost update" can be the next:
If the static getDerivedStateFromProps is defined then it is rerun in every update process according to official documentation https://reactjs.org/docs/react-component.html#updating.
so if that state value comes from props at the beginning it is overwrite in every update.
If it is the problem then U can avoid setting the state during update, you should check the state parameter value like this
static getDerivedStateFromProps(props: TimeCorrectionProps, state: TimeCorrectionState): TimeCorrectionState {
return state ? state : {disable: false, timeCorrection: props.timeCorrection};
}
Another solution is add a initialized property to state, and set it up in the first time (if the state is initialized to non null value.)
Not All Components.
the state in component looks like the source of the waterfall of state of the whole APP.
So the change happens from where the setState called. The tree of renders then get called from there. If you've used pure component, the render will be skipped.
Regardless of the well explained answers here, there may be other reasons why you don't see the change you expect post changing the props or state:
Watch out for any event.preventDefault(); in the code where you want to re-render by a state \ props change, as it will cancel any cancelable event following this statement.
You could use setState() only after comparing the current state value and the new one and they are different.

Categories