React Hook useEffect has a missing dependency with useEffect

1.4k Views Asked by At

I am trying to Save to Local in React and my app is working, but I cannot deploy it because of this warning. The warning is:

React Hook useEffect has a missing dependency: 'saveLocalTodos'. Either include it or remove the dependency array

And my code is:

// Run once when the app starts
  useEffect(() => {
    getLocalTodos();
  }, []);

  // useEffect
  useEffect(() => {
    // Function
    function filterHandler() {
      switch (status) {
        case `completed`:
          setFilteredTodos(todos.filter((todo) => todo.completed === true));
          break;
        case `uncompleted`:
          setFilteredTodos(todos.filter((todo) => todo.completed === false));
          break;
        default:
          setFilteredTodos(todos);
          break;
      }
    }
    filterHandler();
    saveLocalTodos();
  }, [todos, status]);

  // Save to Local
  const saveLocalTodos = () => {
    localStorage.setItem("todos", JSON.stringify(todos));
  };
  const getLocalTodos = () => {
    if (localStorage.getItem("todos") === null) {
      localStorage.setItem("todos", JSON.stringify([]));
    } else {
      let todoLocal = JSON.parse(localStorage.getItem(`todos`));
      setTodos(todoLocal);
    }
  };
2

There are 2 best solutions below

5
On BEST ANSWER

Just include your dependency in the dependencies array of React.useEffect then.

You're using saveLocalTodos inside your useEffect but not defining it in the dependencies array. Normally, the rule of thumb is to include everything (functions, variables, state, props) which is used inside the useEffect to be in the dependencies array. Because your effect depends on them and should reinvoke itself once their value changes.

 const saveLocalTodos = React.useCallback(() => {
    localStorage.setItem("todos", JSON.stringify(todos));
  }, [todos]);

  useEffect(() => {
    // Function
    function filterHandler() {
      switch (status) {
        case `completed`:
          setFilteredTodos(todos.filter((todo) => todo.completed === true));
          break;
        case `uncompleted`:
          setFilteredTodos(todos.filter((todo) => todo.completed === false));
          break;
        default:
          setFilteredTodos(todos);
          break;
      }
    }
    filterHandler();
    saveLocalTodos();
  }, [todos, status, saveLocalTodos]);

Also, wrap your saveLocalTodods with React.useCallback because, in every re-render of your component, the function reference changes. Then your effect will be fired for no reason. Put todos in the dependencies array inside the saveLocalTodos. You want your function to change only when todos change. Otherwise, you will get stale todos.

0
On

You are getting this error because inside useEffect you're calling getLocalTodos, saveLocalTodos function which are defined outside of useEffect. Ideally inside useEffect dependency array you should define all the outer function, props variable etc. which are used inside useEffect. So whenever there is any changes in the dependency the effect will be triggered inside useEffect. When you are creating any function wrap that function inside React.useCallback and there you can pass dependency of that particular function , just like for your case saveLocalTodos is dependent on todos, so your function will only change when todos will change. In that way your function will change only when necessary.

      const filterHandler = React.useCallback(() => {
        switch (status) {
            case `completed`:
              setFilteredTodos(todos.filter((todo) => todo.completed === true));
              break;
            case `uncompleted`:
              setFilteredTodos(todos.filter((todo) => todo.completed === false));
              break;
            default:
              setFilteredTodos(todos);
              break;
          }
      }, [todos, status]);
      
      // Save to Local
      const saveLocalTodos = React.useCallback(() => {
        localStorage.setItem("todos", JSON.stringify(todos));
      }, [todos]);
      
      const getLocalTodos = React.useCallback(() => {
        if (localStorage.getItem("todos") === null) {
          localStorage.setItem("todos", JSON.stringify([]));
        } else {
          let todoLocal = JSON.parse(localStorage.getItem(`todos`));
          setTodos(todoLocal);
        }
      },[]);

      useEffect(() => {
        filterHandler();
        saveLocalTodos();
        getLocalTodos();
      }, [getLocalTodos, filterHandler, saveLocalTodos]);