Search code examples
reactjsinfinite-loopuse-effect

React Hook useEffect has missing dependencies: xxx. Either include them or remove the dependency array react-hooks/exhaustive-deps


I am making an api call from useEffect hook

function ChangePassword(props) {
    const token = props.match.params.token;

    const [state, setState] = useState({
        password: "",
        confirmPassword: "",
    });
    const [status, setStatus] = useState({
        loaded: false,
        auth: false,
    });

    useEffect(() => {
        let { auth } = status;

        axios
            .get(
                `http://localhost:2606/api/hostler/changepassword?token=${token}`
            )
            .then((res) => {
                console.log("res", res);
                auth = res.status === 202;
            })
            .then(() => setStatus({ auth, loaded: true }))
            .catch((err) => console.log("err", err));
    },[]);

    return (
        // code
    );
}

But react gives warning

React Hook useEffect has missing dependencies: 'status' and 'token'. Either include them or remove the dependency array react-hooks/exhaustive-deps

also adding status to dependency array will result in an infinite loop because setStatus is called inside of useEffect


Solution

  • If you want the effect to run only once when the component mounts then it is technically correct to specify an empty dependency array. However, the React-hooks linting rules aren't able to differentiate this case. You can disable the rule specifically for that line.

    I notice also that your effect doesn't really have a dependency on status.auth since you are always mutating/overwriting it anyway, you can remove it and just set the new auth state value.

    useEffect(() => {
      axios
        .get(
          `http://localhost:2606/api/hostler/changepassword?token=${token}`
        )
        .then((res) => {
          console.log("res", res);
          setStatus({ auth: res.status === 202, loaded: true })
        })
        .then(() => )
        .catch((err) => console.log("err", err));
    
      // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);
    

    However, disabling the rule will possibly mask future updates, so you'll want to include token as a dependency. If the component rerenders/remounts and the token has changed you'll want to ensure you are working with the latest values. In other words, you don't want to use stale state/prop values.

    useEffect(() => {
      axios
        .get(
          `http://localhost:2606/api/hostler/changepassword?token=${token}`
        )
        .then((res) => {
          console.log("res", res);
          setStatus({ auth: res.status === 202, loaded: true })
        })
        .then(() => )
        .catch((err) => console.log("err", err));
    
    }, [token]);
    

    If you want to only run the GET request when auth is false then it would be a dependency and should be included. And so you don't render loop if res.status === 202 resolves false, also include a condition that you haven't completed loading yet.

    useEffect(() => {
      !auth && !loaded && axios
        .get(
          `http://localhost:2606/api/hostler/changepassword?token=${token}`
        )
        .then((res) => {
          console.log("res", res);
          setStatus({ auth: res.status === 202, loaded: true })
        })
        .then(() => )
        .catch((err) => console.log("err", err));
    
    }, [auth, loaded, token]);