-
Notifications
You must be signed in to change notification settings - Fork 48.6k
Remove key-collision invariant. #1201
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
Conversation
Are you sure? It really seems like it should break on duplicate keys, if not immediately then possibly later when the wrong node is removed, and it really ought to, you f-ed up. However, if both nodes have identical sub-hierarchy it could probably "adopt" the wrong one without breaking, but in other cases it should break. |
@syranide The collision case should be treated just like missing keys, in which we continue rendering just fine. There is no reason the whole app has to break (assuming your whole app is built using react). |
if (Array.isArray(children)) { | ||
for (var i = 0; i < children.length; i++) { | ||
var child = children[i]; | ||
key = getComponentKey(child, i); | ||
if (traverseContext && traverseContext.hasOwnProperty(key)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can't rely on traverseContext
being an object holding the children. In ReactChildren.mapChildren
, traverseContext
is a MapBookKeeping
object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, my initial version used a separate map to keep track of collisions but @sebmarkbage was heavily opposed because of perf implications - would it be reasonable to force contexts to provide a getResultMap
function or something like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The best approach may be to put the logic in flattenChildren, though there you only have the full key, not the parts. (Also your code here is wrong because you want to be checking for nameSoFar + (nameSoFar ? SUBSEPARATOR : SEPARATOR) + key
, not just key
.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In flattenChildren I have no way to reassign the key.
@cpojer @spicyj What is the intended solution to this problem? I mean, so duplicate keys are unacceptable. So one solution is to "unassign" conflicting keys, but that will wreak havoc on child behavior, also, what should happen when the component with the still assigned key is removed, should the "unassigned" component recreate itself to get that key? Or should it remain "unassigned" indefinitely? While neither sounds great, having it recreate itself and get the key sounds like the best action. However, conflicting keys would be a major no-no performance-wise (but perhaps it just has to work). The best solution is possibly that in addition to their keys, keys may also have a key-index, which basically translates to Theoretically, it seems that components could somehow keep track of their own key-index as well/instead, allowing removes and inserts to have correct behavior, or at least better behavior (as in, they get the wrong props, but they are not recreated). But I'm not sure right now at what cost that would be. Intuitively it seems like just keeping the above map and if a component has an index, that is used to set Or? |
Needs some @sebmarkbage up in here |
Duplicate keys is always incorrect. This is just trying to avoid a hard crash and try to keep going as long as possible. However, this can always lead to incorrect state. The solution should be whatever is fastest in the default case with no collisions. Perf in the broken case doesn't really matter. Can't we just prefix all collisions with a counter format in flattenChildren?
It doesn't resolve conflicts for you in map etc. and other helpers but you may not want to. |
@sebmarkbage Isn't this best done in Doing it in |
We could potentially also ignore everything after the first child with a given key, so
renders |
@spicyj I've thought about that, and it seems like the cleanest approach to me (right now at least), it's almost like a feature (keys are unique) as opposed to the "emergency survival" of trying to patch it up. I'm assuming it would be even cheaper as we would just not call |
Keys often use dynamic data that are out of the control of the person creating the React component so key-collisions might happen in user code. While it is still important to avoid that from happening, we can still keep rendering with limited user impact (only impacts perf). This change makes it so that key collisions are logged the same as missing key warnings and it assigns fallback IDs for collisions.
Not sure if this is the best solution and whether it might break something - everything seemed to work fine from my testing.
Fixes #566.
cc @spicyj