React functional component is called on every parent state change - reactjs

I'm new in React and I found thing that makes me confused.
Almost every tutorial suggests using functional (stateless) components.
But recently I discovered that if functional component is a child component, it is rendered every time when parent component state or props changed even if I don't pass any props to it.
It doesn't happen with class-based component.
What is the reason for this situation?
Should I use only class-based components in order to get the highest performance?

Here are your options out of the box:
Use a pure function like you did:
[+] Easy to write and to understand
[-] Rerenders each time the parent component rerenders (might not be an issue if you don't pass it any props though)
Use a class component and implement shouldComponentUpdate
[+] you have full control over the rerendering conditions.
[-] Takes a lot more code to write
Use a PureComponent
[+] uses a shallow shouldComponentUpdate automagically so less code to write
[-] can have undesirable side effects (false negative for complex objects and non pure child components not rendering properly)
In your particular case, since you are not passing any props to your child component, using a pure function is perfectly acceptable. Because that means its just pure static HTML and would rerender each time anyways even if it was part of the parent.
Lastly, for more advanced uses, have a look at https://github.com/acdlite/recompose

Related

How to limit scope of re-rendering to single component in React?

My components are structured looks like this. Each component is it's own functional component in its own file - this is just a schematic of how they interract.
<FirstLevelComponent>
// propValue is declared on this level with useState hook.
<SecondLevelComponent someProp={propValue}>
<ChildComponent1></ChildComponent1>
<ChildComponent2 someProp={propValue}></ChildComponent2>
<ChildComponent3></ChildComponent3>
</SecondLevelComponent>
</FirstLevelComponent>
someProp is declared in state in the FirstLevelComponent and is passed along to the SecondLevelComponent and when it changes, it triggers the re-render of the entire SecondLevelComponent. But the only dependency on that prop is in one of the children. The rest of the children are unaffected.
What would be the best way to isolate that behavior and only limit scope of re-rendering to the single child that depends on that prop?
Some constraints:
This is a huge production application so something like Just add redux would not be an easy solution.
Refactoring SecondLevelComponent will be a challenge (1500 lines of code), while I am open to such opportunity`, I am looking for the way to achieve this assuming it's not a hello world project. Solutions that are easy and ideal for application in its early stages are quite a rework when we are dealing with legacy code.
and only limit scope of re-rendering to the single child that depends on that prop?
What you're looking to accomplish goes against the nature of React. If a parent component has state, and that state changes, React will react and rerender the component.
Stopping the parent component from rerendering when its state changes is an anti pattern (as opposed to simply stopping its children from rerendering with useMemo).
The way I see it, you have the following options:
Add state to the child component instead. If it's the only component reliant on this state, it should be a simple refactor.
Replace useState with useRef in the parent component. Pass this to the child for creating initial state.
Option two could lead to other implications if anything else in the app is dependant on this piece of state.
Now, if you just want to keep the extra children from rerendering, which isn't exactly what I quoted above from the question, you could just simply use useMemo (and useCallback if passing a function as a prop)..
I saw the same problem, also big application
Option 1
You can take advantage of render from React composition if you can move propValue state and ChildComponent2 component into SecondLevelComponent
Explanation: the states in SecondLevelComponent wont rerender his childs like ChildComponent1 and ChildComponent3
Option 2
You can memoize you components using React.useMemo not React.memo because you are not passing propValue in ChildComponent1 and ChildComponent3 like this:
// How to memoize
const ChildComponent1Memoized = React.useMemo(()=><ChildComponent1/>,[propValue])
// In this case ChildComponent1Memoized will not rerender again
<SecondLevelComponent someProp={propValue}>
{ChildComponent1Memoized} // <---
<ChildComponent2 someProp={propValue}></ChildComponent2>
<ChildComponent3></ChildComponent3>
</SecondLevelComponent>
Extra
if you are passing functions as props be sure you are using useCallbak in every function, because they are rebuilding in every render, that cause Memo dosent work

React Native how to use React.memo on every component from collection to render only changed ones. Strange behavior of React.memo

I have been trying to improve rendering performance by using React.memo on components. It works properly until I use it on every single component from the collection being inserted as children.
I prepared snack HERE
To understand what is going on you need to open the console because there are some logs presented.
You will see WrapperMemo which generally renders Child components (they are also wrapped in some auxiliary components to check something I will explain later).
When you press the first button named SLICE MYNUMBERS you will see that React.memo's isEqual function called isWrapperEqual returns true, but it still does not stop from rerendering Child components.
However neither Wrapper nor all other auxiliary wrapper components (ChildOk) are rendered!
When you click any PRESS ME:[number] button it forces you to re-render other Child components when only one is being modified.
It is worth mentioning that TestCompMemo is never re-rendered when any of the buttons are pressed - which is correct behavior.
1) Is there any explanation for that?
2) Is there a way to use React.memo on every component from collection to render only changed ones (as it is presented in Wrapper component) ?
Is there any explanation for that?
In your snack example, you have a Context.Provider wrapper at the top level of the application.
This is what causes the re-render. This is expected behavior and you can read more about it here: https://github.com/facebook/react/issues/15156#issuecomment-474590693
Is there a way to use React.memo on every component from collection to render only changed ones (as it is presented in Wrapper component) ?
Using React.memo on every component is not recommended. You can run into bugs where components do not re-render and you are adding overhead with all the comparisons.
If you want to optimize the application, you might want to run the profiler and figure out which parts of the app is slowing things down and work from there.
But if you want to prevent unnecessary re-renders, you could move context usage up a few levels and pass the onPress to the child components.

Does React re-render all components when any state changes within context consumers?

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.

Will using the React props spread operator significantly slow-up my application?

When passing props down into a React component I am currently of doing this:
<MyComponent
{...this.props}
foo=foo
bar=bar
/>
foo and bar are props that I know MyComponent will need. However, in most cases MyComponent also has components within it that need props from higher components, hence I use the {...this.props} operator to pass them forward. Should I be doing this, or should I be listing out exactly the props that the child components of MyComponent will need?
You should use a state management like Flux, Redux or Mobx (i think, haven't used Mobx at all) to combat this problem of passing props through multiple levels without the intermediate components needing them.
You should be passing only the props exactly needed down to the child. I read a great post on github about this but can't find it.
It's just hard to manage when your app grows and it's really an abuse of the Es6 spread syntax operator (i.e it makes it easy short term to pass props down, but long term you still have the problem, you are just masking it). Not sure if it slows down application but it will re-render all child components again if the prop changed which is unnecessary.
For example when using Redux you can "connect" components to a global state (think databases) and pass them through as props for whichever components you want and bypass components having to forward props to child components.
It's hard at first to learn but 1000% worth it.
You should pass in only the props needed, or implement a container component that only passes in the props needed. Or you can implement shouldComponentUpdate on your component. The easiest way to get performance is to only pass in the required props though.

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