-
Notifications
You must be signed in to change notification settings - Fork 47.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
When updating from many-item to single-item array, re-render is forced although item key hasn't changed #1493
Comments
I can't immediately repro this. You can see from the source of traverseAllChildren.js that the intent is certainly to treat a single child as the same as an array: If you could post a jsfiddle or jsbin that would be helpful. In either case, it shouldn't be hard to use 0.10.0 or a nightly from http://react.zpao.com/builds/master/latest/. |
Tried to repro it myself but can't: http://jsfiddle.net/kb3gN/2231/, however there exists a somewhat unintuitive problem: http://jsfiddle.net/Pa5Ud/, if you wrap all of those 3 cases in another array, logic dictates that the result should be the same, but it isn't. I would assume that this is the problem you're seeing, although I'm thinking there is no possible |
Closing, but @gaearon if you can repro let me know. |
@syranide You're correct, the problem was due to a nested array (I didn't realize I was actually putting one array inside the other). The simplest repro: http://jsfiddle.net/EMeQ2/1/ Your repro is exactly how I came to this problem: I accidentally introduced an extra depth to each inner component, and the “one item” special case broke because of it. This is by design, right? It makes sense that But maybe we could document this in Keys section? It's not immediately obvious that |
@gaearon Yeah, this is an unfortunate side-effect of an optimization; if a component is given just one child then In-order for this optimization to successfully reuse the first child AFAIK they've made a very intentional decision to go this way as it avoids an unnecessary allocation in cases with single children. But it makes the behavior of arrays and objects as children really rather unintuitive. Thinking about the problem briefly, the only solution I see (while keeping the optimization) would be to add some simple logic that can detect when an entry goes from being a component to an (array of) array of components or vice versa. Updating the ID in this case is currently not feasible, but given #1570 + another PR or two and might be. But I'm not convinced that this would be a good idea. |
I'm not sure I understood you correctly so let me rephrase it. Can React warn me when the same node definition in JSX “jumps” between levels? render: function () {
var node = <div />;
if (this.state.condition) {
return node;
} else {
return <div>node</div>;
// or return [node]
} This is useful for cases when I, as a novice user, expect React to understand the node is being moved up/down a level, whereas React doesn't support this behavior. This would also have catched the previous use case with different level in nested arrays. If this is what you are suggesting, what are the challenges that make it difficult to implement now? What are potential false positives? |
@gaearon Keys in React are only considered unique/reusable within the container they're rendered inside Key-less components in React are given an implicit key that is equal to its index among the children of the parent. Meaning that |
I see, so these are the limitations of the current implementation. From your PRs I have seen, you are trying to change how React finds node's component, and plan to get rid of It would have been awesome if React not just warned, but also recognized “move up/down an array/container” as a valid operation. |
@gaearon I think you misunderstood my intent, what I described above is not a flaw in React, it is how it's designed. If you don't explicitly add a So considering that we render the following next: So given that we now have a snapshot of the previous and next hierarchy, as React sees it:
The following will happen:
When dealing with nesting such as: |
Ah okay. I misunderstood this paragraph, can you elaborate on it?
But still, I have a few questions.
At the risk of suggesting something very stupid.. React cannot see it, but JSX generator can. Could we possibly solve this in JSX generator, or are we constrained by keeping it a simple syntax transform? I'm talking about render: function () {
var node = <div />;
if (this.state.condition) {
return node;
} else {
return <div>node</div>;
// or return [node]
}
} being translated to something like render: function () {
var node = React.DOM.div({ uniqueKey: React.nextUniqueKey() });
if (this.state.condition) {
return node;
} else {
return React.DOM.div({ uniqueKey: React.nextUniqueKey() }, node);
// or return [node]
}
} The (huge?) downside is sacrificing the simplicity of the transform, the upside is being able to reason about node moving up or down in the hierarchy. |
Oh wait, this is totally wrong. In fact JSX generator would need to generate these IDs itself at compile time. This really looks too much of bookkeeping to place on a now-transparent generator so probably not a good direction. Still, I wouldn't mind it if a separate (optional?) tool processed my JSX files and amended calls to component constructors with an extra “unique-as-in-source-file” key that React could recognize to better reason about nodes in runtime. |
Given If alternating rendering one and then the other (without the special-case), only
But we want the first child to be reused, so React must ignore the first array when constructing the ID to compensate for the fact that As for your examples of |
I understand now, thanks for taking time to explain!
Yeah, right.
Is there some discussion on the web that I can read? |
@gaearon No problem :) IIRC there's no log for the discussion, it was mostly a quick idea on my part and not really thorougly evaluated / detailed. If this is something you feel strongly about and can make a case for, I say it's worth "giving it a shot". I think it makes a lot of sense from a theoretical perspective, it's basically the only feature that "manual construction/destruction of components" can boast, but React can't. |
This is actually avoidable after-all #2378 |
Consider this:
Usually,
key
is respected in this case and elements don't get unmounted and remounted unnecessarily if theirkey
s match between subsequentrender
calls.However, if all elements but one are removed, React 0.10.0 for some reason doesn't realize it's the same component (the
key
hasn't changed) and forces its DOM node to update.I tried setting a breakpoint inside
shouldUpdateReactComponent
and_updateChildren
to debug the issue and it looks likeprevComponentInstance
isnull
because its name innextChildren
andprevChildren
is slightly different (in one case it includes leading0:
, in the other it doesn't).Anyway, the workaround for me was, curiously, not returning an array when there is just one item, and rendering it as is. In this case, React was able to understand it's the same component, and didn't force an unnecessary re-render.
I'm sorry for not providing a workable fiddle or something—jsFiddle hosts an old version of React, and it's 8am where I live so I should go to sleep. This should be fairly simple to reproduce, unless it's already fixed.
The text was updated successfully, but these errors were encountered: