我试图找到正确的方法来定义一些组件,这些组件可以以通用的方式使用:
<Parent>
<Child value="1">
<Child value="2">
</Parent>
当然,在父组件和子组件之间呈现有一个逻辑,您可以想象<select>和<option>是这个逻辑的一个例子。
这是为了解决这个问题的一个虚拟实现:
var Parent = React.createClass({
doSomething: function(value) {
},
render: function() {
return (<div>{this.props.children}</div>);
}
});
var Child = React.createClass({
onClick: function() {
this.props.doSomething(this.props.value); // doSomething is undefined
},
render: function() {
return (<div onClick={this.onClick}></div>);
}
});
问题是无论何时使用{this.props。Children}定义一个包装器组件,如何将某些属性传递给它的所有子组件?
有一种稍微干净一点的方法,试试:
<div>
{React.cloneElement(this.props.children, { loggedIn: this.state.loggedIn })}
</div>
编辑:
要与多个单独的子组件一起使用(子组件本身必须是一个组件),您可以这样做。在16.8.6中测试
<div>
{React.cloneElement(this.props.children[0], { loggedIn: true, testPropB: true })}
{React.cloneElement(this.props.children[1], { loggedIn: true, testPropA: false })}
</div>
用新道具克隆孩子
你可以使用React。子元素遍历子元素,然后使用React.cloneElement用新道具(浅合并)克隆每个元素。
请参阅代码注释,为什么我不推荐这种方法。
const Child = ({childName, sayHello}) => (
<button onClick={() => sayHello(childName)}>{childName}</button>
);
函数父({children}) {
//我们将sayHello函数传递给子元素。
函数sayHello(childName) {
console.log(' Hello from ${childName} the child ');
}
const childrenWithProps = React.Children。映射(children, child => {
//检查isValidElement是安全的方法,可以避免
// typescript错误。
if (React.isValidElement(child)) {
返回的反应。cloneElement(child, {sayHello});
}
返回的孩子;
});
返回< div > {childrenWithProps} < / div >
}
函数App() {
//这种方法不太类型安全,Typescript不友好
//看起来像你试图渲染' Child '没有' sayHello '。
//这也会让代码的读者感到困惑。
回报(
<父>
<Child childName="Billy" />
<Child childName="Bob" />
父> < /
);
}
ReactDOM。render(<App />, document.getElementById("container"));
< script src = " https://unpkg.com/react@17 umd格式/ react.production.min.js " > < /脚本>
< script src = " https://unpkg.com/react-dom@17 umd格式/ react-dom.production.min.js " > < /脚本>
< div id = "容器" > < / div >
将子函数作为函数调用
或者,你可以通过渲染道具将道具传递给孩子们。在这种方法中,children(可以是children或任何其他道具名)是一个函数,它可以接受你想传递的任何参数,并返回实际的子函数:
const Child = ({childName, sayHello}) => (
<button onClick={() => sayHello(childName)}>{childName}</button>
);
函数父({children}) {
函数sayHello(childName) {
console.log(' Hello from ${childName} the child ');
}
//这个组件的children必须是一个函数
//返回实际的子节点。我们可以通过
//它的参数然后传递给他们作为道具(在这个
//如果我们传递' sayHello ')。
返回< div >{孩子(sayHello)} < / div >
}
函数App() {
// sayHello是我们在Parent中传递的参数
//我们现在传递到Child。
回报(
<父>
{(sayHello) => (
<反应。片段>
<Child childName="Billy" sayHello={sayHello} />
<Child childName="Bob" sayHello={sayHello} />
< /反应。片段>
)}
父> < /
);
}
ReactDOM。render(<App />, document.getElementById("container"));
< script src = " https://unpkg.com/react@17 umd格式/ react.production.min.js " > < /脚本>
< script src = " https://unpkg.com/react-dom@17 umd格式/ react-dom.production.min.js " > < /脚本>
< div id = "容器" > < / div >
向嵌套子节点传递道具
随着React Hooks的更新,你现在可以使用React了。createContext和useContext。
import * as React from 'react';
// React.createContext accepts a defaultValue as the first param
const MyContext = React.createContext();
functional Parent(props) {
const doSomething = React.useCallback((value) => {
// Do something here with value
}, []);
return (
<MyContext.Provider value={{ doSomething }}>
{props.children}
</MyContext.Provider>
);
}
function Child(props: { value: number }) {
const myContext = React.useContext(MyContext);
const onClick = React.useCallback(() => {
myContext.doSomething(props.value);
}, [props.value, myContext.doSomething]);
return (
<div onClick={onClick}>{props.value}</div>
);
}
// Example of using Parent and Child
import * as React from 'react';
function SomeComponent() {
return (
<Parent>
<Child value={1} />
<Child value={2} />
</Parent>
);
}
反应。createContext发光的地方React。cloneElement case不能处理嵌套组件
function SomeComponent() {
return (
<Parent>
<Child value={1} />
<SomeOtherComp>
<Child value={2} />
</SomeOtherComp>
</Parent>
);
}
从上面所有的答案中得到启发,这就是我所做的。我传递一些道具,比如一些数据和一些组件。
import React from "react";
const Parent = ({ children }) => {
const { setCheckoutData } = actions.shop;
const { Input, FieldError } = libraries.theme.components.forms;
const onSubmit = (data) => {
setCheckoutData(data);
};
const childrenWithProps = React.Children.map(
children,
(child) =>
React.cloneElement(child, {
Input: Input,
FieldError: FieldError,
onSubmit: onSubmit,
})
);
return <>{childrenWithProps}</>;
};
Parent.jsx:
import React from 'react';
const doSomething = value => {};
const Parent = props => (
<div>
{
!props || !props.children
? <div>Loading... (required at least one child)</div>
: !props.children.length
? <props.children.type {...props.children.props} doSomething={doSomething} {...props}>{props.children}</props.children.type>
: props.children.map((child, key) =>
React.cloneElement(child, {...props, key, doSomething}))
}
</div>
);
Child.jsx:
import React from 'react';
/* but better import doSomething right here,
or use some flux store (for example redux library) */
export default ({ doSomething, value }) => (
<div onClick={() => doSomething(value)}/>
);
和main.jsx:
import React from 'react';
import { render } from 'react-dom';
import Parent from './Parent';
import Child from './Child';
render(
<Parent>
<Child/>
<Child value='1'/>
<Child value='2'/>
</Parent>,
document.getElementById('...')
);
参见示例:https://plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview