React: Questions regarding "props.children"

Created on 26 May 2018  ·  6Comments  ·  Source: facebook/react

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

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

nor

javascript // 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.

Question

Most helpful comment

_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!

All 6 comments

_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!

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/reactjs.org#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....

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.

@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.

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 Thanks for your answer

Was this page helpful?
0 / 5 - 0 ratings