React will not create a new nested component as expected - reactjs

In my application I have a page change mechanism that uses one of the properties of parent component state.
So I do something like:
class mainComponent{
state={
mypage:null
}
onclickhandler(page){
this.setState({mypage:page});
}
render(){
return <div>{page}</div>
};
}
In my "page" property I have various input fields and other components.
When I change the page, all inputs get updated with new values. However, my custom nested components are never re-created.
Instead, reach just calls componentDidUpdate on them.
Is there a way to force it to construct a new nested component somehow?

React won't recreate component when it's not needed - it's a part of optimizations and general way react works.
When 'new', 'expected' component exists (the same type, the same or no key id) it only receives updated props.
Updating props sometimes doesn't force rerendering (it's guaranteed for state updates). PureComponent compares props shallowly, you can check shouldComponentUpdate (should return true to force render).

Related

Refresh only the component that changed from the parent

I have a react component that is a list, let's call it List, it has a state named items that is an array of object of the form :
{ id: String, name: String }
its render function is something like
render () {
return (
<ul>
{ items.map((item) => {
return (
<Item key={item.id} item={item} />
);
})}
</ul>
);
}
with Item being a react component too, of course.
So now, my question: let's say that at one point, I know that the data changed (it can only be a name change, of course), how can I trigger Item to refresh based on its key from the parent?
I found two solutions so far :
refreshing the whole state, but I don't like it cause it seems overkill.
maintaining an array of references to the child components, but I don't like it cause it feels like a waste of memory.
so what's a 'reactier' way of doing this?
If the items array changed then the render of the Parent is called, since the keys Item components are same, the component is not re-created but its componentWillReceiveProps function is called and the Item component is re-rendered.
What you can do as an optimization is to create a Item component as a Pure component which you can do by extending React.PureComponent. React.PureComponent’s shouldComponentUpdate() only shallowly compares the objects so if the Props haven't changed a rerender on child is not called.
According to the DOCS:
If your React component’s render() function renders the same result
given the same props and state, you can use React.PureComponent for a
performance boost in some cases.
React.PureComponent’s shouldComponentUpdate() only shallowly
compares the objects. If these contain complex data structures, it may
produce false-negatives for deeper differences. Only extend
PureComponent when you expect to have simple props and state, or use
forceUpdate() when you know deep data structures have changed. Or,
consider using immutable objects to facilitate fast comparisons of
nested data.
Furthermore, React.PureComponent’s shouldComponentUpdate() skips
prop updates for the whole component subtree. Make sure all the
children components are also “pure”.
By implementing shouldComponentUpdate(nextProps, newState) in your React components you can calculate whether or not the component needs to update. So if you send a whole new object in, this should trigger already in its default implementation (they don't do a deep-compare), but if the property input.name is changed then it will not detect this. So you could implement this yourself like this:
shouldComponentUpdate(nextProps, newState) {
return this.props.name === nextProps.name; // use === to compare types too!
}
Now I'm a bit rusty on the JavaScript (I prefer TypeScript), so I am not sure if this is the correct way to compare strings.
Using the shouldComponentUpdate() method, you can specify whether or not a component should update based on the current props and the new props which are provided as:
shouldComponentUpdate(nextProps){
return this.props.item.name !== nextProps.item.name;
}
Here's a JsFiddle with a demo: https://jsfiddle.net/Vorcan/Lakj6qwb/
Usually, you have to create a new object instance using something like Object.assign().
Other than the docs, another good resource to check out shouldComponentUpdate is https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/update/using_should_component_update.html

Will a stateless component re-render if its props have not changed?

One thing I had learned about React is that if the props to a component don’t change, then React doesn’t bother re-rendering the component. Is that true for stateless components too? Or do they behave more like “stupid” functions and get executed every time?
For example, if I had:
import StatelessComponent from '../StatelessComponent';
export default class DocumentsTable extends React.Component {
state = {
something: 'foobar',
};
render() {
return (
<div>
{ this.state.something }
<StatelessComponent theOnlyProp='baz'>
</div>
)
}
};
When this.state.something updates its value, does <StatelessComponent> get re-rendered? Or is it “smart” enough to see that its props didn’t change, like other React components?
UPDATE 25.10.2018
Since React 16.6, you can use React.memo for functional components to prevent re-render, similarly to PureComponent for class components:
const MyComponent = React.memo((props) => {
return (
/* markup */
);
});
Also, memo does internal optimization.
And unlike a userland memo() higher-order component implementation, the one built into React can be more efficient by avoiding an extra component layer.
Blockquote
OLD ANSWER
Yes, they always re-render 1 (unless you use React.memo as explained above) if setState() is called in the component itself or one of its parents, because functional stateless components don't carry a shouldComponentUpdate. In fact, each React component is being re-rendered1 unless they implement shouldComponentUpdate.
Important to note is that calling render() doesn't mean that DOM Nodes are being manipulated in any way. The render method just serves the diff algorithm to decide which DOM Nodes need to really be attached / detached. Note that render() is not expensive, it's the DOM manipulations that are expensive. They are executed only if render() returns different virtual trees.
From React's documentation
Just to be clear, rerender in this context means calling render for all components, it doesn’t mean React will unmount and remount them. It will only apply the differences following the rules stated in the previous sections.
Just don't worry and let render() be called unless your component is huge, then you're better off with stateful Component that implements shouldComponentUpdate().
Look here for an interesting discussion.
1 means that render() function of the component is called, not that the underlying DOM node is being manipulated.
See react does not only rerenders only If props are changed it even rerenders itself if any state change is there. In your case the component will rerender as your state is changing. The way react works is based on an algorithm named Reconciliation, what this algorithm does is that it compares your virtual DOM with real DOM and if it sees any change then it rerender your actual DOM by replacing it with your virtual DOM so any change in state will cause rerendering of the whole component.
Will a Stateless component re-render if its props have not changed?
Yes. Stateless render function will be called even if nothing has changed. However, React will in the reconciliation phase compare the virtual DOM (generated by the render function) against the existing DOM. This is far in the pipeline, hence not ideal if the render function was costly to compute.
A Pure component does have a default shallow comparison of the property and would have stopped the render to be executed. See the Pure component as a normal class React component that has a shouldComponentUpdate that compare with a triple equal the existing properties and the new one.
That being said, you can wrap your Stateless component into a Pure Component by using Recompose.pure (https://github.com/acdlite/recompose/blob/master/docs/API.md#pure) which will automatically perform, like the Pure Component, a shallow comparison without compromising on the short syntax of the Stateless component.
import StatelessComponent from '../StatelessComponent';
const PureChildFromStatelessComponent = Recompose.pure(StatelessComponent);
// ...
<PureChildFromStatelessComponent ... />

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);
}

