How to ignore a property update of the state in ReactJs? - 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.

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.

componentWillRecieveProps vs getDerivedStateFromProps

componentWillRecieveProps is deprecated and getDerivedStateFromProps is being advocated to use.
But componentWillRecieveProps gets called when the props get changed but getDerivedStateFromProps is getting called on any state change even.
My requirement is to reset state on props change. So how would I get to know if the props really got changed.
I am not in favor of storing the props value in state as it would unnecessarily make the state bulkier and I still not have given state management(Redux or Context API) a thought
Please advise
how would I get to know if the props really got changed.
Compare previous prop with new prop.
For simplest use case, just use componentDidUpdate(), with prop comparing:
componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
According to React Doc, it is OK to perform side effect or setState() inside componentDidUpdate().
And setState() can cause re-rendering, which affects component performance. When performance in this case is actually a thing, read on: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#when-to-use-derived-state

Haze about 'setState' in React official doc

I am reading react.js official docs.
Here is one of them.
I am confused about this paragraph:
setState() will always lead to a re-render unless
shouldComponentUpdate() returns false. If mutable objects are being
used and conditional rendering logic cannot be implemented in
shouldComponentUpdate(), calling setState() only when the new state
differs from the previous state will avoid unnecessary re-renders.
Question: Why calling setState() will avoid unnecessary re-renders if mutable objects are being used and conditional rendering logic cannot be implemented in shouldComponentUpdate()?
shouldComponentUpdate deep-dive might help you
Calling setState() will always trigger a re-render of the component(unless you have defined shouldComponentUpdate()). But keeping performance and efficiency in mind, we want the component to re-render only if the state value has actually changed.
This is where shouldComponentUpdate() lifecycle method comes into play.
In this method, a check can be done to determine whether the state value has changed. If state has changed, returns true and the component re-renders.
Mutable objects refer to Arrays, objects etc in javascript. Numbers and Strings are immutable.
Mutable object example:
const a = [1,2]; // memory address of a is 0xff456e
a.push(3); // memory address of a is 0xff456e(same)
Immutable object example:
let b = 'Hello'; // memory address of b is 0xee789e
b = 'World'; // memory address of b is 0xee789f(different because its a new object created with value 'World')
If your component is a PureComponent, then react by default will define the shouldComponentUpdate() to reduce unnecessary re-renders. But you need to use immutable objects for that to work correctly(i.e create a new array or object manually and assign to your state, else your component won't re-render correctly).
So, the point they are making is this : Don't call setState() unless the state value has actually changed if your using a normal react component without a shouldComponentUpdate() check to avoid situations like this:
this.setState({ items: [1, 2, 3] }); // re-render once
// lot of code goes here
this.setState({ items: [1, 2, 3] }); // re-render twice
Note: Although the value of items is unchanged, there is a wasteful re-render of the component caused as shown above. So, avoid setting state if there is no change of value.
I think you read that wrong.
It's a two term conditional:
IF
mutable objects are being used
AND
conditional rendering logic cannot be implemented in shouldComponentUpdate()
THEN
[you should call] setState() only when the new state differs from the previous state [so it] will avoid unnecessary re-renders.
(Alteration by me are in brackets.)
Basically, it means that it's up to you to test if you should call setState if you can't rely on React's internal tests due to technical limitations on your side.
The doc wanted to say that in the following conditions, the re-render will not happen:
If shouldComponentUpdate hook returns false.
If mutable objects are being used.
If some conditional logic is not used for re-render like force update inside shouldComponentUpdate hook.
If the effect of calling setState method only change the previous state value.
BTW, I'm still not satisfied that I couldn't make clear enough. :(:

Is React smart enough to bypass setState if I pass in the same value as the current state?

I am setting multiple states in setState, but one of the states will not always change though. Is React smart enough to bypass setting that state it remains unchanged?
No, by default React will update your component with every setState call.
You can use shouldComponentUpdate to let the component know that it shouldn't update when certain conditions are met.
shouldComponentUpdate(nextProps, nextState) {
// If these match, we know we don't need a redraw
if (this.state.foo === nextState.foo) return false;
// Otherwise, continue with the update
return true;
}
Note that shouldComponentUpdate doesn't affect child-components, who will each check their own update conditions when they receive new props or new state.
You can read more about the React component lifecycle over here.

PureComponent's shallowCompare is not working as expected

I am trying to avoid unnecessary re-rendering of a child component (Review) by extending it from PureComponenet.
This component is being called from the parent component (Reviews), which is not a PureComponent.
By debugging I am seeing that whenever a change occurs to one of the review, all the reviews related to that product are getting re-rendered. Obviously Reviews' props gets updated and it passes individual Review's relevant props to individual Review components.
where I am also seeing this,
nextProps.reviewer === this.props.reviewer_info -> false
nextProps.reviewer_info = []
this.props.reviewer_info = []
I believe that pureComponent uses shallowCompare and it should return true while comparing 2 empty arrays, is that correct?
Does my parent component also needs to be a PureComponent in order to stop unnecessary rendering of my child components? Is that the missing part?
My review component has other child components, which I know also should be declared pureComponents. (I will update them but does this stop Review component from achieving pureComponent's benefits?)
Or above 2 are not the correct reasons for the re-rendering in-spite of using pureComponent and I am missing some other part?
Does my parent component also needs to be a PureComponent in order to stop unnecessary rendering of my child components?
No, that's not necessary.
I believe that pureComponent uses shallowCompare and it should return true while comparing 2 empty arrays, is that correct?
It is not. The shallow comparison uses === to compare the old and new value of each property on props and state. But as you can see in a browser console [] === [] is false. This is because each [] expression creates a new array object that is unequal to the others.
As a consequence, a simple array or object literal in a property (e.g. <Review prop={[]}/>) will trigger a rerender of Review whenever Reviews is rendered, negating the effect of the PureComponent.
To enjoy the optimized behavior, you will need to make sure that each non-primitive prop keeps referring to the same object instance as long as its value doesn't change (e.g. by keeping it in Review's state). Furthermore, if there is a change, you will need to create a fresh top-level object for that prop, rather than modify the existing one in-place (so don't use push or assign directly to the prop objects properties.) The immutable-js package can be helpful with this, but es6 spread operators often work fine as well.
My review component has other child components, which I know also should be declared pureComponents. (I will update them but does this stop Review component from achieving pureComponent's benefits?)
It shouldn't affect Review (unless you create the children in the render method of Reviews, like <Review ..>..<Child ../>..</Review>, in which case props.children will change every time and trigger rerendering.)
You can use shouldComponentUpdate() alternatively if your prop/state is an array:
shouldComponentUpdate(nextProps, nextState) {
return JSON.stringify(this.props.reviewer_info) !== JSON.stringify(nextProps.reviewer_info);
}

Resources