reactjsreact-hooksreact-lifecycle

React useEffect - in component or in service?


We have a hook (service) for our component, which have an init() and a shutdown() function (for some reason). The init() should be called at least once to initiate (starts a heart-beat operations), and the shutdown() to stop the heart-beat.

export default function EditProductForm({formToBeModify}: IEditProductClientFormProps): ReactNode {
  const service = useEditProductService();

  useEffect(() => {
    service.init();
    return () => service.shutdown();
  }, []);

 return {
   <div ...>
 }
)

This useEffect() is goot enough to do so, because its dependency is [] which means that the init() is called at component did mount and the shutdown() is called at component will unmount.

It is adviced to insert logic into component (.tsx) file, but everything should go into service code. In this case the service will look somehow like this:

export default function useEditProductService() {

   function init() { 
      ...
   }

   function shutdown() {
      ...
   }

   useEffect(() => {
        init();
        return () => shutdown();
      }, []);

  return {
     init,
     shutdown
  }
}

It is not clear for me, as the component several times re-render itself, and every time the service is called throught const service = useEditProductService(formToBeModify) that does it works the same as the useEffect() remains there, inside the component?

I think if it is outside the service, in the component itself, it works as I described above, the init() and shutdown() is called once. If it is inside the service it is called several times during the component lifetime. What do you think?


Solution

  • It's the same as running the effect directly in a component, or components. All React hooks are called each and every render cycle, your useEditProductService hook included. It's the useEffect hook with its dependencies that controls when its callback is invoked.

    The main purpose of custom React hooks is code-reuse.

    Instead of writing the following in each component that uses the "service"

    function init() { 
      ...
    }
    
    function shutdown() {
      ...
    }
    
    useEffect(() => {
      init();
      return shutdown;
    }, []);
    

    You can simply use the useEditProductService hook in it's place.

    useEditProductService();
    

    I generally keep a "rule of three", meaning once I've written the same chunk of code/logic a third time somewhere in the codebase, I'll refactor it into a reusable function or React hook, etc, since it's no longer a one-off-ish unit of code.