Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

shouldComponentUpdate in function components

Tags:

reactjs

I have a question regarding React's shouldComponentUpdate (when not overwritten). I do prefer pure, function components, but I am afraid that it updates every time, even though props/state did not change. So I am considering using the PureComponent class instead.

My question regarding that: Do function components have the same shouldComponentUpdate check as PureComponents? Or does it update every time?

like image 795
MoeSattler Avatar asked Dec 01 '16 11:12

MoeSattler


People also ask

Why do we use shouldComponentUpdate () function in Reactjs?

Use shouldComponentUpdate() to let React know if a component's output is not affected by the current change in state or props. The default behavior is to re-render on every state change, and in the vast majority of cases you should rely on the default behavior.

Can we use ComponentDidUpdate in functional component?

ComponentDidUpdate is a React component lifecycle method invoked immediately after a component's updates are flushed to the DOM. This is one of the most used built-in methods, which is not called for the initial render nor applicable to your functional details.

Can we use componentWillMount in functional component?

Manage componentWillMount with useEffect with a return function triggered when a component unmounts from the DOM. This is the only time it will fire on component render (componentWillMount) This means you can easily use and within functional components.

How do you prevent re-render in React functional component?

But, is there an option to prevent re-rendering with functional components? The answer is yes! Use React. memo() to prevent re-rendering on React function components.


2 Answers

A functional component will rerender every time the parent renders it, no matter if the props have changed or not.

However, using the React.memo high order component it is actually possible for functional components to get the same shouldComponentUpdate check which is used in PureComponent https://reactjs.org/docs/react-api.html#reactmemo

You can simply wrap your functional component in React.memo on export like shown here.

So

const SomeComponent = (props) => (<div>HI</div>) export default SomeComponent 

Could instead be

const SomeComponent = (props) => (<div>HI</div>) export default React.memo(SomeComponent) 

Example

The following example show how this affects rerenders

The parent component is just a regular functional component. It is using the new react hooks to handle some state updates.

It just have some tick state which only serves the purpose of giving some clues on how often we rerender a prop, while it forces a rerender of the parent component twice a second.

Further we have a clicks state which tell how often we have clicked the button. This is what the prop we send to the children. Hence they should ONLY rerender if the number of clicks changes if we use React.memo

Now notice that we have two different kind of children. One wrapped in memo and one which is not. Child which is not wrapped, will rerender every time the parent rerenders. MemoChild which is wrapped, will only rerender when the clicks property changes.

const Parent = ( props ) => {   // Ticks is just some state we update 2 times every second to force a parent rerender   const [ticks, setTicks] = React.useState(0);   setTimeout(() => setTicks(ticks + 1), 500);   // The ref allow us to pass down the updated tick without changing the prop (and forcing a rerender)   const tickRef = React.useRef();   tickRef.current = ticks;    // This is the prop children are interested in   const [clicks, setClicks] = React.useState(0);    return (     <div>       <h2>Parent Rendered at tick {tickRef.current} with clicks {clicks}.</h2>       <button          onClick={() => setClicks(clicks + 1)}>         Add extra click       </button>       <Child tickRef={tickRef} clicks={clicks}/>       <MemoChild tickRef={tickRef} clicks={clicks}/>     </div>   ); };  const Child = ({ tickRef, clicks }) => (   <p>Child Rendered at tick {tickRef.current} with clicks {clicks}.</p> );  const MemoChild = React.memo(Child); 

Live Example (also on CodePen):

console.log("HI");    const Parent = ( props ) => {    const [ticks, setTicks] = React.useState(0);    const tickRef = React.useRef();    tickRef.current = ticks;    const [clicks, setClicks] = React.useState(0);        setTimeout(() => setTicks(ticks + 1), 500);    return (      <div>        <h2>Parent Rendered at tick {tickRef.current} with clicks {clicks}.</h2>        <button           onClick={() => setClicks(clicks + 1)}>          Add extra click        </button>        <Child tickRef={tickRef} clicks={clicks}/>        <MemoChild tickRef={tickRef} clicks={clicks}/>      </div>    );  };    const Child = ({ tickRef, clicks }) => (    <p>Child Rendered at tick {tickRef.current} with clicks {clicks}.</p>  );    const MemoChild = React.memo(Child);      ReactDOM.render(    <Parent />,    document.getElementById('root')  );
<div id="root"></div>  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>  <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
like image 111
the_cheff Avatar answered Oct 14 '22 05:10

the_cheff


In React, functional components are stateless and they do not have lifecycle methods. Stateless components are an elegant way of writing React components without much code in our bundle. But internally, Stateless components are wrapped in a class without any optimizations currently applied. That means both stateless and stateful components has the same code path internally (although we define them differently).

But in the future React may optimize stateless components as said it here:

In the future, we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations. [ More reading... ]

shouldComponentUpdate

This is where you we can apply our custom optimizations and avoid unnecessary re-rendering of components. The usage of this method with different types of components are explained below:

  • Functional Stateless components

    As said before, stateless components do not have life cycle methods thus we cannot optimize them using shouldComponentUpdate. But they are already optimized in a different way, they have much simpler and elegant code structure and costs less bytes than a component with all life-cycle hooks.

  • extend React.PureComponent

    From React v15.3.0, we have a new base class called PureComponent to extend with PureRenderMixin built-in. Under the hood this employs a shallow comparison of current props/state with next props/state within a shouldComponentUpdate.

    That said, we still cannot rely on PureComponent class to get our components optimized to the level we want. This anomaly case happens if we have props with Object types (arrays, dates, plain objects). This is because we have this problem when comparing objects:

    const obj1 = { id: 1 }; const obj2 = { id: 1 }; console.log(obj1 === obj2); // prints false 

    Hence a shallow comparison won't suffice to determine whether things have changed or not. But use PureComponent class if your props are just string, number, boolean.. and not objects. Also use it if you do not want to implement your own custom optimizations.

  • extend React.Component

    Consider the above example; if we know that the objects have changed if the id has changed, then we can implement our own custom optimization by comparing obj1.id === obj2.id. That is where we can extend our normal Component base class and use shouldComponentUpdate to do the comparison of specific keys by ourselves.

like image 20
yadhu Avatar answered Oct 14 '22 05:10

yadhu