我试图找到正确的方法来定义一些组件,这些组件可以以通用的方式使用:
<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}定义一个包装器组件,如何将某些属性传递给它的所有子组件?
用新道具克隆孩子
你可以使用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 >
我确实努力让列出的答案工作,但失败了。最终,我发现问题在于正确地建立亲子关系。仅仅在其他组件中嵌套组件并不意味着存在父子关系。
例1。亲子关系;
function Wrapper() {
return (
<div>
<OuterComponent>
<InnerComponent />
</OuterComponent>
</div>
);
}
function OuterComponent(props) {
return props.children;
}
function InnerComponent() {
return <div>Hi! I'm in inner component!</div>;
}
export default Wrapper;
例2。嵌套的组件:
function Wrapper() {
return (
<div>
<OuterComponent />
</div>
);
}
function OuterComponent(props) {
return <InnerComponent />
}
function InnerComponent() {
return <div>Hi! I'm in inner component!</div>;
}
export default Wrapper;
如上所述,道具传递在例1中有效。
下面的文章对此进行了解释https://medium.com/@justynazet/passing-props-to-props-children-using-react-cloneelement- andrend-props -pattern-896da70b24f6
下面是我的版本,适用于单个、多个和无效的子节点。
const addPropsToChildren = (children, props) => {
const addPropsToChild = (child, props) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, props);
} else {
console.log("Invalid element: ", child);
return child;
}
};
if (Array.isArray(children)) {
return children.map((child, ix) =>
addPropsToChild(child, { key: ix, ...props })
);
} else {
return addPropsToChild(children, props);
}
};
使用的例子:
https://codesandbox.io/s/loving-mcclintock-59emq?file=/src/ChildVsChildren.jsx:0-1069
如果有人想知道如何在有一个或多个子节点的TypeScript中正确地做到这一点。我使用uuid库为子元素生成唯一的键属性,当然,如果只克隆一个元素,则不需要这些属性。
export type TParentGroup = {
value?: string;
children: React.ReactElement[] | React.ReactElement;
};
export const Parent = ({
value = '',
children,
}: TParentGroup): React.ReactElement => (
<div className={styles.ParentGroup}>
{Array.isArray(children)
? children.map((child) =>
React.cloneElement(child, { key: uuidv4(), value })
)
: React.cloneElement(children, { value })}
</div>
);
如您所见,此解决方案负责呈现ReactElement的数组或单个ReactElement,甚至允许您根据需要将属性传递给子组件。