我试图找到正确的方法来定义一些组件,这些组件可以以通用的方式使用:
<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 v0.13中引入的,请继续阅读以获取更多信息,因此您将了解到以下内容:
<div>{React.cloneElement(this.props.children, {...this.props})}</div>
所以,请从React文档中找到这些行,让你了解它是如何工作的,以及如何使用它们:
在React v0.13 RC2中,我们将引入一个新的API,类似于
React.addons。cloneWithProps,用这个签名:
React.cloneElement(element, props, ...children);
与cloneWithProps不同,这个新函数没有任何魔力
出于同样的原因合并style和className的内置行为
我们在transferPropsTo中没有这个特性。没人能确定
所有魔法物品都是这样,所以
很难推理的代码和重用时的风格
有一个不同的签名(例如在即将到来的React Native中)。
反应。cloneElement几乎等同于:
<element.type {...element.props} {...props}>{children}</element.type>
然而,与JSX和cloneWithProps不同,它还保留了引用。这
意味着如果你得到一个有裁判的子球,你不会不小心
从你祖先那里偷来。你会得到相同的引用
你的新元素。
一种常见的模式是映射子元素并添加一个新道具。
有许多关于cloneWithProps失去ref的问题报道,
让你的代码更难推理。同样的道理
使用cloneElement的pattern将按预期工作。例如:
var newChildren = React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, { foo: true })
});
注意:反应。cloneElement(child, {ref: 'newRef'})会覆盖
所以这仍然是不可能的两个父母有一个裁判
相同的子元素,除非你使用callback-refs。
这是React 0.13的一个关键功能,因为现在有道具了
不可变的。升级路径通常是克隆元素,但通过
这样做你可能会失去裁判。因此,我们需要一个更好的升级
路径。当我们升级Facebook的呼叫网站时,我们意识到这一点
我们需要这个方法。我们从社区得到了同样的反馈。
因此我们决定在最终版本之前再做一个RC
一定要把这个弄进去。
我们计划最终弃用React.addons.cloneWithProps。我们不是
还在做,但这是一个开始思考的好机会
你可以考虑使用React。cloneElement代替。我们会
确保在我们真正发布之前发布一个带有弃用通知的版本
删除它,这样就不需要立即采取行动。
这里……
如果有人想知道如何在有一个或多个子节点的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,甚至允许您根据需要将属性传递给子组件。
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
方法一——克隆儿童
const Parent = (props) => {
const attributeToAddOrReplace= "Some Value"
const childrenWithAdjustedProps = React.Children.map(props.children, child =>
React.cloneElement(child, { attributeToAddOrReplace})
);
return <div>{childrenWithAdjustedProps }</div>
}
完整的演示
方法2 -使用可组合的上下文
上下文允许您将道具传递给深度子组件,而无需显式地将其作为道具传递给中间的组件。
环境也有缺点:
数据不会以常规的方式流动——通过道具。
使用上下文将在使用者和提供者之间创建契约。理解和复制重用组件所需的需求可能会更加困难。
使用可组合的上下文
export const Context = createContext<any>(null);
export const ComposableContext = ({ children, ...otherProps }:{children:ReactNode, [x:string]:any}) => {
const context = useContext(Context)
return(
<Context.Provider {...context} value={{...context, ...otherProps}}>{children}</Context.Provider>
);
}
function App() {
return (
<Provider1>
<Provider2>
<Displayer />
</Provider2>
</Provider1>
);
}
const Provider1 =({children}:{children:ReactNode}) => (
<ComposableContext greeting="Hello">{children}</ComposableContext>
)
const Provider2 =({children}:{children:ReactNode}) => (
<ComposableContext name="world">{children}</ComposableContext>
)
const Displayer = () => {
const context = useContext(Context);
return <div>{context.greeting}, {context.name}</div>;
};
有一种稍微干净一点的方法,试试:
<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>