[英]React.Children with non-element children
Given a component receives a function as a child ( callback as a child pattern, also known as render prop pattern): 给定一个组件接收一个函数作为子元素( 回调为子模式,也称为渲染道具模式):
<Foo>{() => <Bar/>}</Foo>
React.Children.count(props.children) === 0
in Foo
. React.Children.count(props.children) === 0
中Foo
。
The documentation doesn't seem to mention that React.Children
accepts only valid React elements, so the fact that child function is ignored looks odd. 文档似乎没有提到
React.Children
只接受有效的React元素,因此忽略React.Children
这一事实看起来很奇怪。
How does React.Children
treat non-element children and why? React.Children
如何对待非元素儿童?为什么?
References to official sources and/or source code are welcome. 欢迎参考官方来源和/或源代码。
As others have stated, the documentation states that React.Children.count(children)
only returns the count of the number of children that are valid React
Components
. 正如其他人所说, 文档指出
React.Children.count(children)
只返回有效React
Components
的子节点数。
React.Children
does not ignore other types of children, and if you need to get the count, you only need to determine the length of the array in the root child Object, just like you would in vanilla js. React.Children
不会忽略其他类型的子React.Children
,如果需要获取计数,您只需要确定根子对象中数组的长度,就像在vanilla js中一样。 If you look at react-motion , you'll see that they specify that children
must be type of func
: 如果你看一下react-motion ,你会看到他们指定
children
必须是func
类型:
Mouse.propTypes = {
children: PropTypes.func.isRequired
};
And they further ensure that there's only one child with React.Children.only
( docs ): 并且他们进一步确保只有一个孩子使用
React.Children.only
( docs ):
render(): ReactElement {
const renderedChildren = this.props.children(this.state.currentStyle);
return renderedChildren && React.Children.only(renderedChildren);
}
React does not handle different types of children on its own, instead, you have to handle them yourself. React不会自己处理不同类型的孩子,而是你必须自己处理它们。 I put together a code sandbox to show you why.
我整理了一个代码沙箱来向您展示原因。
Disclaimer: It's not a solution but just an eye where we could look at. 免责声明:这不是一个解决方案,而只是我们可以看到的眼睛。
I'm not sure but if it is indeed needed to be fixed in React itself, then I would suggest to change in the following function: 我不确定,但如果确实需要在React本身修复,那么我建议改变以下函数:
export function isValidElement(object) {
return (
typeof object === 'object' &&
object !== null &&
object.$$typeof === REACT_ELEMENT_TYPE
);
}
This code: 这段代码:
typeof object === 'object'
To something like: 对于这样的事情:
typeof Object.create(object) === 'object'
And also add a Symbol for such something like: 并为以下内容添加符号:
Symbol.for('react.function')
in the React Symbol . 在React符号中 。
this.props.children.length
This lets you count the function as child component
as a children. 这使您可以将
function as child component
计function as child component
作为function as child component
。 this.props.children
includes any type of element, expressions, or component whilst this.props.children
inside React.Children
function as child is being ignored as children. this.props.children
包含任何类型的元素,表达式或组件,而作为子this.props.children
React.Children
函数中的React.Children
被忽略为子React.Children
。 Continue reading bellow to understand it better... 继续阅读以下内容,以便更好地了解它......
However, I have just submitted an issue and if you wish you can keep following there . 但是,我刚刚提交了一个问题,如果您希望可以继续关注那里 。
The docs specifies that the React.Children.count
only counts the component in children. 文档指定
React.Children.count
仅计算子项中的组件。
You probably already have known what exactly is children in react . 你可能已经知道究竟是什么孩子在做出反应 。
React takes everything as children except the function as child. 除了作为孩子的功能外,React将所有内容都视为子项。
Here's the reference where it states: 以下是它所述的参考:
React components don't support functions as children.
React组件不支持子功能。
If you wish you can look deeper here . 如果你希望你能在这里看得更深。
So, you have function as a child in <Foo />
component so it does return 0 as it's not being considered as children. 因此,您在
<Foo />
组件中具有作为子项的功能,因此它确实返回0,因为它不被视为子项。
You can optionally count those expressions as its children then you may convert them to array first and then count like: 您可以选择将这些表达式计为其子项,然后您可以先将它们转换为数组,然后计算如下:
class CountExpression extends React.Component {
render() {
const children = React.Children.toArray(this.props.children)
return <p>{React.Children.count(children)}</p>
}
}
{ /* Counts 2 */ }
<CountExpression>
{'one'}
{'two'}
{ () => <p>Still, this will be ignored as child and is not included in array</p>}
</CountExpression>
Here's a draft demo if you want to have a look. 如果你想看一下,这是一个草案演示 。
Look at the following example how children is being counted: 请看以下示例如何计算子项:
class CountChildren extends React.Component {
render() {
return <p>{React.Children.count(this.props.children)}</p>
}
}
{ /* Renders 1 */ }
<CountChildren>
Simply a text!
</CountChildren>
{ /* Renders 2 */ }
<CountChildren>
<p>Html element</p>
<ChildComponent />
</CountChildren>
{ /* Renders 3 */ }
<CountChildren>
Simply a text!
<p>Html element</p>
<ChildComponent />
</CountChildren>
{ /* Renders 3 */ }
<CountChildren>
Simply a text!
<p>Html element</p>
<ChildComponent />
{ /* ignores it as it's not a component */ }
{ () => <div>Function as a child component</div> }
</CountChildren>
So, you can notice that React can accept any type of children regardless of array, a function, or an object, etc. 因此,您可以注意到React可以接受任何类型的子节点,而不管数组,函数或对象等。
If you wish you can also ignore rendering the children checking it with a condition. 如果您愿意,您也可以忽略渲染孩子用条件检查它。 For eg.:
例如:
class SingleChildComponent extends React.Component {
render() {
return (
<div>
{
Array.isArray(this.props.children) ?
'Sorry, you can only pass single child!' :
this.props.children()
}
</div>
)
}
}
{ /* Renders 'Sorry, you can only pass single child!' */ }
<SingleChildComponent>
<p>First children</p>
<SecondChildren />
</SingleChildComponent>
{ /* Renders `<p>Single child</p>` */ }
<SingleChildComponent>
<p>Single child</p>
</SingleChildComponent>
If you wish, you can convert the children to array and sort it out like below: 如果您愿意,可以将子项转换为数组并将其排序如下:
class SortComponent extends React.Component {
render() {
const children = React.Children.toArray(this.props.children)
return <>{ children.sort().join(', ') }</>
}
}
{ /* Renders 'Computer, Furniture, Machine' */ }
<SortComponent>
{'Machine'} { /* First child */ }
{'Computer'} { /* Second child */ }
{'Furniture'} { /* Third child */ }
</SortComponent>
Enforcing a single child: 强迫单个孩子:
Bad: 坏:
class OnlyChildComponent extends React.Component {
render() {
return this.props.children()
}
}
{ /* Enforcing it as a single child component */ }
OnlyChildComponent.propTypes = {
children: React.PropTypes.func.isRequired
}
If there are more children, then it just shows warning in the console and let the program execute next. 如果有更多的孩子,那么它只是在控制台中显示警告并让程序接下来执行。
Good: 好:
class OnlyChildComponent extends React.Component {
render() {
return React.Children.only(this.props.children)()
}
}
If there are more than one child, then it will throw an error! 如果有多个孩子,那么就会抛出错误! And it halts the program execution.
它会暂停程序执行。 It's perfect to avoid mess with our component.
这是完美的避免混乱我们的组件。
According to the docs : 根据文件 :
React.Children.count returns the total number of components in children, equal to the number of times that a callback passed to map or forEach would be invoked.
React.Children.count返回子节点中的组件总数,等于调用传递给map或forEach的回调的次数。
What the above statement means is that if the children element passed to a component is iteratable then only the count will be incremented. 上述语句的含义是,如果传递给组件的子元素是可迭代的,那么只有计数才会递增。
Now lets look at the code snippet that React internally used to calculate count. 现在让我们看一下React内部用来计算count的代码片段。
React.children.count
uses the following code React.children.count
使用以下代码
The major piece of code is 主要代码是
function traverseAllChildren(children, callback, traverseContext) {
if (children == null) {
return 0;
}
return traverseAllChildrenImpl(children, '', callback, traverseContext);
}
So if children
is null
, it returns 0. 因此,如果
children
为null
,则返回0。
function traverseAllChildrenImpl( children, nameSoFar, callback, traverseContext, ) { ... function traverseAllChildrenImpl(children,nameSoFar,callback,traverseContext,){...
switch (type) {
case 'string':
case 'number':
invokeCallback = true;
break;
case 'object':
switch (children.$$typeof) {
case REACT_ELEMENT_TYPE:
case REACT_PORTAL_TYPE:
invokeCallback = true;
}
}
}
if (invokeCallback) {
// Other code
return 1;
}
So from the above code we infer that we children is String
, Number
it will return 1 ; 所以从上面的代码我们可以推断,我们的孩子是
String
, Number
将返回1;
Moving on to the next part 继续下一部分
if (Array.isArray(children)) {
for (let i = 0; i < children.length; i++) {
child = children[i];
nextName = nextNamePrefix + getComponentKey(child, i);
subtreeCount += traverseAllChildrenImpl(
child,
nextName,
callback,
traverseContext,
);
}
}
It implies that for each element within the children array, the count is incremented. 这意味着对于children数组中的每个元素,计数都会递增。
const iteratorFn = getIteratorFn(children);
if (typeof iteratorFn === 'function') {
For functions provided as children which are iteratable such as Maps, Sequence it will iterate though the elements and calculate the subtree count 对于作为可迭代的子项提供的函数,例如Maps,Sequence,它将迭代元素并计算子树计数
For an object it will return an error. 对于对象,它将返回错误。
invariant(
false,
'Objects are not valid as a React child (found: %s).%s',
childrenString === '[object Object]'
? 'object with keys {' + Object.keys(children).join(', ') + '}'
: childrenString,
addendum,
);
A demo for the above piece of information is in the codesandbox here 上面的信息的演示在这里的代码框中
From the official doc : 来自官方文件 :
React.Children
provides utilities for dealing with thethis.props.children
opaque data structure.React.Children
提供了处理this.props.children
不透明数据结构的实用程序。
In other words, the React.Children
namespace is full of utility functions that are helpful when using this.props.children
for its primary intended purpose within the composition model . 换句话说,
React.Children
命名空间中充满了实用函数,当将this.props.children
用作组合模型中的 主要用途时,这些函数this.props.children
用。
The composition model uses the special children
property for things that can be rendered directly into the output of a parent component , so utility functions like React.children.count
are programmed to only include things that can be directly rendered . 组合模型使用特殊的
children
属性来处理可以直接呈现到父组件的输出中的东西,因此像React.children.count
这样的实用程序函数被编程为仅包含可以直接呈现的内容 。
Because a function cannot be directly rendered into the output of a parent component, it is not included in the results from React.Children.count
. 因为函数不能直接呈现到父组件的输出中,所以它不包含在
React.Children.count
的结果中。
children
prop? children
道具? The children
prop is a special prop that React uses to pass a component its children
. children
道具是React用来传递children
成分的特殊道具。
As I'm sure you know, JSX is just syntactic sugar for React.createElement
. 我相信你知道, JSX只是
React.createElement
语法糖 。
The call signature for React.createElement
looks like this: React.createElement
的调用签名如下所示:
React.createElement(type, props, ...children)
So when a component is passed children in JSX like this: 所以当一个组件在JSX中传递子代像这样:
<Foo>
<div>child1</div>
<div>child2</div>
</Foo>
...it results in this code: ...导致此代码:
React.createElement(Foo, null,
React.createElement("div", null, "child1"),
React.createElement("div", null, "child2")
);
...where the children are passed as the trailing arguments and are available in the component as the special children
prop. ...将子项作为尾随参数传递,并在组件中作为特殊
children
。
children
prop for? children
道具是什么? The children
prop is a special prop used for component composition . children
道具是用于组成部分的特殊道具。
From the Composition vs Inheritance doc : 从Composition vs Inheritance doc :
React has a powerful composition model, and we recommend using composition instead of inheritance to reuse code between components.
React有一个强大的组合模型,我们建议使用组合而不是继承来重用组件之间的代码。
...and later: ...然后:
We recommend that such components use the special
children
prop to pass children elements directly into their output我们建议这些组件使用特殊的
children
prop将子元素直接传递给它们的输出
So the composition model uses the special children
prop to allow for reusable components: 因此组合模型使用特殊的
children
prop来允许可重用的组件:
// Renders its children within a container div with class "foo":
const Foo = (props) => (<div className="foo">{props.children}</div>);
children
prop? children
道具? Well...anything. 嗯...什么。 There are no actual restrictions on what can be passed in the
children
prop... children
道具中可以传递的内容没有实际限制......
...but only certain things can be passed to a component using children
for the composition model and expecting to render the child "directly into their output". ...但是只有某些东西可以传递给使用
children
组件组件模型的组件,并期望将子项“直接呈现给他们的输出”。
So this is valid: 所以这是有效的:
<Foo>some text</Foo>
...and this is valid: ......这是有效的:
<Foo><div>something</div></Foo>
...and even this is valid: ......甚至这是有效的:
<Foo>
<Foo>
some text
</Foo>
</Foo>
...but this is not valid: ......但这不是有效的:
<Foo>{() => 'some text'}</Foo>
...since Foo
cannot render a function
directly into its output. ...因为
Foo
无法将function
直接渲染到其输出中。
As you pointed out, this is the result of the Render Props pattern. 正如您所指出的,这是Render Props模式的结果。
In the doc describing the Render Props pattern it starts with passing a render function as a render
prop . 在描述Render Props模式的doc中,它首先将渲染函数作为
render
道具传递 。
Then it points out that using a prop named render
is completely arbitrary and the prop name could be anything ... 然后它指出使用名为
render
的道具完全是任意的, 道具名称可以是任何东西 ......
...and it ends by pointing out that: ......最后指出:
we could just as easily use the
children
prop!我们可以轻松地使用
children
道具!
...and showing how a render function could be passed in as the children
prop. ...并展示如何渲染功能,可以通过在为
children
撑起。
But then it includes this warning: 但是它包含了这个警告:
Since this technique is a little unusual, you'll probably want to explicitly state that
children
should be a function in yourpropTypes
when designing an API like this.由于这种技术有点不寻常,
propTypes
在设计像这样的API时,您可能希望明确说明children
应该是propTypes
的函数。
In other words, this is a non-standard use of the special children
prop. 换句话说,这是特殊
children
道具的非标准使用。
children
children
传递任意的东西 As I noted earlier, anything can be passed as children
. 正如我前面提到的, 任何东西都可以作为
children
传递。
So taking it a step further, here is a component that expects an Object
containing three functions, header
, body
, and footer
: 所以更进一步,这里是一个组件,它需要一个包含三个函数的
Object
, header
, body
和footer
:
const ExpectsObject = (props) => (
<div>
{props.children.header()}
{props.children.body()}
{props.children.footer()}
</div>
);
This highly unusual component would be used like this: 这个非常不寻常的组件将使用如下:
<ExpectsObject>
{{
header: () => (<div>header</div>),
body: () => (<div>body</div>),
footer: () => (<div>footer</div>)
}}
</ExpectsObject>
...and this works just fine. ......这很好用。
But again, this is a very non-standard use of the special children
property and would require careful documentation, and since this approach does not follow the composition model the utility functions in the React.Children
namespace don't know how to handle the custom object in children
. 但同样,这是一个非常标准的特殊
children
属性使用,需要仔细的文档,并且由于这种方法不遵循组合模型 , React.Children
命名空间中的实用程序函数不知道如何处理自定义children
对象。
In fact, calling React.Children.count
with this particular object as children
causes the utility function to throw an error. 实际上,使用此特定对象作为
children
对象调用React.Children.count
会导致实用程序函数抛出错误。
React.Children.count
not count a function? React.Children.count
不计算函数? So why is a function not included in the count returned by React.Children.count
? 那么为什么函数不包含在
React.Children.count
返回的计数中呢?
React.Children.count
is designed to be a utility method for the composition model and associated use of the special children
property. React.Children.count
旨在作为组合模型的实用方法以及特殊children
属性的关联使用。
Because a function cannot be rendered directly into the output of a component following the composition model, it is not included in the count
returned by React.Children.count
. 由于函数无法直接呈现在组合模型之后的组件输出中,因此它不包含在
React.Children.count
返回的count
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.