React props.children
React 的特性,或者说 JS 的特性都太抽象勒。
无法理解
在看 React 文档的时候,以我当时的水平,看到了这个令人费解的东西:
function AlertButton({ message, children }) {
return (
<button onClick={() => alert(message)}>
{children}
</button>
);
}
export default function Toolbar() {
return (
<div>
<AlertButton message="正在播放!">
播放电影
</AlertButton>
<AlertButton message="正在上传!">
上传图片
</AlertButton>
</div>
);
}
按我当时的理解,Button 组件中将接收的 props 中的 children
属性作为按钮显示的文本,但这里的 AlertButton
组件没有传递 children
属性,因此按钮应该不显示内容才对。
但实际情况是,按钮不仅正常显示了:
而且当修改 AlertButton
组件的内容,即“播放电影”和“上传图片”时,页面按钮的文本也会改变:
export default function Toolbar() {
return (
<div>
<AlertButton message="正在播放!">
播放电影:すずめの戸締まり
</AlertButton>
<AlertButton message="正在上传!">
上传图片
</AlertButton>
</div>
);
}
页面内容正常变化了:
这就给我整晕了,说明 AlertButton
组件的文本被当成 {children}
了,无法理解。
逐渐理解
拷问了 GPT 和百度了一下后,发现原因了:
props.children
是一个特殊的参数,它表示组件所有的子节点。 它是一个特殊的参数,是隐式传递的。在组件内部通过 props.children
就可以获得组件的子节点,有以下几种情况:
- 组件没有子节点,
props.children
类型为 undefined; - 组件有一个子节点,
props.children
类型为 object; - 组件有多个子节点,
props.children
类型为 array。
但是使用 props.children
参数,要求组件标签必须有开始和结束标签,以标识子节点,之前都没有注意到:
function Toolbar() {
return (
<div>
// 有开始和结束标签
<AlertButton message="正在播放!">
播放电影:すずめの戸締まり
</AlertButton>
<AlertButton message="正在上传!">
上传图片
</AlertButton>
</div>
);
}
在之前(一般情况)都是直接使用自闭合标签传递 props 的,如:
function Toolbar() {
return (
<div>
// 自闭合,列出 props
<AlertButton message="正在播放!" children="播放电影:すずめの戸締まり" />
<AlertButton message="正在上传!">
上传图片
</AlertButton>
</div>
);
}
代码改成这样和之前效果是一样的,但这里就出现了个新问题,直接在 props 中传递 children
,但又有子节点,则何如?
function Toolbar() {
return (
<div>
<AlertButton message="正在播放!" children="播放电影:すずめの戸締まり" />
// 既有子节点,又传递了 children props
<AlertButton message="正在上传!" children="上传图片:すずめの戸締まり" >
上传图片
</AlertButton>
</div>
);
}
如此执行了一下,第二个按钮显示的文本还是“上传图片”,看来有子节点的话,children
还是会取子节点;当把子节点删掉时,children
取的就是 props 里传递的值了。
理解一切
除了传递参数以外,children
还有一些其他用法和辅助方法。
插槽
children
可以当成 Vue 里的插槽使用,如
function Child(props) {
return (
<div>
<div>{props.children[2]}</div>
<div>{props.children[1]}</div>
<div>{props.children[0]}</div>
</div>
)
}
function Parent() {
return (
<Child>
<div>这是0,应该排第一</div>
<div>这是1,应该排第二</div>
<div>这是2,应该排第三</div>
</Child>
)
}
Parent
控件中传入子节点的顺序是 0、1、2,但 Child
控件中使用 children
获取到的是一个数组,所以可以按任意顺序读取,实际效果:
这就相当于作为插槽使用了。
React.Children
React 还提供了一些关于 children
的方法,这里简单列一下把:
-
React.Children.map
object React.Children.map(object children, function fn)
此方法可以遍历
children
里的所有子节点,并在每个子节点中调用 fn 函数,返回一个新的children
对象; -
React.Children.forEach
React.Children.forEach(object children, function fn)
也是遍历
children
里的所有子节点,并在每个子节点中调用 fn 函数,但不返回对象; -
React.Children.count
number React.Children.count(object children)
统计
children
中的子节点个数,返回个数; -
React.Children.Only
object React.Children.only(object children)
检查
children
中是否只有一个子节点,返回这个节点,如果有多个子节点则抛出异常。