简体   繁体   中英

Does declaring a React.memo inside another functional component provide the memoization performance as it is intended?

Lately I've been thinking about ways to memoize some children components of a functional component, based on Dmitri's how to use React.memo wisely . These children components may / may not be using some props from the main component.

Let the component be written like so:

export const MainComponent = ({a, b}) => {
  const AComponent = React.memo(() => <p>This is the string of {a}</p>)

  return (
    <div>
      <AComponent />
      <p>This is the string of {b}</p>
    </div>
  )
}

Say the MainComponent is re-rendered several times over its lifecycle in a parent component.

const App = () => {
  const [a, setA] = useState(0); // a is update only when a button is pressed
  const [b, setB] = useState(0); // b is updated every 1 second

  const onClick = () => setA(a => a + 1);

  useEffect(() => {
    const interval = setInterval(() => setB(b => b + 1, 1000);
    return () => clearInterval(interval);
  }, []);

  return (
    <div>
      <MainComponent a={a.toString()} b={b.toString()} />
      <button onClick={onClick}>increment a</button>
    </div>
  )
}

I understand that as states a and b in the App are updated, it needs to be re-rendered, which in turn has to re-render the MainComponent which depends on a and b .

There are 2 questions:

  1. When the interval updates b in App , a is not updated at all, so when the MainComponent re-renders, should it use the memoized AComponent , not paying the cost of rendering?

  2. I do know that when the button is pressed, a in App is updated, which makes MainComponent re-render; the AComponent cannot use the memoized version, so it needs to pay the cost of rendering over again, but after that it becomes memoized, ready for the next re-render of MainComponent . Am I correct to say, while b is not updated during this render cycle, the <p>This is the string of {b}</p> (not memoized) is re-rendered, paying the cost of rendering?


Am I using React's memo correctly? Which of these alternatives would be better? Are they functionally the same?

  1. React.memo
const AComponent = React.memo(props => <p>This is the string of {props.a}</p>)
  1. React.useMemo
const aComponent = React.useMemo(() => <p>This is the string of {a}</>, [a])

// then use it like
return <>{ aComponent }</>

I used the memo in a little experiment, I upload the pictures maybe it will be useful.

Title.js:

title.js 描述

App.js:

app.js 说明

a) Memoizing is always faster in my tests (despite some blog posts saying otherwise). Having said that you're not going to see any noticeable gains unless you have a particularly heavy component, or the component is rendering lots of children. It could also save you from loosing focus if you're typing and that input gets re-rendered because the parent does.

b) In short none of them will work, React.memo has to sit outside the render function, it's not part of the "magic" use hook functions which maintain references. You have to use useMemo for inside render. Here's an example: The components which don't re-render every second are CComponent and DComponent :

 const CComponent = React.memo(({ count }) => ( <p>Not re-rendered every second: {count}</p> )); const MainComponent = (props) => { const AComponent = React.memo(() => ( <p>Re-render when parent does: {props.count}</p> )); const BComponent = React.memo(({ count }) => ( <p>Re-render when parent does: {count}</p> )); const DComponent = React.useMemo(() => ( <p>Not re-rendered every second: {props.count}</p> ), [props.count]); return ( <div> <AComponent /> <BComponent count={props.count} /> <CComponent count={props.count} /> {DComponent} <p>parent count: {props.count}</p> <p>parent anotherCounter: {props.anotherCounter}</p> </div> ); }; function App() { const [count, setCount] = React.useState(0); const [anotherCounter, setAnotherCounter] = React.useState(100); React.useEffect(() => { const h = setInterval(() => { setCount((c) => c + 1); }, 6000); const h2 = setInterval(() => { setAnotherCounter((c) => c + 1); }, 1000); return () => { clearInterval(h); clearInterval(h2); }; }, []); return ( <div className="App"> <MainComponent count={count} anotherCounter={anotherCounter} /> </div> ); } ReactDOM.render(<App />, document.querySelector('#container'));
 <script src="https://unpkg.com/react@17/umd/react.production.min.js"></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script> <div id="container"></div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM