-
Notifications
You must be signed in to change notification settings - Fork 47k
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
Select element update #7054
Select element update #7054
Conversation
Leave a comment |
@@ -195,6 +217,9 @@ var ReactDOMSelect = { | |||
); | |||
didWarnValueDefaultValue = true; | |||
} | |||
|
|||
warnIfDuplicateValues(inst); |
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.
This should be placed within the _DEV_
block at the top of the function so it only runs in development.
@@ -58,6 +58,28 @@ function warnIfValueIsNull(props) { | |||
} | |||
} | |||
|
|||
function warnIfDuplicateValues(inst) { | |||
const options = inst._currentElement.props.children; |
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.
I think it is legal for this to be a composite component, so the children might not be simple option
tags.
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.
They can also be nested inside arrays/maps/sets etc, so this does not work reliably.
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.
Also not to forget that <optgroup>
is a thing.
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.
Also, children
can be null
or an element (not wrapped in an array). I'm not sure whether it is supported or not yet, but IIRC the idea is also to support anything that is iterable as valid value for children, so we can't assume this to be an array in that case?
@git-richard updated the pull request. |
if (options[i].props.value === options[j].props.value) { | ||
warning( | ||
false, | ||
`Select element contains duplicate option value ${options[i].props.value} in options #${i} & #${j}` |
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.
We try to include a stack trace for new warnings. Again, #7040 is a good example.
I updated the code based on previous feedback. Moved to the dev tools. It only processes controlled |
} | ||
|
||
// Check the array for duplicate values. | ||
for (var i = 0; i < values.length-1; i++) { |
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.
This strategy for duplicate checking is a bit slow: O(n²)
We can check for duplicates in the first loop (line 52) by making values
a dictionary and checking if the current value has already been seen:
const values = {};
for (const option of options) {
... // code for handling optgroups
if (option.type === 'option'
&& option.props != null
&& option.props.value) {
const value = option.props.value;
if (!values[value]) {
values[value] = value;
} else {
// warn about duplicate
return;
}
}
}
However, in reality, the chances of a <select>
having enough <options>
s to make this matter is small, but doing it this way does save us some lines of code.
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.
Thanks for the feedback. I updated it to use the dictionary but found an issue so I reverted back to the arrays. The dictionary approach shows a warning for a select
like this:
<select>
<option value={{val: 'a'}}>a</option>
<option value={{val: 'b'}}>b</option>
</select>
Those are different values and shouldn't show a warning. I agree that a select
shouldn't normally have enough options for the array comparisons to be a performance issue.
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.
@git-richard The value is stringified (inspect the DOM) so in your case they are actually the same.
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're right. I didn't notice that. Do you think using a dictionary of the options, as mentioned above, is a correct approach? Thanks,
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.
If we can avoid O(n^2)
definitely seems favorable I'd say. EDIT: For most it probably doesn't matter, but consider it's just a warning, ensuring it never balloons seems like a good idea.
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.
I changed it back to a dictionary. Do you see any other issues with it?
if (option.type === 'option') { | ||
if ((option.props != null) && (option.props.value)) { | ||
const value = option.props.value; | ||
if (!values[value]) { |
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.
Needs to use hasOwnProperty
. I'm not sure if it really matters, but it may make sense to explicitly stringify value as well?
LGTM |
} | ||
|
||
if (option.type === 'optGroup') { | ||
if ((option.props != null) && (option.props.children != null)) { |
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.
it would be great if we can merge this two ifs.
It is this still relevant? |
@montogeek nope, these files don't exist anymore. Thanks for the heads up! |
What was the relevant issue? |
From the original message: "...displays a warning if there are duplicate option values." Then he introduced an array comparison algorithm |
The issue this was meant to address is #6959. @git-richard I'm sorry we didn't provide any feedback on this. Sometimes low priority issues slip through the cracks. It's been over a year since this PR was opened, and the codebase has seen substantial changes. If you want to keep working on this, I would suggest looking at Feel free to open a new PR for this if you do choose to pursue it, and also feel free to ping us with questions. Thanks! |
I saw some others might be working on this issue but I'm submitting a PR to get some practice. Don't mean to step on any toes.
The code loops through the options in a select element and displays a warning if there are duplicate option values. There are more efficient array comparison algorithms but this works well. It shows all duplicate values rather than exiting the evaluation loops on the first dupe it finds.
Appreciate any feedback.