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.
@gaearon your last point touched something I didn't want to mention but I'll do it: whatever key-like solution we choose, the state of the component linked to that unique key is destroyed if on next render the key doesn't exist anywhere anymore. Otherwise (if you allow the state linked to key to persist across renders where it's not present), you'd have no idea when to garbage collect that state, since that same key might appear an hour later. With async rendering this gets way too hacky. Which is actually one of the reason I'm trying to look into cursors: you don't have this problem with them, I think.
Your first-class key solution is close to what we've imagined before. The trouble I had with it is the following: if you receive an array from an unknown source and generate components based on it, to be able to keep the state of these list items with the first-class key, you'd need to generate them, store them in the state (and keep track of which list item already has a key and which list item is new...), then in your render, access those new keys from the state. Can't do that reliably.
If anyone's wondering about the use-case of preserving the state of a dynamic list across, say, reparenting, imagine a list of images that you want to put in a light box, or a list of items that you drag from container1 and drop into container2, or animated transition that takes one component, animate it halfway, and move it into another container to finish its animation.
@threepointone then maybe what you're saying is that cursor + one global state is effectively the way to go? =)
@jaredly: yeah, this should be an optimization that comes more or less for free once we solve this problem.