How to use "shouldComponentUpdate" with Hooks?

Asked

Viewed 203 times

0

I have a component <Filho> who does not receive props, but is rendered again whenever the <Pai> has some status update (useState).

To solve this I figured I could do, in the component <Filho>, something like:

shouldComponentUpdate(nextProps, nextState) {
    return nextState !== this.state;
}

But how to do this with Hooks? Or is there another alternative to avoid the rerender?

Current situation:

[RENDER] Pai
[RENDER] Filho
[RENDER] Pai
[RENDER] Filho

function Filho() {
  // O componente Filho tem estado, apesar de eu não atualizá-lo nesse exemplo
  const [b, setB] = React.useState([]);

  console.log("[RENDER] Filho");
  return <div></div>;
}

function Pai() {
  const [a, setA] = React.useState([]);

  React.useEffect(() => {
    function fetchData() {
      setTimeout(function(){
        setA(['a', 'b']);
      }, 1500);

    }

    fetchData();
  }, []);

  console.log("[RENDER] Pai");

  return <Filho />;
}

ReactDOM.render( <Pai /> , document.querySelector("#app"));
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

<div id="app"></div>

Expected result:

[RENDER] Pai
[RENDER] Filho
[RENDER] Pai

1 answer

2


In such cases, you can use the API React.memo, one Higher order Function. Of documentation:

If your Function Component renders the same result given the same props, you can involve in it a call to React.memo for an increase in performance in some cases, by memoizing the result. This means that React will skip rendering the component and reuse the last rendered result.

React.memo checks only the prop changes. If your function componetne involved in React.memo have a useState or useContext hook in its implementation, it will still be rendered when the state or context changes.

Behold:

const Filho = React.memo(function Filho() {
  const [b, setB] = React.useState([]);

  console.log("[RENDER] Filho");
  
  return <div></div>;
});

function Pai() {
  const [a, setA] = React.useState([]);

  React.useEffect(() => {
    function fetchData() {
      setTimeout(function(){
        setA(['a', 'b']);
      }, 1500);

    }

    fetchData();
  }, []);

  console.log("[RENDER] Pai");

  return <Filho />;
}

ReactDOM.render( <Pai /> , document.querySelector("#app"));
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

<div id="app"></div>

By default, React.memo will superficially compare the properties passed to the component, so that if they are equal to the previous ones, no new rendering will occur. If you want to change this behavior, you can use a comparative function in the second argument of memo, who receives prevProps and nextProps, previous and subsequent properties, respectively:

function MyComponent(props) {
  // ...
}

function areEqual(prevProps, nextProps) {
  // Retorne `true` caso as propriedades forem iguais.
  // E `false` caso forem diferentes. Nesse último caso, o componente será renderizado novamente.
}

export default React.memo(MyComponent, areEqual);

This article can help a lot too.

  • Good answer! The article is also good, I think the readability of the code worsens a little depending on how the method approached in the article is used, but it is good to have options in mind when facing a more serious performance problem :)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.