Home > database >  why history is needed in dependency array of useEffect
why history is needed in dependency array of useEffect

Time:09-24

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.

  • Related