如何向 {this.props.children} 传递 props

如何向 {this.props.children} 传递 props

技术背景

在 React 开发中,经常会遇到需要向 {this.props.children} 传递 props 的场景。例如,当我们有一个父组件,它包含多个子组件,并且希望将一些共享的数据或函数传递给这些子组件时,就需要找到合适的方法来实现这一需求。

实现步骤

1. 使用 React.cloneElement 克隆子元素并添加新的 props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const Child = ({ childName, sayHello }) => (
<button onClick={() => sayHello(childName)}>{childName}</button>
);

function Parent({ children }) {
function sayHello(childName) {
console.log(`Hello from ${childName} the child`);
}

const childrenWithProps = React.Children.map(children, child => {
if (React.isValidElement(child)) {
return React.cloneElement(child, { sayHello });
}
return child;
});

return <div>{childrenWithProps}</div>
}

function App() {
return (
<Parent>
<Child childName="Billy" />
<Child childName="Bob" />
</Parent>
);
}

ReactDOM.render(<App />, document.getElementById("container"));

2. 使用 render props 模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const Child = ({ childName, sayHello }) => (
<button onClick={() => sayHello(childName)}>{childName}</button>
);

function Parent({ children }) {
function sayHello(childName) {
console.log(`Hello from ${childName} the child`);
}

return <div>{children(sayHello)}</div>
}

function App() {
return (
<Parent>
{(sayHello) => (
<>
<Child childName="Billy" sayHello={sayHello} />
<Child childName="Bob" sayHello={sayHello} />
</>
)}
</Parent>
);
}

ReactDOM.render(<App />, document.getElementById("container"));

3. 使用 Context API 传递共享数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React from 'react';

const { Provider, Consumer } = React.createContext({ color: 'white' });

class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: { color: 'black' },
};
}

render() {
return (
<Provider value={this.state.value}>
<Toolbar />
</Provider>
);
}
}

class Toolbar extends React.Component {
render() {
return (
<div>
<p> Consumer can be arbitrary levels deep </p>
<Consumer>
{value => <p> The toolbar will be in color {value.color} </p>}
</Consumer>
</div>
);
}
}

核心代码

使用 React.cloneElement 的核心代码

1
2
3
4
5
6
const childrenWithProps = React.Children.map(children, child => {
if (React.isValidElement(child)) {
return React.cloneElement(child, { newProp: 'newValue' });
}
return child;
});

使用 render props 模式的核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Parent({ children }) {
const someProps = { prop1: 'value1', prop2: 'value2' };
return <div>{children(someProps)}</div>;
}

function App() {
return (
<Parent>
{props => (
<ChildComponent {...props}>
Bla-bla-bla
</ChildComponent>
)}
</Parent>
);
}

使用 Context API 的核心代码

1
2
3
4
5
6
7
8
9
const { Provider, Consumer } = React.createContext(defaultValue);

<Provider value={/* some value */}>
{children}
</Provider>

<Consumer>
{value => /* render something based on the context value */}
</Consumer>

最佳实践

  • 使用 React.cloneElement:在使用前要确保了解其工作原理,避免意外覆盖 ref。如果需要对多个子元素进行操作,使用 React.Children.map 进行遍历。
  • 使用 render props 模式时:这种方式使代码更加灵活,适合需要动态传递 props 的场景。但要注意子组件需要正确处理传入的函数参数。
  • 使用 Context API 时:Context 适合传递全局共享的数据,如主题、用户信息等。但要注意,过多使用 Context 会使组件之间的依赖关系变得复杂,降低代码的可维护性。

常见问题

1. React.cloneElementReact.Children.map 中使用时,元素的 key 会改变

可以使用 React.Children.forEach 代替 React.Children.map 来避免这个问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
render() {
const newElements = [];
React.Children.forEach(this.props.children,
child => newElements.push(
React.cloneElement(
child,
{...this.props, ...customProps}
)
)
);
return (
<div>{newElements}</div>
);
}

2. 使用 React.cloneElement 时遇到 TypeError: Cannot add property myNewProp, object is not extensible 错误

在函数式组件中,props 是不可变的。可以通过克隆 props 并创建新的子元素来解决这个问题。

1
2
3
4
5
6
7
8
9
10
11
12
const MyParentComponent = (props) => {
return (
<div className='whatever'>
{props.children.map((child) => {
const newProps = { ...child.props };
newProps.myNewProp = 'something';
const preparedChild = { ...child, props: newProps };
return preparedChild;
})}
</div>
);
};

3. 子元素不是 React 组件,如文本字符串

在使用 React.cloneElement 时,需要先检查元素是否为有效的 React 元素。

1
2
3
4
5
6
let childrenWithProps = React.Children.map( this.props.children, function(child) {
if (React.isValidElement(child)){
return React.cloneElement(child, props);
}
return child;
});

如何向 {this.props.children} 传递 props
https://119291.xyz/posts/how-to-pass-props-to-this-props-children/
作者
ww
发布于
2025年7月13日
许可协议