Allow complex objects as children of <option> only if value="..." is provided #21431
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This one has bothered me for a while and as part of rewriting SSR now seems like a good time.
For legacy reasons, we previously used React.Children to flatten the children of
<option>
. The reason for this is becase we need to know what the value will be so that we can see if it matches what the selected value passed to<select>
was to know if this is the selected one. Also, option only accepted text nodes as children anyway (now this is not strictly true because template tags are allowed).However, at some point we stopped relying on flattening on the client because we just rely on the value that the browser provides after flattening.
React.Children is not really ever best practice because it doesn't allow for certain abstractions and so it lags in capabilities. Ideally it would never be used and it can be dead-code eliminated, but that would never be the case if React itself has a dependency on it. So the goal here is to remove that dependency and to simplify things by just using the regular children path.
However, as I noted in #21373, it's fine that we don't know what the implicit value is if we know the explicit value. In fact, almost all usages of
<option>
provides an explicitvalue="..."
attribute anyway. If you do that, then why shouldn't you be able to use all the advanced abstraction features in the child position? Such as function components that return strings but also allowing suspending in those as well as the child position of options in Flight.This PR enables you to pass any children to
<option>
.We still rely on flattening on the server if no value is provided. Therefore I always warn if a "value" is not provided and complex children are provided. I also added warning if dangerouslySetInnerHTML is used without a "value" attribute.
Additionally, it's invalid to pass a
<div>
as a child of<option>
because it won't parse as HTML. That automatically gets covered by the DOM nesting warning in this approach. This new approach adds comment nodes between text nodes. That seems to work fine in Chrome but not sure if there's a browser that has issues with those.Additionally, it might not be valid to pass
<fbt>
as a child of option because we might have previously relied on it being toString:ed which triggers the string mode. Now it's being rendered as an element which might let it be rendered as a span. That might lead to DOM nesting warnings. We need to find a way to fix that in fbt because this is not the place to fix that. Additionally for the purpose of comparing the value of the option, fbt shouldn't be used. It needs to pass a separate value prop.