eslint warnings are there to warn users about inapproriate use of useEffect. Since hooks depend a lot on closures its extremely important that we write them properly
Now the reason that eslint warns you for missing dependency is because you use onUpdate
inside of useEffect
Now ESlint is good not not extremely intelligent to figure out what you as a developer want
Its absolutely allright to want the function to be called only on initial render. However ESlint doesn't know if you have intervals or subscriptions within the function which depend on the closure variables and thus warns you that onUpdate might need to be re-run whenever its recreated or its dependency changed
If you are absolutely sure that what you are writing is correct, you could disable the warning like
useEffect(() => {
props.onUpdate(from, to);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Now that your intention is to call the onUpdate on from and to change which you do via handleClick, it actually right to add them as dependency too
const [state, setState] = useState(initial);
useEffect(() => {
props.onUpdate(from, to);
}, [from, to]);
const handleClick = (e) => {
const filter = e.target.value;
const { from, to } = getDatesFromFilterValue(filter);
setState((prev) => ({ ...prev, from, to, filter }));
};
Now one last thing, you can add onUpdate as a dependency to useEffect if you write it with a useCallback in its parent to make sure that its only created when needs
const Parent = () => {
const [state, setState]= useState({});
const onUpdate = useCallback((from, to) => {
setState(prev => ({
// use functional setState here to get updated state using prev
// and return the updated value
}))
}, [])
...
return (
<Child onUpdate={onUpdate} />
)
}
Child
const [state, setState] = useState(initial);
useEffect(() => {
props.onUpdate(from, to);
}, [from, to, onUpdate]);
// Now that onUpdate is created on once, adding it to dependency will not be an issue
const handleClick = (e) => {
const filter = e.target.value;
const { from, to } = getDatesFromFilterValue(filter);
setState((prev) => ({ ...prev, from, to, filter }));
};