Children
Children
允许你操作和转换作为 children
prop。 接收的 JSX。
¥Children
lets you manipulate and transform the JSX you received as the children
prop.
const mappedChildren = Children.map(children, child =>
<div className="Row">
{child}
</div>
);
参考
¥Reference
Children.count(children)
调用 Children.count(children)
来计算 children
数据结构中的子组件数量。
¥Call Children.count(children)
to count the number of children in the children
data structure.
import { Children } from 'react';
function RowList({ children }) {
return (
<>
<h1>Total rows: {Children.count(children)}</h1>
...
</>
);
}
参数
¥Parameters
-
children
:你的组件接收的children
prop 值。¥
children
: The value of thechildren
prop received by your component.
返回
¥Returns
这些 children
中的节点数。
¥The number of nodes inside these children
.
注意事项
¥Caveats
-
空节点(
null
、undefined
和布尔值)、字符串、数字和 React 元素 均计为独立节点。数组不算作单独的节点,但它们的子节点算。遍历深度不超过 React 元素:它们不会被渲染,并且它们的子元素不会被遍历。片段 不会被遍历。¥Empty nodes (
null
,undefined
, and Booleans), strings, numbers, and React elements count as individual nodes. Arrays don’t count as individual nodes, but their children do. The traversal does not go deeper than React elements: they don’t get rendered, and their children aren’t traversed. Fragments don’t get traversed.
Children.forEach(children, fn, thisArg?)
调用 Children.forEach(children, fn, thisArg?)
为 children
数据结构中的每个子组件运行一些代码。
¥Call Children.forEach(children, fn, thisArg?)
to run some code for each child in the children
data structure.
import { Children } from 'react';
function SeparatorList({ children }) {
const result = [];
Children.forEach(children, (child, index) => {
result.push(child);
result.push(<hr key={index} />);
});
// ...
参数
¥Parameters
-
children
:你的组件接收的children
prop 值。¥
children
: The value of thechildren
prop received by your component. -
fn
:你希望为每个子组件运行的函数,类似于 数组forEach
方法 回调。此方法将以子组件作为第一个参数,其索引作为第二个参数进行调用。索引从0
开始,每次调用时递增。¥
fn
: The function you want to run for each child, similar to the arrayforEach
method callback. It will be called with the child as the first argument and its index as the second argument. The index starts at0
and increments on each call. -
可选
thisArg
:调用fn
函数时应使用的this
值。如果省略,则为undefined
。¥optional
thisArg
: Thethis
value with which thefn
function should be called. If omitted, it’sundefined
.
返回
¥Returns
Children.forEach
返回 undefined
。
¥Children.forEach
returns undefined
.
注意事项
¥Caveats
-
空节点(
null
、undefined
和布尔值)、字符串、数字和 React 元素 均计为独立节点。数组不算作单独的节点,但它们的子节点算。遍历深度不超过 React 元素:它们不会被渲染,并且它们的子元素不会被遍历。片段 不会被遍历。¥Empty nodes (
null
,undefined
, and Booleans), strings, numbers, and React elements count as individual nodes. Arrays don’t count as individual nodes, but their children do. The traversal does not go deeper than React elements: they don’t get rendered, and their children aren’t traversed. Fragments don’t get traversed.
Children.map(children, fn, thisArg?)
调用 Children.map(children, fn, thisArg?)
映射或转换 children
数据结构中的每个子组件。
¥Call Children.map(children, fn, thisArg?)
to map or transform each child in the children
data structure.
import { Children } from 'react';
function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}
参数
¥Parameters
-
children
:你的组件接收的children
prop 值。¥
children
: The value of thechildren
prop received by your component. -
fn
:映射函数,类似于 数组map
方法 回调。此方法将以子组件作为第一个参数,其索引作为第二个参数进行调用。索引从0
开始,每次调用时递增。你需要从此函数返回一个 React 节点。这可能是一个空节点(null
、undefined
或布尔值)、字符串、数字、React 元素或其他 React 节点的数组。¥
fn
: The mapping function, similar to the arraymap
method callback. It will be called with the child as the first argument and its index as the second argument. The index starts at0
and increments on each call. You need to return a React node from this function. This may be an empty node (null
,undefined
, or a Boolean), a string, a number, a React element, or an array of other React nodes. -
可选
thisArg
:调用fn
函数时应使用的this
值。如果省略,则为undefined
。¥optional
thisArg
: Thethis
value with which thefn
function should be called. If omitted, it’sundefined
.
返回
¥Returns
如果 children
是 null
或 undefined
,则返回相同的值。
¥If children
is null
or undefined
, returns the same value.
否则,返回一个由 fn
函数返回的节点组成的扁平数组。返回的数组将包含除 null
和 undefined
之外的所有返回节点。
¥Otherwise, returns a flat array consisting of the nodes you’ve returned from the fn
function. The returned array will contain all nodes you returned except for null
and undefined
.
注意事项
¥Caveats
-
空节点(
null
、undefined
和布尔值)、字符串、数字和 React 元素 均计为独立节点。数组不算作单独的节点,但它们的子节点算。遍历深度不超过 React 元素:它们不会被渲染,并且它们的子元素不会被遍历。片段 不会被遍历。¥Empty nodes (
null
,undefined
, and Booleans), strings, numbers, and React elements count as individual nodes. Arrays don’t count as individual nodes, but their children do. The traversal does not go deeper than React elements: they don’t get rendered, and their children aren’t traversed. Fragments don’t get traversed. -
如果你从
fn
返回一个元素或一个包含键的元素数组,则返回元素的键将自动与children
中相应原始项的键合并。当你从数组中的fn
返回多个元素时,它们的键只需在本地彼此唯一即可。¥If you return an element or an array of elements with keys from
fn
, the returned elements’ keys will be automatically combined with the key of the corresponding original item fromchildren
. When you return multiple elements fromfn
in an array, their keys only need to be unique locally amongst each other.
Children.only(children)
调用 Children.only(children)
来断言 children
代表单个 React 元素。
¥Call Children.only(children)
to assert that children
represent a single React element.
function Box({ children }) {
const element = Children.only(children);
// ...
参数
¥Parameters
-
children
:你的组件接收的children
prop 值。¥
children
: The value of thechildren
prop received by your component.
返回
¥Returns
如果 children
是有效元素 返回该元素。
¥If children
is a valid element, returns that element.
否则,抛出错误。
¥Otherwise, throws an error.
注意事项
¥Caveats
-
如果你将数组(例如
Children.map
的返回值)作为children
传递,则此方法始终会抛出异常。换句话说,它强制children
是一个单独的 React 元素,而不是一个包含单个元素的数组。¥This method always throws if you pass an array (such as the return value of
Children.map
) aschildren
. In other words, it enforces thatchildren
is a single React element, not that it’s an array with a single element.
Children.toArray(children)
调用 Children.toArray(children)
创建一个基于 children
数据结构的数组。
¥Call Children.toArray(children)
to create an array out of the children
data structure.
import { Children } from 'react';
export default function ReversedList({ children }) {
const result = Children.toArray(children);
result.reverse();
// ...
参数
¥Parameters
-
children
:你的组件接收的children
prop 值。¥
children
: The value of thechildren
prop received by your component.
返回
¥Returns
返回 children
中的元素的扁平数组。
¥Returns a flat array of elements in children
.
注意事项
¥Caveats
-
返回的数组中将省略空节点(
null
、undefined
和布尔值)。返回元素的 ‘键将根据原始元素计算得出’ 键及其嵌套级别和位置。这确保展平数组不会引起行为变化。¥Empty nodes (
null
,undefined
, and Booleans) will be omitted in the returned array. The returned elements’ keys will be calculated from the original elements’ keys and their level of nesting and position. This ensures that flattening the array does not introduce changes in behavior.
用法
¥Usage
子组件变形
¥Transforming children
要转换组件 作为 children
属性接收, 调用 Children.map
的子 JSX:
¥To transform the children JSX that your component receives as the children
prop, call Children.map
:
import { Children } from 'react';
function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}
在上面的例子中,RowList
将其接收的每个子组件封装到 <div className="Row">
容器中。例如,假设父组件将三个 <p>
标签作为 children
属性传递给 RowList
:
¥In the example above, the RowList
wraps every child it receives into a <div className="Row">
container. For example, let’s say the parent component passes three <p>
tags as the children
prop to RowList
:
<RowList>
<p>This is the first item.</p>
<p>This is the second item.</p>
<p>This is the third item.</p>
</RowList>
然后,使用上面的 RowList
实现,最终渲染结果将如下所示:
¥Then, with the RowList
implementation above, the final rendered result will look like this:
<div className="RowList">
<div className="Row">
<p>This is the first item.</p>
</div>
<div className="Row">
<p>This is the second item.</p>
</div>
<div className="Row">
<p>This is the third item.</p>
</div>
</div>
Children.map
与 使用 map()
转换数组。 类似,区别在于 children
数据结构被视为不透明。这意味着即使它有时是一个数组,你也不应假设它是数组或任何其他特定数据类型。这就是为什么如果需要转换它,应该使用 Children.map
。
¥Children.map
is similar to to transforming arrays with map()
. The difference is that the children
data structure is considered opaque. This means that even if it’s sometimes an array, you should not assume it’s an array or any other particular data type. This is why you should use Children.map
if you need to transform it.
import { Children } from 'react'; export default function RowList({ children }) { return ( <div className="RowList"> {Children.map(children, child => <div className="Row"> {child} </div> )} </div> ); }
深入研究
¥Why is the children prop not always an array?
在 React 中,children
属性被视为不透明的数据结构。这意味着你不应该依赖它的结构。要转换、过滤或计数子元素,你应该使用 Children
方法。
¥In React, the children
prop is considered an opaque data structure. This means that you shouldn’t rely on how it is structured. To transform, filter, or count children, you should use the Children
methods.
实际上,children
数据结构通常在内部表示为数组。但是,如果只有一个子元素,React 将不会创建额外的数组,因为这会导致不必要的内存开销。只要你使用 Children
方法而不是直接检查 children
prop,即使 React 更改了数据结构的实际实现方式,你的代码也不会崩溃。
¥In practice, the children
data structure is often represented as an array internally. However, if there is only a single child, then React won’t create an extra array since this would lead to unnecessary memory overhead. As long as you use the Children
methods instead of directly introspecting the children
prop, your code will not break even if React changes how the data structure is actually implemented.
即使 children
是数组,Children.map
也具有有用的特殊行为。例如,Children.map
将返回元素上的 keys 与传递给它的 children
上的键组合在一起。这确保原始 JSX 子组件即使像上例一样被封装,也不会 “lose” 键。
¥Even when children
is an array, Children.map
has useful special behavior. For example, Children.map
combines the keys on the returned elements with the keys on the children
you’ve passed to it. This ensures the original JSX children don’t “lose” keys even if they get wrapped like in the example above.
为每个子组件运行部分代码
¥Running some code for each child
调用 Children.forEach
迭代 children
数据结构中的每个子组件。它不返回任何值,类似于 数组 forEach
方法。。你可以使用它来运行自定义逻辑,例如构建你自己的数组。
¥Call Children.forEach
to iterate over each child in the children
data structure. It does not return any value and is similar to the array forEach
method. You can use it to run custom logic like constructing your own array.
import { Children } from 'react'; export default function SeparatorList({ children }) { const result = []; Children.forEach(children, (child, index) => { result.push(child); result.push(<hr key={index} />); }); result.pop(); // Remove the last separator return result; }
计数子元素
¥Counting children
调用 Children.count(children)
来计算子组件的数量。
¥Call Children.count(children)
to calculate the number of children.
import { Children } from 'react'; export default function RowList({ children }) { return ( <div className="RowList"> <h1 className="RowListHeader"> Total rows: {Children.count(children)} </h1> {Children.map(children, child => <div className="Row"> {child} </div> )} </div> ); }
将子元素转换为数组
¥Converting children to an array
调用 Children.toArray(children)
将 children
数据结构转换为常规 JavaScript 数组。这允许你使用内置数组方法(例如 filter
、sort
或 reverse
。)来操作数组。
¥Call Children.toArray(children)
to turn the children
data structure into a regular JavaScript array. This lets you manipulate the array with built-in array methods like filter
, sort
, or reverse
.
import { Children } from 'react'; export default function ReversedList({ children }) { const result = Children.toArray(children); result.reverse(); return result; }
备选方案
¥Alternatives
公开多个组件
¥Exposing multiple components
使用 Children
方法操作子组件通常会导致代码脆弱。当你在 JSX 中将子组件传递给组件时,你通常不会期望组件操作或转换单个子组件。
¥Manipulating children with the Children
methods often leads to fragile code. When you pass children to a component in JSX, you don’t usually expect the component to manipulate or transform the individual children.
如果可以,请尽量避免使用 Children
方法。例如,如果你想要将 RowList
的每个子组件都封装在 <div className="Row">
中,可以导出一个 Row
组件,并手动将每一行封装到其中,如下所示:
¥When you can, try to avoid using the Children
methods. For example, if you want every child of RowList
to be wrapped in <div className="Row">
, export a Row
component, and manually wrap every row into it like this:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList> <Row> <p>This is the first item.</p> </Row> <Row> <p>This is the second item.</p> </Row> <Row> <p>This is the third item.</p> </Row> </RowList> ); }
与使用 Children.map
不同,此方法不会自动封装每个子组件。然而,与 之前使用 Children.map
的示例 相比,这种方法有一个显著的优势,因为即使你不断提取更多组件,它也能正常工作。例如,即使你提取自己的 MoreRows
组件,它仍然有效:
¥Unlike using Children.map
, this approach does not wrap every child automatically. However, this approach has a significant benefit compared to the earlier example with Children.map
because it works even if you keep extracting more components. For example, it still works if you extract your own MoreRows
component:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList> <Row> <p>This is the first item.</p> </Row> <MoreRows /> </RowList> ); } function MoreRows() { return ( <> <Row> <p>This is the second item.</p> </Row> <Row> <p>This is the third item.</p> </Row> </> ); }
这不适用于 Children.map
,因为它会将 “看到” 和 <MoreRows />
视为单个子元素(和单个行)。
¥This wouldn’t work with Children.map
because it would “see” <MoreRows />
as a single child (and a single row).
接受对象数组作为 prop
¥Accepting an array of objects as a prop
你还可以显式传递数组作为 prop。例如,此 RowList
接受 rows
数组作为 prop:
¥You can also explicitly pass an array as a prop. For example, this RowList
accepts a rows
array as a prop:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList rows={[ { id: 'first', content: <p>This is the first item.</p> }, { id: 'second', content: <p>This is the second item.</p> }, { id: 'third', content: <p>This is the third item.</p> } ]} /> ); }
由于 rows
是一个常规的 JavaScript 数组,因此 RowList
组件可以对其使用内置数组方法,例如 map
。
¥Since rows
is a regular JavaScript array, the RowList
component can use built-in array methods like map
on it.
当你希望能够将更多信息作为结构化数据与子元素一起传递时,此模式尤其有用。在下面的例子中,TabSwitcher
组件接收一个对象数组作为 tabs
属性:
¥This pattern is especially useful when you want to be able to pass more information as structured data together with children. In the below example, the TabSwitcher
component receives an array of objects as the tabs
prop:
import TabSwitcher from './TabSwitcher.js'; export default function App() { return ( <TabSwitcher tabs={[ { id: 'first', header: 'First', content: <p>This is the first item.</p> }, { id: 'second', header: 'Second', content: <p>This is the second item.</p> }, { id: 'third', header: 'Third', content: <p>This is the third item.</p> } ]} /> ); }
与将子组件作为 JSX 传递不同,此方法允许你将一些额外的数据(例如 header
)与每个组件关联。由于你直接使用 tabs
,并且它是一个数组,因此你不需要 Children
方法。
¥Unlike passing the children as JSX, this approach lets you associate some extra data like header
with each item. Because you are working with the tabs
directly, and it is an array, you do not need the Children
methods.
调用渲染属性自定义渲染
¥Calling a render prop to customize rendering
除了为每个项目生成 JSX 代码之外,你还可以传递一个返回 JSX 代码的函数,并在必要时调用该函数。在此示例中,App
组件将 renderContent
函数传递给 TabSwitcher
组件。TabSwitcher
组件仅为选定的选项卡调用 renderContent
:
¥Instead of producing JSX for every single item, you can also pass a function that returns JSX, and call that function when necessary. In this example, the App
component passes a renderContent
function to the TabSwitcher
component. The TabSwitcher
component calls renderContent
only for the selected tab:
import TabSwitcher from './TabSwitcher.js'; export default function App() { return ( <TabSwitcher tabIds={['first', 'second', 'third']} getHeader={tabId => { return tabId[0].toUpperCase() + tabId.slice(1); }} renderContent={tabId => { return <p>This is the {tabId} item.</p>; }} /> ); }
像 renderContent
这样的属性被称为渲染 prop,因为它指定了如何渲染用户界面的一部分。然而,这并没有什么特别之处:它是一个常规的 prop,恰好是一个函数。
¥A prop like renderContent
is called a render prop because it is a prop that specifies how to render a piece of the user interface. However, there is nothing special about it: it is a regular prop which happens to be a function.
渲染属性是函数,因此你可以向其传递信息。例如,此 RowList
组件将每行的 id
和 index
传递给 renderRow
渲染 prop,后者使用 index
高亮偶数行:
¥Render props are functions, so you can pass information to them. For example, this RowList
component passes the id
and the index
of each row to the renderRow
render prop, which uses index
to highlight even rows:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList rowIds={['first', 'second', 'third']} renderRow={(id, index) => { return ( <Row isHighlighted={index % 2 === 0}> <p>This is the {id} item.</p> </Row> ); }} /> ); }
这是另一个父子组件如何在不操作子组件的情况下进行协作的示例。
¥This is another example of how parent and child components can cooperate without manipulating the children.
故障排除
¥Troubleshooting
我传递了一个自定义组件,但 Children
方法没有显示其渲染结果
¥I pass a custom component, but the Children
methods don’t show its render result
假设你将两个子元素传递给 RowList
,如下所示:
¥Suppose you pass two children to RowList
like this:
<RowList>
<p>First item</p>
<MoreRows />
</RowList>
如果在 RowList
中执行 Children.count(children)
,则会得到 2
。即使 MoreRows
渲染了 10 个不同的项目,或者它返回了 null
,Children.count(children)
仍然会是 2
。从 RowList
的角度来看,它只会 “看到” 接收到的 JSX。它不会 “看到” MoreRows
组件的内部。
¥If you do Children.count(children)
inside RowList
, you will get 2
. Even if MoreRows
renders 10 different items, or if it returns null
, Children.count(children)
will still be 2
. From the RowList
’s perspective, it only “sees” the JSX it has received. It does not “see” the internals of the MoreRows
component.
此限制使得提取组件变得困难。这就是为什么 alternatives 优于 Children
的原因。
¥The limitation makes it hard to extract a component. This is why alternatives are preferred to using Children
.