In the following example, I used history
to redirect some link.
const history = useHistory()
useEffect(() => {
history.push(`/my/link`)
}, [])
Then react complains a missing dependency history
. I do not understand why history
can be a dependency here. It is not a state
variable or from props
.
If I add history
to the dependency array, will it cause an infinite call of useEffect
. Because history
is changed when history.push(
/my/link)
happens.
const history = useHistory()
useEffect(() => {
history.push(`/my/link`)
}, [history])
Is my understanding correct here?
CodePudding user response:
While history
is not directly a state variable (from your component's perspective) nor is it a prop, it is the output of a hook and therefore likely held in state within the hook. Also the exhaustive dependency rule does not just apply to state and prop variables but to any variable whose value might change.
If you want to avoid that infinite rerender, you can just put history.push
in your dependency array or just take push
out of history
and use it directly and put push
in the dependency array. Examples:
const history = useHistory();
React.useEffect(() => {
history.push("/my/link");
}, [history.push]);
const { push } = useHistory();
React.useEffect(() => {
push("/my/link");
}, [push]);
Alternatively, for this specific example, you might consider using the <Redirect>
component instead of history.push
.
CodePudding user response:
There are many things that need clarification.
First, it's not React which complains, but ESlint. Specifically rule react-hooks/exhaustive-deps
from eslint-plugin-react-hooks
which you always can disable but before doing that we better get sure it's the only option.
Next, no, history
is not different. Under the hood useHistory
refers to browser's History API and object it returns stays referentially the same. So no, there would be no rerender loop. Yes, React Router wrapper around it returns the same object that will mutate on future navigation so it's applicable to it as well.
Also quite probable that upon navigation to different address, your application goes to different route configured and that component goes away, being replaced with different set of components. So it would not rerender even if history
was referentially different(that is not the case anyway).
Finally, ESlint does not understand meaning of variables and that they are staying the same, neither that fact you are navigating away. The only thing it does - ensures all the variables used in callback body and declared inside the components tree, are listed in dependencies. Just like that.
In your specific case you can add it into dependencies or disable specific ESlint rule on per-line basis. I'd suggest you go with first approach - it's better for readability to disable rules as rarely as possible. Each time you disable rule is a source of question "why this was disabled?" later and also source of potential issues.