This is where I'm falling on it. What started as initial excitement over a lens-like addition started to feel more like black magic once I realized that it relies _a lot_ on implicit ordering just to preserve a seemingly simple usage pattern.
While it may _look_ nice to be able to just call `useEffect` and have it infer the rest, it just ends up masking the flow of a hidden parameter into that component, and convolutes the user's understanding of what React is actually doing.
I think people are getting too hung up on the "implicit ordering" thing. The only thing that really matters about the ordering of hooks is that the call pattern remains stable between render passes. This code:
Component lifecycle methods are easy to grasp conceptually. At different stages of rendering the component, React will call one of these methods. Easy.
With `useEffect`, I have no idea what is actually happening here, and I've tried re-reading it a few times.
They're easy to grasp conceptually, but they're a pain to use in practice specifically because they force you to split up code which is conceptually related. I've had to deal with some truly monstrous class components in the past which would have been a hell of a lot easier to deal with if we had this.
As for `useEffect`: think about the kind of logic you'd put in `componentDidMount`, `componentDidUpdate`, and `componentWillUnmount`. In CDM, we set up side effects, in CDU, we update those side effects if needed (based on the props, state, and context) and in CWU, we clean up those side effects. If we squint a little, though, we're really only doing two actual operations in those three steps:
didMount: perform side effect A based on props/state/context
didUpdate: clean up side effect A, then perform side effect B based on props/state/context
didUpdate (again): clean up side effect B, then perform side effect C based on props/state/context
willUnmount: clean up side effect C
So the idea here is:
const [name, setName] = useState("world");
function modifyTitle() {
document.title = "Hello, " + name + "!";
return restoreTitle;
}
function restoreTitle() {
document.title = "Untitled";
}
useEffect(modifyTitle);
When this component is rendered, it'll run `modifyTitle` on the first pass, and store its return value (`restoreTitle`) for later. When it's rendered the _next_ time, it'll call `restoreTitle` first and then `modifyTitle` again... which itself returns a new "cleanup function".
So we basically have the same sequence of calls as this is mounted/updated/unmounted:
perform side effect A
cleanup side effect A, then perform side effect B
cleanup side effect B, then perform side effect C
cleanup side effect C
The only difference is we never told React _when_ to perform these steps, just _that it should_ perform them at the appropriate time.
While it may _look_ nice to be able to just call `useEffect` and have it infer the rest, it just ends up masking the flow of a hidden parameter into that component, and convolutes the user's understanding of what React is actually doing.