Skip to content
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

Questions regarding "props.children" #12913

Closed
mcjazzyfunky opened this issue May 26, 2018 · 6 comments
Closed

Questions regarding "props.children" #12913

mcjazzyfunky opened this issue May 26, 2018 · 6 comments

Comments

@mcjazzyfunky
Copy link

mcjazzyfunky commented May 26, 2018

Do you want to request a feature or report a bug?
Just questions

Some questions regarding props.children

In the official React documentation of React.Children you can read that this.props.children is an "opaque data structure".
What does that mean EXACTLY?

I think there are in general three possibilities for the transparency of the data structure of props.children:

Case 1: EVERY aspect of the props.childrendata structure is open and well defined.

If this was right then the term "opaque data structure" would be completely wrong.
Therefore "Case 1" obviously is not the case.

Case 2: NO aspect of the props.children data structure is open or clear.

That would mean that whenever you use props.children you ALWAYS HAVE to use it in combination with React.Children as React.Children is the only one (mmmh, is it really the only one?) who knows about the actual data structure of props.children.

But that would imply that it should neither be allowed to use

// This is used almost everywhere (even in the official React documentation)
<div>{this.props.children}</div>

nor

 // This is often seen with the "Function as child" pattern
 MyComponent.propTypes = {
   children: PropTypes.func.isRequired,
 };

As both examples are very common, it seems that "Case 2" is obviously also not the case.

Case 3: SOME aspects of the props.children data structure are open and well defined.

That would open the possibility that one or even both of the examples in "Case 2" are valid.
But then it would mean that there should be an exact specification what aspects of props.children is well and openly defined and which aspects are really opaque.
Maybe I've missed something in the React documentation, but I think it's not really exactly specified there, is it?

And last but not least a further question:

Why exactly isn't props.children in case there are some children (one ore more) just always an array (as it is done in "Preact" for example)? That would make things so much easier, wouldn't it?

Many thanks in advance for the clarifications.

@danburzo
Copy link

danburzo commented May 28, 2018

Opaque data structure refers to the fact that children can be a lot of things: Arrays, fragments, single elements, string literals, etc.

React.Children contains a few methods that allow you to work with the different kinds of children in an unified way, and is definitely not the only way of interacting with them.

Why exactly isn't props.children in case there are some children (one ore more) just always an array (as it is done in "Preact" for example)?

Other than the fact that it has a shorthand in JSX, children is a property like any other. In fact, you can specify it as:

<Component children={...}/>

When React needs to update the DOM, it would be much more expensive to tell whether two single-item arrays are the same than whether two primitive values (e.g. strings) are the same.

That being said, I do think the React.Children documentation might be expanded a bit, but that's an issue to log to reactjs/reactjs.org repository.

This article may clarify things a bit, it did for me!

@mcjazzyfunky
Copy link
Author

mcjazzyfunky commented May 28, 2018

Many thanks @danburzo for your answers and clarifications.
After your answer, it's clear that I've just completely misuderstood the word "opaque" there.
The React documentation just wanted to say that the data structure of props.children is a completely well-defined union type and that it varies in its concrete representation (as union types always do).

I've just found an older issue that addressed exactly the same topic - wonder what happend to those changes?!? (I have opened an issue at reactjs.org => reactjs/react.dev#914):

https://github.com/facebook/react/pull/6018/files/91223423410f107ff83a8a6ce3158c488247292f?short_path=ef51787#diff-ef51787305cfed62f6f564284762d4de

Nevertheless I would still like to ask the second question again:
Why is "props.children" NOT an array in case that there is exactly ONE child (it really seems a violation against the principle of least astonishment if you do not know the background => if the name is "children" or "items" or "tokens" you normally expect a collection)?
Is it really for performance reasons? I cannot really see that the calculation of "checkResult" below is that time consuming:

const
  child1 = ...,
  child2 = ...,
  child1IsArray = Array.isArray(child1),
  child2IsArray = Array.isArray(child2),
  
  checkResult = child1IsArray && child2IsArray
     && child1.length === 1 && child2.length === 1
     && child1[0] === child2[0]; 

Sorry if asking again seems a bit annoying - but I would really love to know the exact reason for that design decision....

@danburzo
Copy link

I'm glad I could help you with the first part of your question! Unfortunately, as to why single children are not represented as a single-item array, I don't have an exact answer. However, I was trying to point out that you can still validly pass a single child as a normal prop (rather than the JSX shorthand), a fact that I accidentally obscured with my hypothesis that it's related to performance.

@mcjazzyfunky
Copy link
Author

mcjazzyfunky commented May 28, 2018

@danburzo Thanks again for your answer.

Mmh, frankly I hoped that maybe someone from the React team would remember the actual reason for that design decision...
Because there are really some subtle issues with the children prop handling in React.
For example, ask a React newbie whether the components Test1 and Test2 in the following example behave the same (you - as a React pro - know that the answer is "no" =>Test2 will warn on DEV about the missing keys while Test1 will not - but is this really what you would expect if you were a newbie?!?)

const
  h = React.createElement,
  Test1 = () => h('div', null, h('br'), h('br')),
  Test2 = () => h('div', { children: [h('br'), h('br')] });
 
ReactDOM.render(
  h('div', null, h(Test1), h(Test2)),
  document.getElementById('container')
);

By the way: If Test2 would be allowed, this would have some subtle performance benefits if you are implementing some createElement replacements - like some hyperscript function for example.

Another example: Ask a React newbie what "children: PropTypes...." would look like if the requirement is that all children should satisfy a certain condition. I think that would not be easy to answer (at least for a newbie).

To the React team: If the reasons for the design decision in question are not really known or maybe were just a matter of taste, then of course, feel free to close this issue.

@aweary
Copy link
Contributor

aweary commented Jun 24, 2018

I assume the decision to not use an array for a single child was made to avoid unnecessary array allocations.

Because there are really some subtle issues with the children prop handling in React.

We generally don't recommend using an explicit children prop. Most users also use JSX instead of directly calling React.createElement, so the issues you mentioned aren't typically a problem.

@aweary aweary closed this as completed Jun 24, 2018
@mcjazzyfunky
Copy link
Author

@aweary Thanks for your answer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants