Search code examples
reactjsreact-nativereact-hooksuse-effect

Wise to exclude this useEffect dependency array variable? lint recommends 3, but i only want to depend on 1


I'm planning to exclude this but just would like to double-check that there is no better way to do this.

  • This logic detects that the user has moved the app to the background (via appstatevisible) and then sends a bunch of data (views) to the server.
  • eslint recommends including views, users, cancelToken into the dependency array but it doesn't make sense as I only want to send this based on the appStateVisible. I do not want to trigger this every time the user or views eg is updated.

Thus i plan to disable exhaustive-deps for this line.

Is this wise?

useEffect(() => {
    async function handleViews() {
      await sendViews(views, user, cancelToken);
      ... do more
    }
    if (
      appStateVisible.match('inactive') ||
      appStateVisible.match('background')
    ) {
      handleViews();
    }
  }, [appStateVisible]);

Solution

  • No. You have a couple of solutions that are preferred to just ignore the rule.

    You could provide a referentially stable value. I sometimes like to use a useGetter sort of hook:

    const useGetter = <S>(value: S): (() => S) => {
      const ref = useRef(value);
      useLayoutEffect(() => {
        ref.current = value;
      });
      return useCallback(() => ref.current, [ref]);
    };
    

    which allow you to write something like:

    const getViewData = useGetter({ user, cancelToken })
    
    useEffect(() => {
        async function handleViews() {
          const { user, cancelToken } = getViewData()
          const user = await sendViews(views, user, cancelToken);
          ... do more
        }
        if (
          appStateVisible.match('inactive') ||
          appStateVisible.match('background')
        ) {
          handleViews();
        }
    }, [appStateVisible, getViewData]);
    

    Because getViewData don't change, the useEffect will not be triggered for any other reason than the change in the app state. And the return value will not be stale.

    As an alternative, you could provide all the dependencies normally, but this time check if appStateVisible changed or not yourself. Hooks like usePrevious that just refer to the previous value can be useful for checking that previousAppStateVisible !== undefined && appStateVisible !== previousAppStateVisible the value changed from the last render. If not, you can return early. ( https://usehooks.com/usePrevious/ )