鉴于组件接收的功能是子代的(作为子代的回调模式,也称为 render prop 模式):
<Foo>{() => <Bar/>}</Foo>
React.Children.count(props.children) === 0
in Foo
。
文档似乎没有提到React.Children
仅接受有效的React元素,因此忽略子功能这一事实看起来很奇怪。
React.Children
如何对待没有孩子的孩子,为什么?
欢迎引用官方资源和/或源代码。
答案 0 :(得分:13)
正如其他人所述,documentation指出React.Children.count(children)
仅返回有效React
Components
的子代数的计数。
React.Children
不会忽略其他类型的子级,并且,如果需要获取计数,则只需要确定根子级Object中数组的长度即可,就像在普通js中一样。如果查看react-motion,您会发现他们指定children
的类型必须为func
:
Mouse.propTypes = {
children: PropTypes.func.isRequired
};
他们进一步确保只有一个孩子的React.Children.only
(docs):
render(): ReactElement {
const renderedChildren = this.props.children(this.state.currentStyle);
return renderedChildren && React.Children.only(renderedChildren);
}
React不能自己处理不同类型的孩子,相反,您必须自己处理它们。我整理了code sandbox,向您展示原因。
答案 1 :(得分:4)
根据docs:
React.Children.count返回子级中组件的总数,等于 回调传递给map或forEach的次数为 调用。
上面的语句的意思是,如果传递给组件的children元素是可迭代的,则只会增加计数。
现在让我们看一下React内部用于计算数量的代码段。
React.children.count
使用following code
主要代码是
function traverseAllChildren(children, callback, traverseContext) {
if (children == null) {
return 0;
}
return traverseAllChildrenImpl(children, '', callback, traverseContext);
}
因此,如果children
是null
,它将返回0。
function traverseAllChildrenImpl( 孩子们 名字SoFar, 打回来, 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;
}
因此,根据上面的代码,我们推断孩子为String
,Number
将返回1 ;
进入下一部分
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,
);
}
}
这意味着对于子数组中的每个元素,计数都会增加。
const iteratorFn = getIteratorFn(children);
if (typeof iteratorFn === 'function') {
对于作为可迭代子级提供的功能(例如Maps),Sequence会迭代元素并计算子树计数
对于对象,它将返回错误。
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,
);
中有上述信息的演示
答案 2 :(得分:0)
来自official doc:
React.Children
提供了用于处理this.props.children
不透明数据结构的实用程序。
换句话说,React.Children
名称空间充满了实用程序功能,当在composition model内将this.props.children
用于其主要预期目的时,这些功能很有用。 / p>
组成模型使用特殊的children
属性来处理可以直接呈现到父组件输出中的内容,因此实用程序功能如{{ 1}}设置为仅包含可以直接渲染的内容。
因为不能将函数直接呈现到父组件的输出中,所以它不包含在React.children.count
的结果中。
React.Children.count
道具是什么? children
道具是React用来传递其children
组件的特殊道具。
我敢肯定,JSX is just syntactic sugar for React.createElement
。
React.createElement
的呼叫签名如下:
children
因此,当组件通过JSX中的子代时,如下所示:
React.createElement(type, props, ...children)
...它产生以下代码:
<Foo>
<div>child1</div>
<div>child2</div>
</Foo>
......将子代作为尾随参数传递,并在组件中作为特殊的React.createElement(Foo, null,
React.createElement("div", null, "child1"),
React.createElement("div", null, "child2")
);
道具提供。
children
道具有什么用? children
道具是用于组成成分的特殊道具。
来自Composition vs Inheritance doc:
React具有强大的组合模型,我们建议使用组合而不是继承来在组件之间重用代码。
...以及更高版本:
我们建议此类组件使用特殊的
children
属性将子元素直接传递到其输出中
因此,合成模型使用特殊的children
属性来允许可重用的组件:
children
// Renders its children within a container div with class "foo":
const Foo = (props) => (<div className="foo">{props.children}</div>);
道具可以传递什么?好吧...任何事情。对于children
道具中可以传递的内容没有实际限制。
...但是,只有使用children
的合成模型可以将某些东西传递给组件,并期望将子代“直接呈现到其输出中”。
所以这是有效的:
children
...这是有效的:
<Foo>some text</Foo>
...甚至是有效的:
<Foo><div>something</div></Foo>
...但是这无效有效:
<Foo>
<Foo>
some text
</Foo>
</Foo>
...由于<Foo>{() => 'some text'}</Foo>
无法将Foo
直接渲染到其输出中。
正如您所指出的,这是Render Props模式的结果。
在描述“渲染道具”模式的文档中,它以passing a render function as a render
prop开头。
然后指出,使用名为function
的道具是完全任意的,而the prop name could be anything ...
...最后指出:
我们可以轻松使用
render
道具!
...并显示了如何将渲染函数作为children
道具传入。
但是它包含以下警告:
由于这种技术有点不寻常,因此在设计这样的API时,您可能需要明确声明
children
应该是children
中的一个函数。
换句话说,这是特殊propTypes
道具的非标准用法。
children
的形式传递任意内容正如我之前提到的,任何都可以作为children
传递。
更进一步,这是一个期望children
包含三个功能Object
,header
和body
的组件:
footer
此异常组件将像这样使用:
const ExpectsObject = (props) => (
<div>
{props.children.header()}
{props.children.body()}
{props.children.footer()}
</div>
);
...这很好用。
但是同样,这是对特殊<ExpectsObject>
{{
header: () => (<div>header</div>),
body: () => (<div>body</div>),
footer: () => (<div>footer</div>)
}}
</ExpectsObject>
属性的非常非标准使用,并且需要仔细的文档记录,并且由于此方法不遵循组成模型< / strong> children
名称空间中的实用程序功能不知道如何处理React.Children
中的自定义对象。
实际上,使用此特定对象作为children
调用React.Children.count
会导致实用程序函数抛出错误。
children
不算函数?那么React.Children.count
返回的计数中为什么不包含一个函数?
React.Children.count
被设计为组成模型的实用方法,并且是特殊React.Children.count
属性的关联使用。
由于无法按照合成模型直接将函数呈现到组件的输出中,因此该函数不包含在children
返回的count
中。