key
is pretty much crucial for state perservation in React. As of React 0.13 it can't do the following things:
- Clone state
<Comp key={1} /><Comp key={1} />
- Preserve component state across different parents:
// first render
<div><Comp key={1} /></div>
// second render, state destroyed
<span><Comp key={1} /></span>
- More generally, teleport state to another subtree completely:
// render of myComp1
<Comp key={uuid} />
// next render, myComp2
<Comp key={uuid} />
The reason I want these is to unlock some insane tricks, e.g.
- Teleport a half-done animation to another place, i.e. animated transition across views (easily solvable otherwise).
- A generalized version of frosted glass effect.
- Other similar logic, e.g. drag and drop component where a stateful component is transported somewhere else.
But I dislike key for a few reasons, one being that it's basically a user-generated hash, and that's unreliable and doesn't scale: for example, that duplicate component trick is risky, as it's not clear whether we really wanted <Comp key={1} /><Comp key={1} />
or if we accidentally did a key collision.
The (only?) other used alternative I've seen for key is cursors. But I've repeatedly heard that while these solve the problem (?), they're a bit tedius to work with. Is this true? Please leave a comment if you have any experience regarding this. The solution doesn't need reactive updates, or easier state propagation, or any of the nice extras; it just needs to solve the above few cases.
@syranide I think your solution is semantically similar, but @gaearon's allows the components props/children to be changed during re-render without discarding state, which is likely an important use case. That said, I think @gaearon's solution suffers from a problem of when to garbage collect (what if a component passes a key to the parent via a callback? The owner could die before the key does), (what if a component generates a new key for each element in a list, and the list keeps getting new elements? You could easily leak memory despite holding no references).
Either solution, I think you are effectively trying to hang on to a component tree (either explicitly or by reference) in order to preserve state. I find myself agreeing with @threepointone, that state should be hoisted up in these situations. Then, re-using nodes is purely a perf optimization (modulo the state of poorly-designed nodes who's state (like focus) can't be pulled out, but we can (maybe) ignore these in most re-parenting situations). See: facebook/react#3653 (comment)
I think hoisting up state elegantly solves all the use cases mentioned in @chenglou's gist, where
key
is replaced with an opaqueinternalState
object as described in facebook/react#3653 (comment).