All consumers of a Provider will re-render when value prop changes? - reactjs

I'm trying to understand React Context API better. In the react docs I stumbled on this paragraph:
All consumers that are descendants of a Provider will re-render
whenever the Provider’s value prop changes. The propagation from
Provider to its descendant consumers (including .contextType and
useContext) is not subject to the shouldComponentUpdate method, so the
consumer is updated even when an ancestor component skips an update.
I'm having a hard time understanding this, since the definition of a consumer is a little unclear to me. If I try to create an example (codingsandbox) the result is always the same: If the value changes, all children (not consumers) of the Provider rerender.
I'd love to understand what is meant by the paragraph above. Is there a case where the context value updates without triggering a new render of its children (preferably in a function component)?

The problem, according to your sandbox, is how state is being managed in the main app component.
//your useState
const [store, setStore] = React.useState([]);
//the button that is setting the state
<button onClick={() => setStore([])}>Reset store value</button>
This causes the state in the component to change, which rerenders the App component itself, which causes a rerender of all it's children. If you wish to update the context state only, you should avoid storing the value in state and rather directly mount the function within the Context Object for logic to change the property within the value object

Related

How does React.js re-render components?

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.

How does react decide to rerender a component

I know React has a life cycle method called shouldComponentUpdate, Which by default return true and that's how the component decides to update
But How does that life cycle method gets called, When a state or props change for that component. What actually happens when we receive new props or state? When We connect a component to redux state and mapStateToProps, Are we checking for a change in values inside the component? If not, When We are looking for a change in state or props?
when the props or state changes, how the life cycle methods are called?. Do we have a listener that calls these methods when the props or state changes?
You should look at lifecycles of both, how they perform and in what order each method gets called. Looking at react lifecycle image bellow you can see the difference between componentWillMount and componentDidMount and others like componentDidUpdate, componentWillUpdate and so on...
Also you should reason when to use each method
To update state you call this.setState() which tells react that something has changed and it will re-render component tree. If you use this.state.data = something react won't trigger render(). Now to update props, you need to understand how render() actually works. This answer is summarized from existing anwser already:
Every time render() is called react will create a new virtual DOM
where the root node is the component whose render function is called.
The render() function is called when either the state or the props of
a component or any of its children change. The render() function
destroys all of the old virtual DOM nodes starting from the root and
creates a brand new virtual DOM.
In order to make sure the re-rendering of components is smooth and
efficient React uses the Diffing Algorithm to reduce the time it takes
to create a new tree to a time complexity of O(n), usually time
complexity for copying trees is > O(n^2). The way it accomplishes this
is by using the "key" attribute on each of the elements in the DOM.
React knows that instead of creating each element from scratch it can
check the "key" attribute on each node in the DOM. This is why you get
a warning if you don't set the "key" attribute of each element, React
uses the keys to vastly increase its rendering speed.
React Lifecycle
Redux Lifecycle
If you use redux library, may be, your component does not re-render after your props changes. Checkout this issue to resolve the props changes problem with componentWillReceiveProps

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

All component gets rendered even if we required to render only one, in react-redux

I'm not sure it is default behaviour or redux or something else but i found that on dispatching an action, this action traverse through all reducers(that's ok) but it also invoke the connect listener of every reducer that further resulting rendering of its component. This means on every dispatch, all component inside app state tree get rendered. Is this intentionally done by redux or i've done something wronge.
Help me out to clarify this things.
In Redux , your state is global and handled by the redux, whenever you dispatch an action , you are just setting the global state. Your container comonents will receive the new state and reducer would work on them but your components wont be rerendered since previous state and next state would be same.
Only those components would be rendered whose mapStatetoProps result in a different result
This behavior is totally fine. See the React Docs for their virtual DOM concept:
React makes use of a virtual DOM, which is a descriptor of a DOM subtree rendered in the browser. This parallel representation allows React to avoid creating DOM nodes and accessing existing ones, which is slower than operations on JavaScript objects. When a component's props or state change, React decides whether an actual DOM update is necessary by constructing a new virtual DOM and comparing it to the old one. Only in the case they are not equal, will React reconcile the DOM, applying as few mutations as possible.
So you don't have to worry that every component will get re-rendered every time you dispatch an action.

Refresh logic in react component or flux/redux?

So i'm fairly new to React and I can't wrap my head around a concept on how to re-render a main component based on another component.
Lets say we have a to-do application and a to-do item can have a state (new, running, closed). We are displaying all to-do items in a list and we can filter the items based on their state. We are using a bootstrap dropdownbutton like component to set the filter, which is a React component. Now when we change the filter we obviously want to refresh the to-do items.
My question is, does the logic of the selected state belong in Flux/Redux or does the filter component just say "refresh your items" to the main component?
When you use Redux in React application, follow one simple rule - all your components are stateless (means, no component initializes its state or calls .setState() anywhere).
The redux way of design based on state container, one big object that holds all application state. As React component, being connected to Redux store, Redux will pass the state (or portion of it) into that component, as this.props.state property.
That high-order component (or smart component), renders its children components (or dumb components) and handles events from them.
If child component requires change, it triggers corresponding handler (which typically passed as props). The only way to change the state is to dispatch an action. That action contains a type and a payload and based on that type, corresponding reducer is selected. The reducer then produces a new state, based on previous state and action payload.
If in the result of reducer call, state got changed, Redux will re-render high-order component, passing new state in properties. So, all child components will be updated correspondingly.
Check this example and trace how AddTodo component calls .handleClick() and then upper to .onAddClick() which dispatches an action.
If you are using redux, then on your dropdown you should have an onchange handler that dispatches an action every time the value is changed with the selected state (new, running, closed).
Redux reducer will handle this action by changing some state accordingly in the store for example: display = 'completed'. In addition to this, your todo list should also be stored in the store and it will likely be an array.
Your react component should receive a the todo array and display as props, and therefore everytime any prop (todo array or display) change, it will trigger a re-render.
In this case, your component should only display those todos that are complete (i.e. in the render you check if the state of each todo === this.props.display.
So to answer your question: redux keeps the state of the dropdown, which is passed to your main component, your main component then render only the todo's that matches the criteria.
So in a very minimal way, you could pass a function down to the select box, which calls setState on the top-level component. When that state changes, it will re-render its child components. This pattern doesn't scale well, and leads to the same hell React + Flux is trying to get us away from (state everywhere). I would say, using Flux (or even better, Redux), trigger an action that sets the filter in your store, and use derived data based on the filter state + your list of todo's in the todo list.

Resources