5 ms·
"You might be curious how React knows which component useState corresponds to since we’re not passing anything like this back to React. We’ll answer this questi
by hliyan 8y ago
"You might be curious how React knows which component useState corresponds to since we’re not passing anything like this back to React. We’ll answer this question and many others in the FAQ section."
While I appreciate the functional usage of state, this type of magical behavior worries me a bit. Wasn't more straightforward semantics possible (even if the syntax wasn't similarly straightforward)?
- kiliancs 8y ago> React assumes that if you call useState many times, you do it in the same order during every render. Note they also say: > We provide a linter plugin to enforce these rules automatically. Makes me feel a little better.
- maaaats 8y agoWhy couldn't one just pass in `this` to useState, a key, or something similar? Like, if I have two state variables, pass in a name for both, instead of relying on the order being the same.
- adriancooney 8y agoI doubt the linter will enforce the same-ordering, that would require state between runs. I think the linter just ensures hooks are run at the top level in the functional component.
- atomwaffel 8y agoAnd it wouldn't need to. You're free to move around calls to useState() to your heart's content in your code, you just need to call them in the same order during the lifecycle of your component.
- joelg236 8y agoIn the keynote, Dan talked about how this works. It depends on the order that useState is called, which makes conditional branches a no-no (there's a linter for it).
- rhacker 8y agoSince JS is single threaded, they are basically keeping track of which component was last called for render - that way they know useState() calls attached to which components. If the order changes, then they can't keep track of the different objects tracked by useState. So treat it like you would class state - all fields are always there, no matter which functions or conditional branches you take - hence - do your initialization above all your conditionals and loops. Edit: And yes, slight concern of course, it's the got some rough semantic equivalents to a thread-local in Java, albeit in Java the danger exists downstream, this danger is only exhibited during initialization it seems.
- jcheng 8y agoIIUC, the order isn't used to figure out which React component is being rendered, but which of potentially several hook invocations within that single component is currently being invoked. If every unique hook could only be (directly or indirectly) called a single time per component, then there wouldn't need to be this ordering restriction. (Or more realistically, if each hook call also took an ID that is unique per-component.)
- Guillaume86 8y agoJust looking at the code I came to the conclusion it had to work that way, I don't like it, way too easy to break and some people are going to be very confused when things goes wrong. It's a uncommon/unfamiliar coding constraint. If you have to declare them all in the same order every time, you might as well force a single state (like the setState API) and/or keyed state values and have a safe API.
- Fellshard 8y agoThis 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.
- dbbk 8y agoComponent 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.
- mhink 8y agoThey'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.
- spankalee 8y agoIt'd be really great for VDOM, builder APIs, and any immediate mode -> retained mode mapping, to be able to get the _callsite_ of a function invocation. Then they could associate state with the callsite unambiguously without resorting to tracking invocations on a stack and disallowing control flow. Tagged template literals already give us something of a callsite, since the template strings object is cached per callsite. You could do something ugly and use that to associate state: const state = useState``();