React hooks: What/Why `useEffect`?

  1. What are the advantages and use cases of the Effect hook (useEffect())?

    Advantages

    Primarily, hooks in general enable the extraction and reuse of stateful logic that is common across multiple components without the burden of higher order components or render props.

    A secondary benefit (of Effect hooks in particular) is the avoidance of bugs that might otherwise arise if state-dependent side effects are not properly handled within componentDidUpdate (since Effect hooks ensure that such side effects are setup and torn-down on every render).

    See also the peformance and readability benefits detailed below.

     

    Use cases

    Any component that implements stateful logic using lifecycle methods—the Effect hook is a “Better Way”.

     

  2. Why would it be preferable & how does it differ over componentDidMount/componentDidUpdate/componentWillUnmount (performance/readability)?

    Why it’s preferable

    Because of the advantages detailed above and below.

     

    How it differs from lifecycle methods

    Performance

    Effect hooks—

    • feel more responsive than lifecycle methods because they don’t block the browser from updating the screen;
    • will however setup and tear-down side effects on every render, which could be expensive…
    • …so can be optimised to be skipped entirely unless specific state has been updated.

     

    Readability

    Effect hooks result in:

    • simpler and more maintainable components, owing to an ability to split unrelated behaviour that previously had to be expressed across the same set of lifecycle methods into a single hook for each such behaviour—for example:

      componentDidMount() {
        prepareBehaviourOne();
        prepareBehaviourTwo();
      }
      
      componentDidUnmount() {
        releaseBehaviourOne();
        releaseBehaviourTwo();
      }
      

      becomes:

      useEffect(() => {
        prepareBehaviourOne();
        return releaseBehaviourOne;
      });
      
      useEffect(() => {
        prepareBehaviourTwo();
        return releaseBehaviourTwo;
      });
      

      Notice that code relating to BehaviourOne is now distinctly separated from that relating to BehaviourTwo, whereas before it was intermingled within each lifecycle method.

    • less boilerplate, owing to an elimination of any need to repeat the same code across multiple lifecycle methods (such as is common between componentDidMount and componentDidUpdate)—for example:

      componentDidMount() {
        doStuff();
      }
      
      componentDidUpdate() {
        doStuff();
      }
      

      becomes:

      useEffect(doStuff); // you'll probably use an arrow function in reality
      

Leave a Comment