I was wondering if a connect()ed sub-component will re-render if its parent returns false in shouldComponentUpdate?
Say, we have a component tree looking like this
connect(Foo extends PureComponent)
DumbBar
connect(Baz extends PureComponent)
So neither Baz, nor Foo, should re-render if their own props, supplied to them by mapStateToProps, keeps the same. But what happens if the state of the redux store changes and the connect()ed Baz component is notified of a state change and is supplied with new props? Will it re-render, or will the hierarchical update model of React prevent anything from rendering?
Or is this whole question perhaps a bit off, since it's not possible for a child components new rendering to take effect without affecting the ancestors - forcing the ancestors to re-render if a child changes?
If the last point is true, then putting a connect-ed component as a child of a PureComponent is lethal, as it breaks all optimizations you thought were in place, causing re-renders in other places in the sub-tree of the parent.
Yes, it will re-render.
That's the whole idea of using connect - you don't have to pass the props and you can embed it anywhere - it will take care of keeping your component up-to-date.
Each component built with connect will wire up it's own subscription to be notified of changes to the redux store.
So if only the props supplied to mapStateToProps for the child component have changed then the child component will re-render whereas the parent component will not.
You can see this yourself by adding a simple console.log to the render function.
Related
I'm new to react. I'm going through their docs to understand how react works. So it was mentioned that when state/props/setState() changes/called, react rerenders the entire component.
Also, I read that react elements are immutable and they are rendered only when there is a change. So when react tries to render a component it actually traverses through all the elements checks for differences and renders only those elements whose data is changed. It won't simply re-render the entire component.
Am I right regarding this? Or is my understanding wrong?
I read that React elements are immutable and they are rendered only when there is a change.
Saying that React elements are immutable is not true, you need to treat them as immutable, especially component's state and props.
So when React tries to render a component it actually traverses through all the elements checks for differences and renders only those elements whose data is changed.
The default behaviour is to render all sub tree when the parent rerendered by calling setState (which triggers Reconciliation on component's sub tree).
So saying it will render components on "data change" is not true, they will rerender anyway by default, even if the "data" didn't change.
On saying "data is changed" we mean on props change (shallow comparison by default, use memoization to change it).
We can use key prop to help with the reconciliation process.
You are right, react re-renders the component when props or state changes.
That being said, when a child component received new props, react does not check if the props have changed when you use React.Component, it just re-renders even if you pass same props again.
In order to make components render only if they receive different props you can use React.PureComponent in case of class components or you can wrap the component with React.memo() in case of functional components.
I believe a clearer way to summarize when React components re-render would be:
React components re-render when, and only when:
Their state changes (via setState() or useState())
They are passed new props;
Their parent component re-renders.
Caveats:
You must update state correctly for the component to re-render, i.e. via setState() or useState(). If state changes via other, "illegal" means, such as directly accessing state, React won't notice, and won't re-render the component.
React props are read-only, so when we say "when a component's props change," we really mean when the component is passed props with changed values. The props should not be mutated within the component.
If you use useMemo() or React.memo(), then a child component will only re-render when the parent component re-renders if the props it receives have changed.
It's important to distinguish between re-rendering the virtual DOM and updating the actual DOM. Just because a component re-renders doesn't mean it's updated the DOM. When a component re-renders, React compares the new version to the previous, and only updates the actual DOM if something has changed 1, 2.
Nothing has made this clearer for me than this cheat sheet by A. Sidorenko.
Edit:
Essentially yes any state change will trigger a re-render. This includes the component where the state change was initiated and all its children. For instance, if your composition is:
A > B > C
If the state for A is updated then A, B, and C will get re-rendered. There are ways to prevent re-rendering subcomponents (e.g. memo, useMemo) so I point you the cheat sheet referred to above for the complete details.
In the React docs, it says
By default, when your component’s state or props change, your component will re-render.
I understand for state changes, but I am not sure about when props change. As far as I am aware, props are always passed from the parent component to the child component. And when the parent component re-renders (due to a state change, for example), all child components also re-render (ignoring shouldComponentUpdate). So it seems to me that if the parent component re-renders, all child components will re-render regardless of whether I am passing new props to them or not. If I do pass new props to the child component, the fact the child re-renders is simply because the parent is re-rendering, not because I am passing new props.
Is there a scenario where a parent component passes new props to a child component, causing the child component to re-render, but it is not caused simply by the parent component re-rendering?
Is it possible to see an example where a component will re-render because it receives new props, rather than because the parent is re-rendering (or its own state changed)?
Sorry if this is a basic question, I am new to React.
EDIT: I see that Redux can cause components to re-render by passing new props, I'm curious to know what Redux is doing behind the scenes to achieve this.
To answer the question you ask at the end:
If the child is a PureComponent or wrapped in React.memo it will only re-render if the props change. If the parent re-renders then the component will compare the props it receives and if they're identical to the previously passed props it will not re-render.
If the child is not a PureComponent or wrapped in React.memo then it will re-render any time its parent re-renders.
I‘m not new to react and was asking the same thing. There was an article which made it clear for me: https://thoughtbot.com/blog/react-rendering-misconception
The re-render is needed for diff-checking if DOM updates are necessary. If you‘ve implemented shouldComponentUpdate or wrapped with memo (both returning false), then rerender is skipped.
If a child component is connected using redux or context, then it might rerender even without parent being rerendered.
When you use provider, or HOC, or Redux with mapStateToProps for example, the props are changed from other component injecting props.
Connect middleware of redux is similar to subscription, each child component can directly connect to the store through subscription, and whenever the store changes, the store tries to resync all its connected components. New incoming data to any component comes as props resulting in re-render.
Any component getting changed from outside of its state can be through props only, so it is similar to a parent component passing new data / redux store pushing new data to the component.
I'm building an app where I started moving a lot of props I was passing all over the place into context. Essentially I have a context provider component wrapping many children components.
Since I did this I'm having trouble with re-renders. More specifically, I have a graph in one component that is re-rendering when there is a state change in a sibling component that should have no impact on the graph (not passed in as a prop or shared context variable).
Is this expected behavior? And if it is, then should I try to "localize" context as much as possible?
The default behavior is that when a component renders, its children render too. This in turn will cause the grandchildren to render, and so on.
You can skip rendering a component by using PureComponent / shouldComponentUpdate (in class components) or React.memo (in function components). These techniques let you specify that if props didn't change, then the component does not need to render again. If rendering is skipped, then the tree below the component will also be skipped, with one caveat: components that consume the context will rerender if the context value changed, even if their immediate parent did not render.
It's often good to put one of these render-blocking components as an immediate child of your context provider. That way, if the only thing you change is the context value, then the only components that will rerender are ones that are explicitly consuming the context.
I have never used mobx before so my understanding could be off. I have a parent component and child component both injected and observing the same store. The store is passed to props on both when components are initialized. The child component is the one triggering the store action and it accurately updates the ui with the change, however the parent component is simply referencing the same observed property from the same store and does not update or re-render when the child component updates the store. I would think that since the parent component is observing the same observed property that it should receive the updated value, but it's not.
Yes you can inject store to any Component but you should Change mobx observable in Parent Component ,then mobx will trigger re-render and let both parent and child knows that the state has benn changed.you should use #observer on both component by the way.
So I have a component which the state is updated multiple times a second by a child element.
To prevent over-rendering i use the shouldComponentUpdate on the other children to make sure they don't re-render too much.
The element of the state that is updated is required by another child further down the tree.
I would only like that child element to rerender, and not the intermediary children, again, to avoid over-rendering.
How could one do that?
I would only like that child element to rerender, and not the intermediary children, again, to avoid over-rendering. How could one do that?
You can't via the traditional method of passing the state down as props. A change in state in the parent component cannot trigger a re-render on the grand-child component C without triggering a re-render on the child component B first.
For a component to update a child, it needs to go through its life-cycle before it can pass down new props to it.
To my knowledge, the only way around this is to use a state management library, such as Redux or MobX, or use the React Context API and move that state variable there and then "consume" it in your grand-child component.
That being said, unless your tree is multiple levels deep and you don't see any performance issues, I would consider keep using shouldComponentUpdate() like you are already doing.