I'm new to React and trying to make a loading/greeting page that navigates on to the next after a few seconds of being shown. In React Router v6, we have the useNavigate()
hook to allow you to control the navigation, and I am using this to successfully call the navigate function by setting a timeout in a useEffect()
hook. However, the compiler is complaining that I have a missing dependency. I only want it to run once though, not whenever the navigate
changes. What is the best way to do this?
Thanks!
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
function Greeting(props) {
const navigate = useNavigate();
useEffect(() => {
setTimeout(() => navigate(props.nextPage), 3000);
}, []);
return (
<div className="Greeting">
<div>Hello World!</div>
</div>
);
}
export default Greeting;
Line 9:6: React Hook useEffect has a missing dependency: 'navigate'. Either include it or remove the dependency array react-hooks/exhaustive-deps
The useEffect
hook is missing dependencies, both the navigate
function and props.nextPage
that are referenced in the callback. Add them to the dependency array. Don't forget to return a cleanup function in the case that the component unmounts prior to the timeout expiring on its own.
useEffect(() => {
const timerId = setTimeout(() => navigate(props.nextPage), 3000);
return () => clearTimeout(timerId);
}, [navigate, props.nextPage]);
As a general rule you should follow all guidance from the linter. The react-hooks/exhaustive-deps
rule is there to help you write better code. Don't disable the rule for that line unless you absolutely know what you are doing and what future consequences may arise if/when you ever update the callback logic and possibly change dependencies.
You can simply hide this warning by adding this:
useEffect(() => {
setTimeout(() => navigate(props.nextPage), 3000);
// eslint-disable-next-line
}, []);
This warning is displayed by eslint rules. You can read more about this at How to fix missing dependency warning when using useEffect React Hook
Since navigate
is actually a dependency, just add it to the dependency array (The second argument in the useEffect() hook)
Don't worry, the function will still run on mount and it is a safe enough bet that navigate
won't change and cause an unwanted 2nd setTimeout
To be really safe, you can actually put in code to make sure the setTimeout is run only once, but it is overkill anyway
Depending on the structure of how you are wanting to set up the project, you can use a useRef hook to eliminate the needed dependency of the useNavigate hook. Or you can add it into the dependency array. Here is an example of doing that.
const navigate = useRef(useNavigate());
useEffect(() => {
const timeout = setTimeout(() => navigate.current(props.nextPage), 3000);
return () => {
clearTimeout(timeout);
};
}, [props.nextPage]);
However, the compiler is complaining that I have a missing dependency
That is not a compiler error, it's an eslint warning.
ESLint is not very smart and does not know if something should be added as a dependency or not, so it defaults to warning you about potential problems and you are then free to disable this warning if you know that it does not apply to you.
navigate
to the list of dependencies when using it in useEffect
?It depends.
The useNavigate()
hook depends on useLocation().pathname
+ a few other things .
So we can change the question to this:
Should your useEffect
hook run again if the path changes?
Reframing the question like this should make the answer more obvious for new devs.
Important note:
You will need to add naviagate
as a dependency if you are navigating using relative paths.
This is because naviagate
uses useLocation().pathname
to resolve relative paths.
In general I would advise against using relative paths when possible.
The accepted answer here says you should always add navigate
to the list of dependencies, but this can easily lead to problems that are hard to debug if you don't know that navigate
can change.
In most cases your component will only exist on one path, so it wont matter what you do.
The other people answering here apparently don't have much experience with react-router
, so they likely never encountered the case where the choice made a difference.
Anyway here are your choices:
props.nextPage
changesconst navigate = useNavigate();
useEffect(() => {
const timeout = setTimeout(() => navigate(props.nextPage), 3000);
return () => clearTimeout(timeout);
}, [navigate, props.nextPage]);
props.nextPage
changesconst navigate = useNavigate();
useEffect(() => {
const timeout = setTimeout(() => navigate(props.nextPage), 3000);
return () => clearTimeout(timeout);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.nextPage]);
const navigate = useNavigate();
useEffect(() => {
setTimeout(() => navigate(props.nextPage), 3000);
return () => clearTimeout(timeout);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Note about useEffect
callbacks:
useEffect
is re-run I've added a callback that runs clearTimeout()
.trying to make a loading/greeting page that navigates on to the next after a few seconds of being shown
I'm fairly sure you're not going to change the path or the nextPage prop during those few seconds, so in your case it wont make a difference whether you add the dependencies or not.
The only reason why I can suggest adding variables even when they are not necessary is because disabling eslint warnings can make it easier for you to forget to add variables that do change.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.