I would like to do something like this:
//snippet 1
const logFooOnlyOnce = (foo: string) => console.log(foo) //"...only once during the lifetime of this component"
export const MyComponent = ({ foo }: { foo: string }): JSX.Element => {
React.useEffect(() => {
logFooOnlyOnce(foo)
}, []) //<-- error
return <div />
}
Ie. i want to look at a prop of a react component only once and only in a certain scope. This code works with plain JS as i would expect it.
But the eslint rule won't let me. It changes my code to do this:
//snippet 2
import React from 'react'
const logFooOnlyOnce = (foo: string) => console.log(foo) //"...only once during the lifetime of this component"
export const MyComponent = ({ foo }: { foo: string }): JSX.Element => {
React.useEffect(() => {
logFooOnlyOnce(foo)
}, [foo])
return <div />
}
However this is not what i want, if foo
changes it's value, it will be logged more than once.
This seems to work, though:
//snippet 3
const logFooOnlyOnce = (foo: string) => console.log(foo) //"...only once during the lifetime of this component"
export const MyComponent = ({ foo }: { foo: string }): JSX.Element => {
const fooRef = React.useRef(foo)
React.useEffect(() => {
logFooOnlyOnce(fooRef.current)
}, [])
return <div />
}
If snippet 3 is valid, can i ignore the empty dep list in snippet 1
? I don't want to handle an edge case, i want to ignore it altogether.
Also, i notice that the rule can somewhat be cheated with:
//snippet 4
const logFooOnlyOnce = (foo: string) => console.log(foo) //"...only once during the lifetime of this component"
const myCallback = (foo: string) => () => {
logFooOnlyOnce(foo)
}
export const MyComponent = ({ foo }: { foo: string }): JSX.Element => {
React.useEffect(myCallback(foo), [])
return <div />
}
As well as this:
//snippet 5
const logFooOnlyOnce = (foo: string) => console.log(foo) //"...only once during the lifetime of this component"
export const MyComponent = ({ foo }: { foo: string }): JSX.Element => {
const myCallback = (foo: string) => () => logFooOnlyOnce(foo)
React.useEffect(myCallback(foo), [])
return <div />
}
Why is this rule complaining in this case, and what would be the cleanest way to achieve what i want?
In order to apply an eslint-ignore
directive, i need to have a very good case to argue with a higher power.
CodePudding user response:
Just ignore the rule, using this comment
// eslint-disable-next-line react-hooks/exhaustive-deps
Ex.
export const MyComponent = ({ foo }: { foo: string }): JSX.Element => {
React.useEffect(() => {
logFooOnlyOnce(foo)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return <div />
}
CodePudding user response:
If you dislike ignoring the rule, you can also make a helper function that makes it clear what you want without having to put eslint-disables everywhere:
import { EffectCallback, useEffect } from 'react';
const useEffectOnce = (effect: EffectCallback) => {
useEffect(effect, []);
};
(You can reference this but it's extremely short)