5 ms·
Hooks feel like a good system that stopped right before it became pretty. Like, an componentDidMount event is a now useEffect with no second argument? But a c
by knodi123 4y ago
Hooks feel like a good system that stopped right before it became pretty. Like, an componentDidMount event is a now useEffect with no second argument? But a componentWillUnmount event is now a useEffect, with no second argument, that returns a callback?
It's powerful, and it's not that hard to use, but it's cryptic and random.
Why call it useEffect instead of some other more meaningful phrase? I mean, "componentDidMount" tells you exactly what it is. Does "useEffect"?
Why should onload things be a function, but onunload should be a function returned by a function?
Why does useRef give you a thing used for attaching handles to DOM elements so you can refer to them elsewhere, AND it gives you an object whose .current property can be used as a variable that persists without causing a render?
It's like someone complained that there were two many functions in the API, and their names were too long, so they overcorrected in the opposite direction.
- ajkjk 4y agoI love hooks, but I think they made a few mistakes. The weirdness around useEffect having different behavior with no second argument vs [] is one of them. And they should have included a useUnloadEffect() by default, even though it's trivial to write, just for clarity. It's way too easy to miscount the number of ()s in useEffect(() => () => {}, []); They also should have included a few other basic hooks, like useStableValue() for just computing a constant once on first render. I hate the name `componentDidMount` though. useEffect seems much better to me.
- kareemsabri 4y agouseRef is useStableValue
- ajkjk 4y agoNot quite, having to use for current everywhere is just annoying, and it doesn't allow for a one-time constructor function.
- kareemsabri 4y agoWhy do you have to use current everywhere? You can do const { current: myStableValue } = useRef(computation); Not sure what you mean by one time constructor function, but you can pass the result of a function to initial value. const { current: myStableValue } = useRef((() => { //called once })());
- tshaddox 4y agoThis is incorrect. useRef takes an initialValue argument, but it does not treat functions as lazy initializers like useState does. If you pass a function to useRef, you’re just going to get that function as the initial value. If you want to lazily initialize a ref, you need to manually check if it has been initialized and run your expensive code if it hasn’t. Dan Abramov provides what appears to be a pattern officially recommended by the React team: https://github.com/facebook/react/issues/14490#issuecomment-454973512 https://github.com/facebook/react/issues/14490#issuecomment-...
- kareemsabri 4y agoIt's not a function, it's an immediately-invoked function expression. Edit: I guess it would be a bit inefficient to invoke the IIFE every render just to initialize the value once. Probably better to use useMemo with [] as the dependency list then, either one achieves the same result.
- tshaddox 4y agoI missed that you were immediately invoking the function expression. But yes, that doesn't save you from running the expensive calculation every subsequent render.
- graftak 4y agoI always do `const [stableValue] = useState(() => ...)`. It’s clear, simple, and easy to promote to an interactive value.
- AgentME 4y agoIt seems like almost always if you want to do anything during unmount, it's cleaning up something you set up in a useEffect call, which you also want to do any time the useEffect call's dependency list changes, so it being part of the useEffect hook helps programmers fall into the pit of success. React's older class-based components had the unmount handling separate (componentWillUnmount), and in my experience on a large React codebase, many if not most components using componentWillUnmount were subtly flawed and failed to handle certain cases where props or state updated in a way that should have caused effects to be cleaned up or adjusted. React's useEffect hook way of grouping up effects with their own cleanup code helps people handle these cases correctly even without realizing it sometimes.
- nickdandakis 4y agoYou're translating the class-based way of working in React against hooks, without stopping to consider understanding hooks as a first-class principle instead of a translation. It's called useEffect because it runs on the (side)effects observed by the dependency array. An empty array happens to happen on mount. useEffect returns a teardown function which happens to correlate with unmount when an empty array is passed. It's called useRef because it returns a (mutable) reference that's detached from the reactive layer. The DOM element connection is just sugar. I agree that useEffect has a lot of footguns, but this position seems very shallow. The whole pattern changed, it doesn't make a lot of sense to continue comparing the two
- pcthrowaway 4y ago> useEffect returns a teardown function which happens to correlate with unmount when an empty array is passed. I believe this teardown function runs on unmount whether or not the dependency array is empty.
- NTARelix 4y agoThis is correct, but I think the point nickdandakis was trying to make is that useEffect does not directly correlate with componentDidUnmount because useEffect's returned callback could be called in the middle of the component's lifecycle, not only when the component unmounts.
- pcthrowaway 4y agoErr.. no, you pass a callback (let's call it the effect callback) that returns a callback (the cleanup callback) to useEffect. The effect callback gets called when the component mounts, and when dependencies (if any) change. The cleanup callback gets called when the dependency unmounts (and will not be called at other times in the lifecycle) edit: I'm wrong, the cleanup is also called when dependencies change, just not on mount.. eg. it gets called before the effect callback being called again. In other words, the cleanup function is called on unmount when useEffect is called with an empty array, and if useEffect has dependencies, it will also be called when those dependencies change
- madeofpalk 4y ago> Like, an componentDidMount event is a now useEffect with no second argument? Doesn't useEffect with no second argument run after every render? componentDidMount's equivalent is for an empty dependency array in the second argument?
- Rapzid 4y agoBingo. And this is the situation we are in. Even knowing basically what's going on and still getting it wrong with just the bare minimum choices of ,_ or ,[]. Mix in actual deps coming from other hooks, changing as the components re-render, and multiple layers of "custom hooks" with more of the same and it's like trying to hold back a tsunami.
- Griffinsauce 4y agoThey could've easily added useOnMount, useOnRender, etc It doesn't matter that they are sugar. Just like useState is built on useReducer, it would be massively helpful to simplify and clarify what's going on.
- nightski 4y agoSure but these functions would be trivial to define in one's own code base if they so desired.
- madeofpalk 4y agoThey very purposefully didn't add useOnMount because of the subtle bugs developers introduce when they do things only on mount. People would react to the initial props in componentDidMount, but never respond to their change. Hooks, and useEffect, was supposed to be designed in a way to eliminate that class of bugs.
- phailhaus 4y agoI totally agree about the cryptic-ness when it comes to useEffect and its usage, but the neat thing is that you can trivially write your own thin wrappers with better names! I think `useEffect` is the best name possible given how broad its use cases are, because it's literally for any function side effect.