Search code examples
reactjsreact-hooksfetch

React useEffect mystery on missing dependency warning


The prop is set up in some other component (months calendar), and user live months selection triggers a new api fetch for this component. Both components are hosted by a Home component as children. The option 2 works fine, no error or warning whatsoever. However, when I use the option 1 then I get this warning: React Hook useEffect has a missing dependency: 'getComponentData'. Either include it or remove the dependency array. Note the misleading function name, not the function dependent parameter name.

Just curious: What is the the principal difference between these two options? I use props value in both functions anyway; directly (option 1) and directly, inside the async function (option 2). How does the async method do the trick?

const [componentData, setComponentData] = useState([]);

option 1:

useEffect(() => {
  getComponentData(props.calendarInput);
}, [props.calendarInput]);

async function getComponentData(sm) {
  const data = await api.getUtilityServicePays(sm);
  setComponentData(data);
}

option 2:

useEffect(() => {
  async function getComponentData() {
    const data = await api.getUtilityServicePays(props.calendarInput);
    setComponentData(data);
  }
  getComponentData();
}, [props.calendarInput]);

Solution

  • I think you are misunderstanding what useEffect considers as dependencies. Whatever is directly used in the useEffect callback, might be a dependency. If it changes over renders, you should (in most cases) put it in the dependency array.

    From the docs:

    The list of all reactive values referenced inside of the setup code. Reactive values include props, state, and all the variables and functions declared directly inside your component body.

    For example: A function that is directly executed in the callback, is a dependency. If that function is defined outside the useEffect, whatever it uses inside is not directly a dependency for useEffect.

    The whole idea is that when we are running something inside the callback, all the values should be latest ones. If you define your function (without useCallback) in the main component body, outside useEffect, it is created afresh every render. So in 1st case, getComponentData will always have the latest values for the variables referred in its body.

    In the 1st case, for useEffect callback to run with the latest values, it only needs getComponentData and props.calendarInput in the dependency array then. So that if either of them changes the useEffect callback runs with new values (after renders). So it is not misleading to have getComponentData as the expected dependency, it is absolutely correct.

    In the 2nd case, the function itself is defined inside. So anything the function uses also needs to have the latest value. You do not get any warning for setComponentData because react guarantees that the function will not change over renders. And I think api must be a static service, which is not defined in the React component so it will never be created afresh along with renders.