So instead of "do something when this action/event happens" we get "do something when this data changes". Which ends up being abused a lot, as developers who aren't familiar with this mental model (which is most of them), tend to lump up every action into a data change.
So instead of something like (contrived example):
openAlert() {
alert("I'm an alert");
}
return <button onClick={openAlert()} />
people do: const [alertOpen,setAlertOpen] = useState(false);
useEffect(()=>{
if (alertOpen){
alert("I'm an alert");
}
return ()=>setAlertOpen(false); //if they even remember to unregister
},[alertOpen])
return <button onClick={()=setAlertOpen(true)} />
Beyond that there are multiple other problems:- the fact that useEffect (and hooks in general) is order dependent and can't be put inside branching (so I can't start an affect after an event happens).
- the fact that inside useEffect you read the data of the previous render is very unintuitive (I.E. your data is stale).
- the need to always put all your dependencies that are used inside useEffect in the dependency array explicitly - which can be even harder when dependencies are dynamic - is also a problem for many.
There's probably a few more, but that off the top of my mind.
The fact that there needs to be several eslint rules in order for people to not footgun themselves constantly is quite a red flag on it's own.
And are you sure the data in useEffect is stale? I don't think it is, the documentation[0] seems to suggest that it uses the current closure.
The rest I agree with.
[0] https://reactjs.org/docs/hooks-effect.html#example-using-hoo...
See "You don’t need Effects to handle user events".
There was (and still is) a real problem in react with composability. Which they tried to solve with mixins, then HOC and then hooks.
The three main hooks are useState, useContext and useEffect . useState and useContext are very straightforward and an overall improvement on their previous counterparts. useEffect was an improvement over mixins, but it was a worse solution for class component lifecycle methods.
It was fairly obvious that useEffect had a lot of problems from the get go, but it was deemed worthwhile to add so that functional programming proponents could have state without classes and to finally have a way to use composability without the horribleness of mixins and hocs (which were worse then useEffect).
The real problem is that useEffect has it's own problems, and while we no longer have the old problems, the new problems might be worse.