Search code examples
reactjsnext.jsmemoreact-memo

Will the component be re-rendered every time I enter the page even though the data hasn't changed?


I'm not deep understanding how memo works with the component. I have component like this. It is very simple and it is static.

const Home = memo((props: any) => {     
  useEffect(() => {
    console.log('home changed');
  }, [])   
  return (
    <>
    <h1>This is homepage</h1>
    </>
  )  
})

I has route is /home. Everytime I come to Homepage route, I always got message with log home change. So this component is not cached with memo? If it was cached, how to realize the Component is not re-rendered?


Solution

  • memo can't stop the very first render which happens when mounting a component. That render must always take place. So if you route away from the home page you unmount it, and when you route back you must re-mount it, resulting in one render

    Instead what memo does is potentially skip later renders. Every time the component would re-render due to a change in props, react will first do a shallow comparison of all the props. If none of them changed, it skips rendering and uses what your component returned last time.

    Actually, in big project we have many props, each props have many functions inside. Example: Input onChange always change props, so we have to add useMemo for each action like this?

    In order for memo to work, the props need to stay the same, so you will need to make sure you're not changing props. Functions are indeed one case you need to be careful of, because if you're making a brand new function every time you render and then passing that to a child component, the child is constantly having its memoization broken. The fix for this is typically to wrap your function in useCallback so you use the same instance of the function on each render. For example:

    const Parent = () => {
      // This log will happen when mounting, plus every time the count state changes
      console.log('rendering parent');
    
      const [count, setCount] = useState(1);
      useEffect(() => {
        const id = setInterval(() => {
          setCount(prev => prev + 1);
        }, 1000);
        return () => { clearInterval(id); };
      }, []);
      
      // Without the useCallback, we would be making a new onChange with every render of
      //   Parent. With useCallback, we keep reusing the function from the first render
      const onChange = useCallback((event) => {
        // Do something
      }, []);  
    
      return (
        <div>
          Count: {count}
          <Child onChange={onChange} />
        </div>
    }
    
    
    const Child = memo(({ onChange }) => {
      // This log will only happen when mounting, plus when onChange changes. And thanks
      //   to useCallback, onChange will not change.
      console.log('rendering child');
      return (
        <input onChange={onChange}/>
      )
    })