Home>
I think the reasons for using

useEffect are as follows.

① Processing can be performed after rendering
(2) Execution timing can be controlled by the second argument of useEffect
③ Cancellation processing at the time of unmounting can be set

But conversely, if you don't need any of the above, is it okay to execute asynchronous processing without using useEffect?
For example, in the process of fetching data from the server with Ajax and displaying it on the component, I feel that it may not be necessary to use useEffect for the following reasons.

① Issuing an Ajax request does not have to wait for the end of rendering
(2) If the rendering of the component is controlled by React.memo etc., the execution timing can be controlled without useEffect
③ There is no particular processing when unmounting

The code in that case is as follows.

const [name, setName]= useState ('');
axios.get (`https://example.com/api/users/XXX`).then (res= >
 setName (res.data.name));
return (<
div >
{name} <
/div >
);

What code do you expect if you don't use useEffect? Please post the sample code!

hinaloe2022-02-11 23:21:51

@hinaloe Sample code added! Thank you.

denko2022-02-11 23:21:51
  • Answer # 1

    useEffectYou shouldn't always use, butuseEffectIt is better to use.

    Personally, when doing asynchronous processinguseCallbackI think it's okay to move even if you do it,useEffectI understand that is a best practice.

    Sample code

    The sample code below has a major problem.
    The point is that a request is made for each rendering.

    const [name, setName]= useState ('');
    //↓ axios.get is executed every time the component is rendered.
    //In other words, a request is made for each rendering!
    axios.get (`https://example.com/api/users/XXX`).then (res= >
     setName (res.data.name));
    return (<
    div >
    {name} <
    /div >
    );
    
    1. FirstsetNameIs executed, then React's re-rendering process runs.
    2. When renderingaxios.get ()Is executed

    The API server is making useless requests, which is obviously a problem.

    useEffectWhen using

    useEffectWith, API requests won't run multiple times.

    const Example= ()= >
     {
      const [name, setName]= useState ('');
      //Executed only once after component mounting
      useEffect (()= >
     {
        let unmounted= false;
        async function fetchName () {
          //I used fetch instead of axios
          //https://developer.mozilla.org/ja/docs/Web/API/fetch
          const res= await fetch ('https://example.com/api/users/XXX');
          if (! res.ok) {
            throw new Error (`unexpected status: $ {res.status}`);
          }
          const data= await res.body ();
          //If you setName after unmounted, it will prevent the memory from being released, so the debugger will be warned.
          if (! unmounted) {
            setName (data.name);
          }
        }
        fetchName ();
        return ()= >
     {
          unmounted= true;
        };;
      },
     []);
      return <
    div >
    {name} <
    /div >
    ;
    };;
    

    API request after user action (useCallback)

    Next, let's consider the case where the timing of the API request is after the button is clicked.
    In the case belowuseCallbackIs used.

    //Load the name when the button is clicked
    const Example= ()= >
     {
      const [name, setName]= useState ('');
      const unmountRef= useRef (false);
      useEffect (()= >
     {
        return ()= >
     {
          unmountRef.current= true;
        };;
      },
     []);
      const handleClick= useCallback (async ()= >
     {
        const res= await fetch ('https://example.com/api/users/XXX');
        if (! res.ok) {
          throw new Error (`unexpected status: $ {res.status}`);
        }
        const data= await res.body ();
        //If you setName after unmounted, it will prevent the memory from being released, so the debugger will be warned.
        if (! unmountRef.current) {
          setName (data.name);
        }
      },
     []);
      return (
        ≪
    div >
          ≪
    button onClick= {handleClick} >
    Read name <
    /button >
          ≪
    div >
    Name: {name} <
    /div >
        ≪
    /div >
      );
    };;
    

    In this case, after unmountingsetNameTo prevent it from runninguseRefIs used to save the mount state.

    API request after user action (useEffect)

    It's pretty verbose, but it's an ideal implementation.

    //Load the name when the button is clicked
    function reducer (state, action) {
      switch (action.type) {
        case "LOAD":return {... state,
            isLoading: true,
          };;
        case "SET_NAME":
          return {
            isLoading: false,
            name: action.payload,
          };;
        default: default:
          return state;
      }
    }
    function reset () {
      return {
        isLoading: false,
        name:'',
      };;
    }
    const Example= ()= >
     {
      const [state, dispatch]= useReducer (reducer, undefined, reset);
      const {isLoading, name}= state;
      useEffect (()= >
     {
        if (! isLoading) {
          return;
        }
        let unmounted= false;
        async function fetchName () {
          //I used fetch instead of axios
          //https://developer.mozilla.org/ja/docs/Web/API/fetch
          const res= await fetch ('https://example.com/api/users/XXX');
          if (! res.ok) {
            throw new Error (`unexpected status: $ {res.status}`);
          }
          const data= await res.body ();
          //If you dispatch after being unmounted, it will prevent the memory from being released, so the debugger will be warned.
          if (! unmounted) {
            dispatch ({type: "SET_NAME", payload: data.name});
          }
        }
        fetchName ();
        return ()= >
     {
          unmounted= true;
        };;
      },
     [isLoading]);
      const handleClick= useCallback (async ()= >
     {
        dispatch ({type: "LOAD"});
      },
     []);
      return (
        ≪
    div >
          ≪
    button onClick= {handleClick} >
    Read name <
    /button >
          ≪
    div >
    Name: {name} <
    /div >
        ≪
    /div >
      );
    };;
    

    In this last caseisLoadingButtrue trueWhen it becomes, the API request is executed and the state of the component is updated.

    The amount of code required for implementation will increase, but considering maintainability such as ease of understanding the logic when reading the code,useCallbackthanuseEffectI think that the gunbai will go up.