Search code examples
reactjsrerender

How to restrict react Component to Re-rendering,


I am passing functions to my child component. And I am using React.memo to restrict compoenent from re-rendering. But My component rerenders when parent re-renders. I tried to check why this is happening by using useEffect on all the props and I get to this point that my functions are causing compoenent to re-renders.

// my functions

const scrollToView = (index) => {
  if (scrollRef && scrollRef.current && scrollRef.current[index]) {
    scrollRef.current[index].scrollIntoView({ behavior: 'smooth' });
   }
};

const scrollToReportView = (reportIndex) => {
  if (scrollToReportRef && scrollToReportRef.current && 
      scrollToReportRef.current[reportIndex]) {
      scrollToReportRef.current[reportIndex].scrollIntoView({ 
      behavior: 'smooth' });
    }
  }

.......
function LeftNav({
  scrollToView, //function
  scrollToReportView, //function
  reports, //object
}) {



  useEffect(() => {
     console.log('scrollToView')
  }, [scrollToView])



  useEffect(() => {
    console.log('scrollToReportView')
  }, [scrollToReportView])
  useEffect(() => {
    console.log('reports')
  }, [reports])

  return (
    <div>{'My Child Component'}</div>
  );
}

export default memo(LeftNav);

And this is how my left nav is being called

 <LeftNav
   scrollToView={(index) => scrollToView(index)}
   scrollToReportView={(repIndex)=> scrollToReportView(repIndex)}
   reports={reports}
 />


Solution

  • With

     <LeftNav
       scrollToView={(index) => scrollToView(index)}
       scrollToReportView={(repIndex)=> scrollToReportView(repIndex)}
       reports={reports}
     />
    

    you're creating new anonymous functions every time you render the LeftNav component, so memoization does absolutely nothing. Just

    <LeftNav
      scrollToView={scrollToView}
      scrollToReportView={scrollToReportView}
      reports={reports}
    />
    

    instead (assuming those functions are stable by identity (e.g. are declared outside the component or are properly React.useCallbacked or React.useMemoed).

    In other words, if your component is currently

    function Component() {
      // ...
      const scrollToView = (index) => {
        if (scrollRef && scrollRef.current && scrollRef.current[index]) {
          scrollRef.current[index].scrollIntoView({ behavior: "smooth" });
        }
      };
    
      const scrollToReportView = (reportIndex) => {
        if (scrollToReportRef && scrollToReportRef.current && scrollToReportRef.current[reportIndex]) {
          scrollToReportRef.current[reportIndex].scrollIntoView({
            behavior: "smooth",
          });
        }
      };
      return (
        <LeftNav
          scrollToView={(index) => scrollToView(index)}
          scrollToReportView={(repIndex) => scrollToReportView(repIndex)}
          reports={reports}
        />,
      );
    }
    

    it needs to be something like

    function Component() {
      // ...
      const scrollToView = React.useCallback((index) => {
        if (scrollRef?.current?.[index]) {
          scrollRef.current[index].scrollIntoView({ behavior: "smooth" });
        }
      }, []);
    
      const scrollToReportView = React.useCallback((reportIndex) => {
        if (scrollToReportRef?.current?.[reportIndex]) {
          scrollToReportRef.current[reportIndex].scrollIntoView({
            behavior: "smooth",
          });
        }
      }, []);
      return (<LeftNav scrollToView={scrollToView} scrollToReportView={scrollToReportView} reports={reports} />);
    }
    

    so the scrollToView and scrollToReportView functions have stable identities.