Selective children component render

A basic question I need help here.
Whenever this.setState invoked at parent components, all the children components will be rendered. This will cause the performance issue if I have huge amount of child components.
Lets give an example,
Parent Component
handleToggleTick() {
const newObj = Object.assign({}, this.state, { iconName: ''});
this.setState({
iconName: newObj.iconName,
});
}
render() {
return(
<ChildComponentA iconName={this.state.iconName} toggleTick={() => this.handleToggleTick}></ChildComponentA>
<ChildComponentB></ChildComponentA>
<ChildComponentC></ChildComponentA>
)
}
Based on the example above, whenever handleToggleTick invoked from childcomponentA, setState invoked for new iconName. What I want is, only ChildComponentA only the one get render since props.iconName is related to it, but not for childcomponentB and childcomponentC.
I understand there is an option to check shouldComponentUpdate in childcomponent to prevent it get render. But, imagine I have over 100 of childcomponent, would it be frustrating to write over 100 times of shouldComponentUpdate method?
I need help here, please advice!
React doesn't provide any way to render children selectively. The component will either render or not. But I need to highlight a few points why this is not a problem when we use React in practice.
First of all, you don't need to manually implement shouldComponentUpdate for each component. If you don't want to rerender component if its props and state haven't changed, you can just extend from the PureComponent class instead of the Component class. Note that React.PureComponent's shouldComponentUpdate() only uses shallow comparison for state and props. But this shouldn't be a problem if you follow react best practices and avoid mutating the state.
Also, it's not practical to have more than 100 different components in one render method. React always encourages decomposing your UI into smaller components and using component composition. When we follow this approach, components will be nested inside each other in different levels instead of having a large number of components in one render method.
What I'm trying to explain is it's more practical and easy to manage when we compose our component in a nested fashion (2) rather than having lots of components inside a big container component (1).
In your example, if ChildComponentB and ChildComponentC are inside another component called ChildConatainerComponent then we only need to implement shouldComponentUpdate() for ChildConatainerComponent. Then it will automatically stop rendering any child element inside it.
render() {
return(
<ChildComponentA iconName={this.state.iconName}
toggleTick={() => this.handleToggleTick}/>
<ChildConatainerComponent/>
)
}
class ChildConatainerComponent extends PureComponent {
render() {
return (
<div>
<ChildComponentB/>
<ChildComponentC/>
</div>
);
}
}
Another very import concept to keep in mind is calling render function doesn't mean that React recreates all the DOM elements again. The render method only make changes to React virtual DOM which is an in-memory representation of DOM and it's faster than actual DOM. Then React compare versions of virtual DOM which are before the update and after the update and the actual DOM will be updated with only what has actually changed.
Another solution you could consider is moving iconName into ChildComponentA, considering this is the only component related to it.

