componentWillUpdate -- is it safe to modify nextProps or nextState? - reactjs

What are the implications of doing this?
class Foo extends React.Component {
componentWillUpdate(nextProps, nextState) {
nextState.bar = transformSomehow(nextState.foo);
}
}
My gut tells me that this is wrong, but I haven't found any documentation saying that nextProps and nextState should be read-only, nor have I found anything saying that this is ok.
The idea here is that I want some kind of sanitized/transformed/modified version of data from state. There are probably plenty of other (better) ways to do this, but is this also a viable option?

I would say that this is not an ideal approach because you are trying to mutate state. You should not call setState from within componentWillUpdate, nor should you try to update your state object at this point. This stage is used more for animations, DOM manipulations, etc.
If you want to set some other vars based on nextState, go for it. If you want to work ad hoc copies of state to get something done, all good. But I think that attempting to mutate state in this method runs counter to the intended use of this lifecycle method. From the docs on componentWillUpdate:
Note that you cannot call this.setState() here. If you need to update state in response to a prop change, use componentWillReceiveProps() instead.

Related

Why shouldn't I use shouldComponentUpdate in ReactJS?

Previously we used componentWillReceiveProps() to update a component on props change. Let's say have a component enable or disable an input field depending on some state in App.js
Now this is marked unsafe and
https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops
Explains several alternatives to use instead.
But I wonder why none of these mention the use of shouldComponentUpdate() for this case. I could use
shouldComponentUpdate(nextProp){
this.setState({
active: nextProp.active
});
return true;
}
To set the state of the component active which removes the disabled from an input field.
From reading the docs I couldn't understand why they suggest rather complicated memoization helpers or the componentDidUpdate lifecycle (which only provides previousProbs and thus state that's older than the current state).
Is there a reason not to do it like in my example?
shouldComponentUpdate is used in the case that a prop change is causing a re-render that you don't want - you may only care about certain props that affect the rendered view. You can manually inspect nextProps for changes to those specific props and decide to render or not.
You should not modify state (or anything else) inside of shouldComponentUpdate.

How to create an event with state update (by getDerivedStateFromProps)

I am trying to change componentWillReceiveProps method to static getDerivedStateFromProps and can't figure out how to use the keyword this inside the getDerivedStateFromProps function.
Originally, I wanted to use this.props.history.push() inside the getDerivedStateFromProps but could not figure out how.
I then tried returning state (as getDerivedStateFromProps is supposed to do) and could not create an event with it. A brief look at the code will make my question more comprehensible.
static getDerivedStateFromProps(nextProps, prevState){
if(nextProps.passwordResetResult.message==="Password reset
Successfully")
{
alert("Password reset successfully please login again");
this.props.history.push('/home')
}
}
I know I am supposed to return state at the end; but I didn't find it useful for my purpose, as don't understand how do I create an event which will be triggered on state change after return and then trigger this.props.history.push().
Above code caused error
this.props is undefined.
I understand I can't use this keyword inside getDerivedStateFromProps; am I correct in assuming that?
getDerviedStateFromProps is a static function, so you can't access this from it, static functions are bound to the class itself and not the instance of the component. I believe its intentionally done this way to avoid side effects.
So Instead of using this.history.push you should use nextProps.history.push which you get in the lifecycle hook arguments.
But as for your use-case since you said you don't really need to return state, it means you don't really need derived state because you don't change internal state based on specific props. You should use componentDidUpdate instead for side-effects, see https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops

How to intercept setState function to capture individual state changes?

I am implementing activity logging in a React application to track users activity and a practical way would be to simply log each change to the component state. The state object is large in my scenario, so I would like to capture only the changed elements, which would also be easier to read in the log. To do so it would be ideal to intercept every setState call so that I can get the specific pieces that changed in the state. I have most of all the state in a higher level component so I would have to do this only in one component basically.
I don't want to do this in every call to setState across all my component's code. I want to be able intercept it for once an for all at a component level.
Is there an elegant way of intercepting setState that does not involve creating an alternate setState function stub?
Any tips greatly appreciated. Thanks.
The following lifecycle methods will be showing the changes when state/props changes in a component. You can use the state object to determine the activity logging.
// Happens before the component update
componentWillUpdate(object nextProps, object nextState)
// Happens after the component update
componentDidUpdate(object prevProps, object prevState)
For example,
componentDidUpdate(prevProps, prevState) {
if(prevState.isLoadingData !== this.state.isLoadingData) {
writeToActivityLog('user-requested-new-data');
}
}
The state object is large in my scenario, so I would like to capture
only the changed elements, which would also be easier to read in the
log.
For this scenario, you can make use of utility library like lodash to get all the changed properties or do a deep equal between this.state and prev/nextState. Even then you may require conditions to log different events.

How to ignore a property update of the state in ReactJs?

I know that you can use shouldComponentUpdate to decide if render should be called or not. Citing the docs :
By default, shouldComponentUpdate always returns true to prevent
subtle bugs when state is mutated in place, but if you are careful to
always treat 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.
But my trouble is a bit different. I calculate a value in componentDidUpdate because a need a first render to be able to do my computation, and i'd like to store this value on the state to be able to use it later on in the render function, but I dont want to trigger a render when I modify it.
How would you do it ? Is this the proper way to go ?
Should I store this value somewhere else ? Directly on the this ?
Compute your state before component is actually updated.
componentWillUpdate(nextProps, nextState) {
nextState.value = nextProps.a + nextProps.b;
}
Your component will get computed value with other changes and will update only one time.

Setting state by assignment

In react, is there a reason why someone would want to set the state of a variable by assignment instead of calling setState(...)
Example:
// accessing state var directly
this.state.myVar = 'changed state'
// instead of calling setState
this.setState({myVar: 'changed state'})
To me this seems like an anti-pattern. But maybe there's a good explanation why sometimes it's necessary?
It's necessary, because React has to know wether this.state is considered mutated. There is no dirty-checking in React. The state object is considered dirty when this.setState is called, and no further comparisons are made to its previous state. This answer might help explain this in more detail: Why is React's concept of Virtual DOM said to be more performant than dirty model checking?
Setting (mutating) the state directly will work in this case:
this.state.myVar = 'changed state'
However, it should be avoided according to the React docs:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
The main problem with mutating the state is that it prevents some of the React lifecycle methods from working. For example, React's shouldComponentUpdate() method is often used to speed up the app when dealing with a large number of components. The method allows you to skip re-rendering a component if the state has been updated:
// Return false if you want to skip the `render()` method
shouldComponentUpdate: function(nextProps, nextState) {
return this.state.myVar === nextState.myVar;
}
The above will not work if you are mutating the state. this.state.myVar and nextState.myVar references are the same and therefore the above will always return true.

Resources