React: Parent component re-renders all children, even those that haven't changed on state change

I haven't been able to find a clear answer to this, hope this isn't repetitive.
I am using React + Redux for a simple chat app. The app is comprised of an InputBar, MessageList, and Container component. The Container (as you might imagine) wraps the other two components and is connected to the store. The state of my messages, as well as current message (the message the user is currently typing) is held in the Redux store. Simplified structure:
class ContainerComponent extends Component {
...
render() {
return (
<div id="message-container">
<MessageList
messages={this.props.messages}
/>
<InputBar
currentMessage={this.props.currentMessage}
updateMessage={this.props.updateMessage}
onSubmit={this.props.addMessage}
/>
</div>
);
}
}
The issue I'm having occurs when updating the current message. Updating the current message triggers an action that updates the store, which updates the props passing through container and back to the InputBar component.
This works, however a side effect is that my MessageList component is getting re-rendered every time this happens. MessageList does not receive the current message and doesn't have any reason to update. This is a big issue because once the MessageList becomes big, the app becomes noticeably slower every time current message updates.
I've tried setting and updating the current message state directly within the InputBar component (so completely ignoring the Redux architecture) and that "fixes" the problem, however I would like to stick with Redux design pattern if possible.
My questions are:
If a parent component is updated, does React always update all the direct children within that component?
What is the right approach here?
If a parent component is updated, does React always update all the direct children within that component?
No. React will only re-render a component if shouldComponentUpdate() returns true. By default, that method always returns true to avoid any subtle bugs for newcomers (and as William B pointed out, the DOM won't actually update unless something changed, lowering the impact).
To prevent your sub-component from re-rendering unnecessarily, you need to implement the shouldComponentUpdate method in such a way that it only returns true when the data has actually changed. If this.props.messages is always the same array, it could be as simple as this:
shouldComponentUpdate(nextProps) {
return (this.props.messages !== nextProps.messages);
}
You may also want to do some sort of deep comparison or comparison of the message IDs or something, it depends on your requirements.
EDIT: After a few years many people are using functional components. If that's the case for you then you'll want to check out React.memo. By default functional components will re-render every time just like the default behavior of class components. To modify that behavior you can use React.memo() and optionally provide an areEqual() function.
If a parent component is updated, does React always update all the direct children within that component?
-> Yes , by default if parent changes all its direct children are re-rendered but that re-render doesn't necessarily changes the actual DOM , thats how React works , only visible changes are updated to real DOM.
What is the right approach here?
-> To prevent even re-rendering of virtual DOM so to boost your performance further you can follow any of the following techniques:
Apply ShouldComponentUpdate Lifecycle method - This is applied only if your child component is class based , you need to check the current props value with the prev props value ,and if they are true simply return false.
Use Pure Component -> This is just a shorter version to above method , again works with class based components
Use React memo -> this is the best way to prevent Rerendering even if you have functional components ,you simply need to wrap your components export with React.memo like : export default React.memo(MessageList)
Hope that helps!
If parent component props have changed it will re-render all of its children which are made using React.Component statement.
Try making your <MessageList> component a React.PureComponent to evade this.
According to React docs: In the future React may treat shouldComponentUpdate() as a hint rather than a strict directive, and returning false may still result in a re-rendering of the component. check this link for more info
Hope this helps anyone who is looking for the right way to fix this.
If you're using map to render child components and using a unique key on them (something like uuid()), maybe switch back to using the i from the map as key. It might solve the re-rendering issue.
Not sure about this approach, but sometimes it fixes the issue

Resources