如何将道具传递给 {this.props.children}
How to pass props to {this.props.children}
我正在尝试找到定义一些可以通用方式使用的组件的正确方法:
<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}
定义包装器组件时,您如何将一些 属性 传递给它的所有子组件?
这是你需要的吗?
var Parent = React.createClass({
doSomething: function(value) {
}
render: function() {
return <div>
<Child doSome={this.doSomething} />
</div>
}
})
var Child = React.createClass({
onClick:function() {
this.props.doSome(value); // doSomething is undefined
},
render: function() {
return <div onClick={this.onClick}></div>
}
})
使用新道具克隆 children
您可以使用 React.Children
to iterate over the children, and then clone each element with new props (shallow merged) using React.cloneElement
。例如:
const Child = ({ doSomething, value }) => (
<button onClick={() => doSomething(value)}>Click Me</button>
);
function Parent({ children }) {
function doSomething(value) {
console.log("doSomething called by child with value:", value);
}
const childrenWithProps = React.Children.map(children, child => {
// Checking isValidElement is the safe way and avoids a typescript
// error too.
if (React.isValidElement(child)) {
return React.cloneElement(child, { doSomething });
}
return child;
});
return <div>{childrenWithProps}</div>
}
function App() {
return (
<Parent>
<Child value={1} />
<Child value={2} />
</Parent>
);
}
ReactDOM.render(<App />, document.getElementById("container"));
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<div id="container"></div>
作为函数调用children
或者,您可以使用 render props 将道具传递给 children。在这种方法中,children(可以是 children
或任何其他道具名称)是一个函数,它可以接受您要传递的任何参数,并且 return 是 children :
const Child = ({ doSomething, value }) => (
<button onClick={() => doSomething(value)}>Click Me</button>
);
function Parent({ children }) {
function doSomething(value) {
console.log("doSomething called by child with value:", value);
}
// Note that children is called as a function and we can pass args to it.
return <div>{children(doSomething)}</div>
}
function App() {
// doSomething is the arg we passed in Parent, which
// we now pass through to Child.
return (
<Parent>
{doSomething => (
<React.Fragment>
<Child doSomething={doSomething} value={1} />
<Child doSomething={doSomething} value={2} />
</React.Fragment>
)}
</Parent>
);
}
ReactDOM.render(<App />, document.getElementById("container"));
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<div id="container"></div>
而不是 <React.Fragment>
或简单地 <>
如果你愿意,你也可以 return 一个数组。
我需要修复上面接受的答案,使其使用 that 而不是 this 指针工作。 地图函数范围内的这个没有定义doSomething函数。
var Parent = React.createClass({
doSomething: function() {
console.log('doSomething!');
},
render: function() {
var that = this;
var childrenWithProps = React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, { doSomething: that.doSomething });
});
return <div>{childrenWithProps}</div>
}})
更新:此修复适用于 ECMAScript 5,在 ES6 中不需要 var that=this
要获得更简洁的方法,请尝试:
<div>
{React.cloneElement(this.props.children, { loggedIn: this.state.loggedIn })}
</div>
编辑:
要与多个单独的 children 一起使用(child 本身必须是一个组件),您可以这样做。在 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>
试试这个
<div>{React.cloneElement(this.props.children, {...this.props})}</div>
使用 react-15.1 对我有用。
https://reactjs.org/docs/jsx-in-depth.html#spread-attributes
中建议使用{...this.props}
最巧妙的方法:
{React.cloneElement(this.props.children, this.props)}
传递道具直接children。
查看所有其他答案
通过组件树通过 context
传递共享的全局数据
Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language. 1
免责声明:这是一个更新的答案,之前的答案使用旧上下文 API
它基于消费者/提供原则。首先,创建您的上下文
const { Provider, Consumer } = React.createContext(defaultValue);
然后使用via
<Provider value={/* some value */}>
{children} /* potential consumers */
</Provider>
和
<Consumer>
{value => /* render something based on the context value */}
</Consumer>
All Consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes. The propagation from Provider to its descendant Consumers is not subject to the shouldComponentUpdate method, so the Consumer is updated even when an ancestor component bails out of the update. 1
完整示例,semi-pseudo 代码。
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>
);
}
}
您不再需要 {this.props.children}
。现在你可以使用 render
in Route
包装你的子组件并像往常一样传递你的道具:
<BrowserRouter>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/posts">Posts</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<hr/>
<Route path="/" exact component={Home} />
<Route path="/posts" render={() => (
<Posts
value1={1}
value2={2}
data={this.state.data}
/>
)} />
<Route path="/about" component={About} />
</div>
</BrowserRouter>
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('...')
);
考虑一个或多个的更简洁的方式children
<div>
{ React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
除了@and_rest 的回答,这就是我克隆 children 并添加 class.
的方式
<div className="parent">
{React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
</div>
您可以使用React.cloneElement
,最好在您开始在您的应用程序中使用它之前了解它是如何工作的。它在 React v0.13
中介绍,请继续阅读以获取更多信息,因此与此相关的一些东西对您有用:
<div>{React.cloneElement(this.props.children, {...this.props})}</div>
因此,请使用 React 文档中的几行内容来了解它的工作原理以及如何使用它们:
In React v0.13 RC2 we will introduce a new API, similar to
React.addons.cloneWithProps, with this signature:
React.cloneElement(element, props, ...children);
Unlike cloneWithProps, this new function does not have any magic
built-in behavior for merging style and className for the same reason
we don't have that feature from transferPropsTo. Nobody is sure what
exactly the complete list of magic things are, which makes it
difficult to reason about the code and difficult to reuse when style
has a different signature (e.g. in the upcoming React Native).
React.cloneElement is almost equivalent to:
<element.type {...element.props} {...props}>{children}</element.type>
However, unlike JSX and cloneWithProps, it also preserves refs. This
means that if you get a child with a ref on it, you won't accidentally
steal it from your ancestor. You will get the same ref attached to
your new element.
One common pattern is to map over your children and add a new prop.
There were many issues reported about cloneWithProps losing the ref,
making it harder to reason about your code. Now following the same
pattern with cloneElement will work as expected. For example:
var newChildren = React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, { foo: true })
});
Note: React.cloneElement(child, { ref: 'newRef' }) DOES override the
ref so it is still not possible for two parents to have a ref to the
same child, unless you use callback-refs.
This was a critical feature to get into React 0.13 since props are now
immutable. The upgrade path is often to clone the element, but by
doing so you might lose the ref. Therefore, we needed a nicer upgrade
path here. As we were upgrading callsites at Facebook we realized that
we needed this method. We got the same feedback from the community.
Therefore we decided to make another RC before the final release to
make sure we get this in.
We plan to eventually deprecate React.addons.cloneWithProps. We're not
doing it yet, but this is a good opportunity to start thinking about
your own uses and consider using React.cloneElement instead. We'll be
sure to ship a release with deprecation notices before we actually
remove it so no immediate action is necessary.
更多here...
None 的答案解决了 children NOT React 组件(例如文本字符串)的问题。解决方法可能是这样的:
// Render method of Parent component
render(){
let props = {
setAlert : () => {alert("It works")}
};
let childrenWithProps = React.Children.map( this.props.children, function(child) {
if (React.isValidElement(child)){
return React.cloneElement(child, props);
}
return child;
});
return <div>{childrenWithProps}</div>
}
某些原因 React.children 对我不起作用。这对我有用。
我只想在 child 中添加一个 class。类似于改变道具
var newChildren = this.props.children.map((child) => {
const className = "MenuTooltip-item " + child.props.className;
return React.cloneElement(child, { className });
});
return <div>{newChildren}</div>;
这里的技巧是 React.cloneElement。您可以以类似的方式传递任何道具
根据 cloneElement()
的文档
React.cloneElement(
element,
[props],
[...children]
)
Clone and return a new React element using element as the starting
point. The resulting element will have the original element’s props
with the new props merged in shallowly. New children will replace
existing children. key and ref from the original element will be
preserved.
React.cloneElement()
is almost equivalent to:
<element.type {...element.props} {...props}>{children}</element.type>
However, it also preserves refs. This means that if you get a child
with a ref on it, you won’t accidentally steal it from your ancestor.
You will get the same ref attached to your new element.
因此,您可以使用 cloneElement 为 children 提供自定义道具。但是,组件中可以有多个 children,您需要对其进行循环。其他答案建议您使用 React.Children.map
映射它们。但是 React.Children.map
与 React.cloneElement
不同的是,它更改了元素附加的键和额外的 .$
作为前缀。检查此问题以获取更多详细信息:
如果你想避免它,你应该转而使用 forEach
函数,比如
render() {
const newElements = [];
React.Children.forEach(this.props.children,
child => newElements.push(
React.cloneElement(
child,
{...this.props, ...customProps}
)
)
)
return (
<div>{newElements}</div>
)
}
也许您也可以找到有用的此功能,尽管许多人认为这是一种反模式,但如果您知道自己在做什么并且设计好您的解决方案,它仍然可以使用。
如果你有多个 children 你想 pass props 到,你可以这样做,使用 React.Children.map:
render() {
let updatedChildren = React.Children.map(this.props.children,
(child) => {
return React.cloneElement(child, { newProp: newProp });
});
return (
<div>
{ updatedChildren }
</div>
);
}
如果您的组件只有一个 child,则不需要映射,您可以直接克隆元素:
render() {
return (
<div>
{
React.cloneElement(this.props.children, {
newProp: newProp
})
}
</div>
);
}
将道具传递给嵌套 Children
随着 React 16.6 的更新,您现在可以使用 React.createContext 和 contextType.
import * as React from 'react';
// React.createContext accepts a defaultValue as the first param
const MyContext = React.createContext();
class Parent extends React.Component {
doSomething = (value) => {
// Do something here with value
};
render() {
return (
<MyContext.Provider value={{ doSomething: this.doSomething }}>
{this.props.children}
</MyContext.Provider>
);
}
}
class Child extends React.Component {
static contextType = MyContext;
onClick = () => {
this.context.doSomething(this.props.value);
};
render() {
return (
<div onClick={this.onClick}>{this.props.value}</div>
);
}
}
// Example of using Parent and Child
import * as React from 'react';
class SomeComponent extends React.Component {
render() {
return (
<Parent>
<Child value={1} />
<Child value={2} />
</Parent>
);
}
}
React.createContext 在 React.cloneElement 案例无法处理嵌套组件的地方闪耀
class SomeComponent extends React.Component {
render() {
return (
<Parent>
<Child value={1} />
<SomeOtherComp><Child value={2} /></SomeOtherComp>
</Parent>
);
}
}
任何只有一个子元素的人都应该这样做。
{React.isValidElement(this.props.children)
? React.cloneElement(this.props.children, {
...prop_you_want_to_pass
})
: null}
允许您进行 属性 传输的最佳方法是 children
函数模式
https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9
代码片段:https://stackblitz.com/edit/react-fcmubc
示例:
const Parent = ({ children }) => {
const somePropsHere = {
style: {
color: "red"
}
// any other props here...
}
return children(somePropsHere)
}
const ChildComponent = props => <h1 {...props}>Hello world!</h1>
const App = () => {
return (
<Parent>
{props => (
<ChildComponent {...props}>
Bla-bla-bla
</ChildComponent>
)}
</Parent>
)
}
Render props 是解决此问题的最准确方法。让父组件手动渲染子组件,而不是将子组件作为子组件传递给父组件。 Render是react中内置的props,它带有function参数。在此函数中,您可以让父组件使用自定义参数呈现您想要的任何内容。基本上它与子道具做同样的事情,但它更可定制。
class Child extends React.Component {
render() {
return <div className="Child">
Child
<p onClick={this.props.doSomething}>Click me</p>
{this.props.a}
</div>;
}
}
class Parent extends React.Component {
doSomething(){
alert("Parent talks");
}
render() {
return <div className="Parent">
Parent
{this.props.render({
anythingToPassChildren:1,
doSomething: this.doSomething})}
</div>;
}
}
class Application extends React.Component {
render() {
return <div>
<Parent render={
props => <Child {...props} />
}/>
</div>;
}
}
我认为渲染道具是处理这种情况的合适方法
您让父级提供子组件中使用的必要道具,方法是重构父级代码,使其看起来像这样:
const Parent = ({children}) => {
const doSomething(value) => {}
return children({ doSomething })
}
那么在子Component中可以这样访问父Component提供的功能:
class Child extends React {
onClick() => { this.props.doSomething }
render() {
return (<div onClick={this.onClick}></div>);
}
}
现在最终结构将如下所示:
<Parent>
{(doSomething) =>
(<Fragment>
<Child value="1" doSomething={doSomething}>
<Child value="2" doSomething={doSomething}>
<Fragment />
)}
</Parent>
方法 1 - 克隆 children
const Parent = (props) => {
const attributeToAddOrReplace= "Some Value"
const childrenWithAdjustedProps = React.Children.map(props.children, child =>
React.cloneElement(child, { attributeToAddOrReplace})
);
return <div>{childrenWithAdjustedProps }</div>
}
方法 2 - 使用可组合上下文
Context 允许您将 prop 传递给深层 child 组件,而无需将其作为 prop 显式传递给中间的组件。
上下文有缺点:
- 数据不会以常规方式流动 - 通过 props。
- 使用上下文在消费者和提供者之间创建契约。理解和复制重用组件所需的要求可能更加困难。
使用可组合上下文
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>;
};
使用函数式组件时,在 props.children
上设置新属性时经常会遇到 TypeError: Cannot add property myNewProp, object is not extensible
错误。通过克隆道具然后使用新道具克隆 child 本身,可以解决此问题。
const MyParentComponent = (props) => {
return (
<div className='whatever'>
{props.children.map((child) => {
const newProps = { ...child.props }
// set new props here on newProps
newProps.myNewProp = 'something'
const preparedChild = { ...child, props: newProps }
return preparedChild
})}
</div>
)
}
我在研究类似需求时遇到了这个 post,但我觉得克隆解决方案非常流行,太原始了,让我的注意力从功能上转移了。
我在react文档中找到一篇文章Higher Order Components
这是我的示例:
import React from 'react';
const withForm = (ViewComponent) => {
return (props) => {
const myParam = "Custom param";
return (
<>
<div style={{border:"2px solid black", margin:"10px"}}>
<div>this is poc form</div>
<div>
<ViewComponent myParam={myParam} {...props}></ViewComponent>
</div>
</div>
</>
)
}
}
export default withForm;
const pocQuickView = (props) => {
return (
<div style={{border:"1px solid grey"}}>
<div>this is poc quick view and it is meant to show when mouse hovers over a link</div>
</div>
)
}
export default withForm(pocQuickView);
对我来说,我找到了实现高阶组件模式的灵活解决方案。
当然这取决于功能,但如果其他人正在寻找类似的需求,这很好,这比依赖像克隆这样的原始级别反应代码要好得多。
我积极使用的其他模式是容器模式。一定要读一读,那里有很多文章。
受到以上所有答案的启发,这就是我所做的。我正在传递一些道具,例如一些数据和一些组件。
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}</>;
};
这个答案是w.r.t。反应 v17.x...
将children
作为函数使用,并将props作为render props
模式传递给它,如下所示:-
<ParentComponent {...anyAdditionalProps}>
{
(actualPropsToPass) => <ChildComponent>{children(actualPropsToPass)}</ChildComponent>
}
</ParentComponent>
请确保,实际要投影的内容必须像渲染道具模式中的函数一样添加,以容纳在子函数中作为 prop
传递的参数。
有很多方法可以做到这一点。
您可以将 children 作为道具传递给 parent。
示例 1 :
function Parent({ChildElement}){
return <ChildElement propName={propValue} />
}
return <Parent ChildElement={ChildComponent}/>
将children作为函数传递
示例 2 :
function Parent({children}){
return children({className: "my_div"})
}
OR
function Parent({children}){
let Child = children
return <Child className='my_div' />
}
function Child(props){
return <div {...props}></div>
}
export <Parent>{props => <Child {...props} />}</Parent>
我确实努力让列出的答案起作用但失败了。最终,我发现问题出在正确设置父子关系上。仅仅将组件嵌套在其他组件中并不意味着存在父子关系。
例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 的情况下有效。
万一有人想知道如何在有一个或多个子节点的 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
,甚至允许您根据需要将属性向下传递给子组件。
这是我的版本,适用于单个、多个和无效 children。
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
我正在尝试找到定义一些可以通用方式使用的组件的正确方法:
<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}
定义包装器组件时,您如何将一些 属性 传递给它的所有子组件?
这是你需要的吗?
var Parent = React.createClass({
doSomething: function(value) {
}
render: function() {
return <div>
<Child doSome={this.doSomething} />
</div>
}
})
var Child = React.createClass({
onClick:function() {
this.props.doSome(value); // doSomething is undefined
},
render: function() {
return <div onClick={this.onClick}></div>
}
})
使用新道具克隆 children
您可以使用 React.Children
to iterate over the children, and then clone each element with new props (shallow merged) using React.cloneElement
。例如:
const Child = ({ doSomething, value }) => (
<button onClick={() => doSomething(value)}>Click Me</button>
);
function Parent({ children }) {
function doSomething(value) {
console.log("doSomething called by child with value:", value);
}
const childrenWithProps = React.Children.map(children, child => {
// Checking isValidElement is the safe way and avoids a typescript
// error too.
if (React.isValidElement(child)) {
return React.cloneElement(child, { doSomething });
}
return child;
});
return <div>{childrenWithProps}</div>
}
function App() {
return (
<Parent>
<Child value={1} />
<Child value={2} />
</Parent>
);
}
ReactDOM.render(<App />, document.getElementById("container"));
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<div id="container"></div>
作为函数调用children
或者,您可以使用 render props 将道具传递给 children。在这种方法中,children(可以是 children
或任何其他道具名称)是一个函数,它可以接受您要传递的任何参数,并且 return 是 children :
const Child = ({ doSomething, value }) => (
<button onClick={() => doSomething(value)}>Click Me</button>
);
function Parent({ children }) {
function doSomething(value) {
console.log("doSomething called by child with value:", value);
}
// Note that children is called as a function and we can pass args to it.
return <div>{children(doSomething)}</div>
}
function App() {
// doSomething is the arg we passed in Parent, which
// we now pass through to Child.
return (
<Parent>
{doSomething => (
<React.Fragment>
<Child doSomething={doSomething} value={1} />
<Child doSomething={doSomething} value={2} />
</React.Fragment>
)}
</Parent>
);
}
ReactDOM.render(<App />, document.getElementById("container"));
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<div id="container"></div>
而不是 <React.Fragment>
或简单地 <>
如果你愿意,你也可以 return 一个数组。
我需要修复上面接受的答案,使其使用 that 而不是 this 指针工作。 地图函数范围内的这个没有定义doSomething函数。
var Parent = React.createClass({
doSomething: function() {
console.log('doSomething!');
},
render: function() {
var that = this;
var childrenWithProps = React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, { doSomething: that.doSomething });
});
return <div>{childrenWithProps}</div>
}})
更新:此修复适用于 ECMAScript 5,在 ES6 中不需要 var that=this
要获得更简洁的方法,请尝试:
<div>
{React.cloneElement(this.props.children, { loggedIn: this.state.loggedIn })}
</div>
编辑: 要与多个单独的 children 一起使用(child 本身必须是一个组件),您可以这样做。在 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>
试试这个
<div>{React.cloneElement(this.props.children, {...this.props})}</div>
使用 react-15.1 对我有用。
https://reactjs.org/docs/jsx-in-depth.html#spread-attributes
中建议使用{...this.props}
最巧妙的方法:
{React.cloneElement(this.props.children, this.props)}
传递道具直接children。
查看所有其他答案
通过组件树通过 context
传递共享的全局数据Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language. 1
免责声明:这是一个更新的答案,之前的答案使用旧上下文 API
它基于消费者/提供原则。首先,创建您的上下文
const { Provider, Consumer } = React.createContext(defaultValue);
然后使用via
<Provider value={/* some value */}>
{children} /* potential consumers */
</Provider>
和
<Consumer>
{value => /* render something based on the context value */}
</Consumer>
All Consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes. The propagation from Provider to its descendant Consumers is not subject to the shouldComponentUpdate method, so the Consumer is updated even when an ancestor component bails out of the update. 1
完整示例,semi-pseudo 代码。
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>
);
}
}
您不再需要 {this.props.children}
。现在你可以使用 render
in Route
包装你的子组件并像往常一样传递你的道具:
<BrowserRouter>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/posts">Posts</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<hr/>
<Route path="/" exact component={Home} />
<Route path="/posts" render={() => (
<Posts
value1={1}
value2={2}
data={this.state.data}
/>
)} />
<Route path="/about" component={About} />
</div>
</BrowserRouter>
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('...')
);
考虑一个或多个的更简洁的方式children
<div>
{ React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
除了@and_rest 的回答,这就是我克隆 children 并添加 class.
的方式<div className="parent">
{React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
</div>
您可以使用React.cloneElement
,最好在您开始在您的应用程序中使用它之前了解它是如何工作的。它在 React v0.13
中介绍,请继续阅读以获取更多信息,因此与此相关的一些东西对您有用:
<div>{React.cloneElement(this.props.children, {...this.props})}</div>
因此,请使用 React 文档中的几行内容来了解它的工作原理以及如何使用它们:
In React v0.13 RC2 we will introduce a new API, similar to React.addons.cloneWithProps, with this signature:
React.cloneElement(element, props, ...children);
Unlike cloneWithProps, this new function does not have any magic built-in behavior for merging style and className for the same reason we don't have that feature from transferPropsTo. Nobody is sure what exactly the complete list of magic things are, which makes it difficult to reason about the code and difficult to reuse when style has a different signature (e.g. in the upcoming React Native).
React.cloneElement is almost equivalent to:
<element.type {...element.props} {...props}>{children}</element.type>
However, unlike JSX and cloneWithProps, it also preserves refs. This means that if you get a child with a ref on it, you won't accidentally steal it from your ancestor. You will get the same ref attached to your new element.
One common pattern is to map over your children and add a new prop. There were many issues reported about cloneWithProps losing the ref, making it harder to reason about your code. Now following the same pattern with cloneElement will work as expected. For example:
var newChildren = React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, { foo: true })
});
Note: React.cloneElement(child, { ref: 'newRef' }) DOES override the ref so it is still not possible for two parents to have a ref to the same child, unless you use callback-refs.
This was a critical feature to get into React 0.13 since props are now immutable. The upgrade path is often to clone the element, but by doing so you might lose the ref. Therefore, we needed a nicer upgrade path here. As we were upgrading callsites at Facebook we realized that we needed this method. We got the same feedback from the community. Therefore we decided to make another RC before the final release to make sure we get this in.
We plan to eventually deprecate React.addons.cloneWithProps. We're not doing it yet, but this is a good opportunity to start thinking about your own uses and consider using React.cloneElement instead. We'll be sure to ship a release with deprecation notices before we actually remove it so no immediate action is necessary.
更多here...
None 的答案解决了 children NOT React 组件(例如文本字符串)的问题。解决方法可能是这样的:
// Render method of Parent component
render(){
let props = {
setAlert : () => {alert("It works")}
};
let childrenWithProps = React.Children.map( this.props.children, function(child) {
if (React.isValidElement(child)){
return React.cloneElement(child, props);
}
return child;
});
return <div>{childrenWithProps}</div>
}
某些原因 React.children 对我不起作用。这对我有用。
我只想在 child 中添加一个 class。类似于改变道具
var newChildren = this.props.children.map((child) => {
const className = "MenuTooltip-item " + child.props.className;
return React.cloneElement(child, { className });
});
return <div>{newChildren}</div>;
这里的技巧是 React.cloneElement。您可以以类似的方式传递任何道具
根据 cloneElement()
React.cloneElement(
element,
[props],
[...children]
)
Clone and return a new React element using element as the starting point. The resulting element will have the original element’s props with the new props merged in shallowly. New children will replace existing children. key and ref from the original element will be preserved.
React.cloneElement()
is almost equivalent to:<element.type {...element.props} {...props}>{children}</element.type>
However, it also preserves refs. This means that if you get a child with a ref on it, you won’t accidentally steal it from your ancestor. You will get the same ref attached to your new element.
因此,您可以使用 cloneElement 为 children 提供自定义道具。但是,组件中可以有多个 children,您需要对其进行循环。其他答案建议您使用 React.Children.map
映射它们。但是 React.Children.map
与 React.cloneElement
不同的是,它更改了元素附加的键和额外的 .$
作为前缀。检查此问题以获取更多详细信息:
如果你想避免它,你应该转而使用 forEach
函数,比如
render() {
const newElements = [];
React.Children.forEach(this.props.children,
child => newElements.push(
React.cloneElement(
child,
{...this.props, ...customProps}
)
)
)
return (
<div>{newElements}</div>
)
}
也许您也可以找到有用的此功能,尽管许多人认为这是一种反模式,但如果您知道自己在做什么并且设计好您的解决方案,它仍然可以使用。
如果你有多个 children 你想 pass props 到,你可以这样做,使用 React.Children.map:
render() {
let updatedChildren = React.Children.map(this.props.children,
(child) => {
return React.cloneElement(child, { newProp: newProp });
});
return (
<div>
{ updatedChildren }
</div>
);
}
如果您的组件只有一个 child,则不需要映射,您可以直接克隆元素:
render() {
return (
<div>
{
React.cloneElement(this.props.children, {
newProp: newProp
})
}
</div>
);
}
将道具传递给嵌套 Children
随着 React 16.6 的更新,您现在可以使用 React.createContext 和 contextType.
import * as React from 'react';
// React.createContext accepts a defaultValue as the first param
const MyContext = React.createContext();
class Parent extends React.Component {
doSomething = (value) => {
// Do something here with value
};
render() {
return (
<MyContext.Provider value={{ doSomething: this.doSomething }}>
{this.props.children}
</MyContext.Provider>
);
}
}
class Child extends React.Component {
static contextType = MyContext;
onClick = () => {
this.context.doSomething(this.props.value);
};
render() {
return (
<div onClick={this.onClick}>{this.props.value}</div>
);
}
}
// Example of using Parent and Child
import * as React from 'react';
class SomeComponent extends React.Component {
render() {
return (
<Parent>
<Child value={1} />
<Child value={2} />
</Parent>
);
}
}
React.createContext 在 React.cloneElement 案例无法处理嵌套组件的地方闪耀
class SomeComponent extends React.Component {
render() {
return (
<Parent>
<Child value={1} />
<SomeOtherComp><Child value={2} /></SomeOtherComp>
</Parent>
);
}
}
任何只有一个子元素的人都应该这样做。
{React.isValidElement(this.props.children)
? React.cloneElement(this.props.children, {
...prop_you_want_to_pass
})
: null}
允许您进行 属性 传输的最佳方法是 children
函数模式
https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9
代码片段:https://stackblitz.com/edit/react-fcmubc
示例:
const Parent = ({ children }) => {
const somePropsHere = {
style: {
color: "red"
}
// any other props here...
}
return children(somePropsHere)
}
const ChildComponent = props => <h1 {...props}>Hello world!</h1>
const App = () => {
return (
<Parent>
{props => (
<ChildComponent {...props}>
Bla-bla-bla
</ChildComponent>
)}
</Parent>
)
}
Render props 是解决此问题的最准确方法。让父组件手动渲染子组件,而不是将子组件作为子组件传递给父组件。 Render是react中内置的props,它带有function参数。在此函数中,您可以让父组件使用自定义参数呈现您想要的任何内容。基本上它与子道具做同样的事情,但它更可定制。
class Child extends React.Component {
render() {
return <div className="Child">
Child
<p onClick={this.props.doSomething}>Click me</p>
{this.props.a}
</div>;
}
}
class Parent extends React.Component {
doSomething(){
alert("Parent talks");
}
render() {
return <div className="Parent">
Parent
{this.props.render({
anythingToPassChildren:1,
doSomething: this.doSomething})}
</div>;
}
}
class Application extends React.Component {
render() {
return <div>
<Parent render={
props => <Child {...props} />
}/>
</div>;
}
}
我认为渲染道具是处理这种情况的合适方法
您让父级提供子组件中使用的必要道具,方法是重构父级代码,使其看起来像这样:
const Parent = ({children}) => {
const doSomething(value) => {}
return children({ doSomething })
}
那么在子Component中可以这样访问父Component提供的功能:
class Child extends React {
onClick() => { this.props.doSomething }
render() {
return (<div onClick={this.onClick}></div>);
}
}
现在最终结构将如下所示:
<Parent>
{(doSomething) =>
(<Fragment>
<Child value="1" doSomething={doSomething}>
<Child value="2" doSomething={doSomething}>
<Fragment />
)}
</Parent>
方法 1 - 克隆 children
const Parent = (props) => {
const attributeToAddOrReplace= "Some Value"
const childrenWithAdjustedProps = React.Children.map(props.children, child =>
React.cloneElement(child, { attributeToAddOrReplace})
);
return <div>{childrenWithAdjustedProps }</div>
}
方法 2 - 使用可组合上下文
Context 允许您将 prop 传递给深层 child 组件,而无需将其作为 prop 显式传递给中间的组件。
上下文有缺点:
- 数据不会以常规方式流动 - 通过 props。
- 使用上下文在消费者和提供者之间创建契约。理解和复制重用组件所需的要求可能更加困难。
使用可组合上下文
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>;
};
使用函数式组件时,在 props.children
上设置新属性时经常会遇到 TypeError: Cannot add property myNewProp, object is not extensible
错误。通过克隆道具然后使用新道具克隆 child 本身,可以解决此问题。
const MyParentComponent = (props) => {
return (
<div className='whatever'>
{props.children.map((child) => {
const newProps = { ...child.props }
// set new props here on newProps
newProps.myNewProp = 'something'
const preparedChild = { ...child, props: newProps }
return preparedChild
})}
</div>
)
}
我在研究类似需求时遇到了这个 post,但我觉得克隆解决方案非常流行,太原始了,让我的注意力从功能上转移了。
我在react文档中找到一篇文章Higher Order Components
这是我的示例:
import React from 'react';
const withForm = (ViewComponent) => {
return (props) => {
const myParam = "Custom param";
return (
<>
<div style={{border:"2px solid black", margin:"10px"}}>
<div>this is poc form</div>
<div>
<ViewComponent myParam={myParam} {...props}></ViewComponent>
</div>
</div>
</>
)
}
}
export default withForm;
const pocQuickView = (props) => {
return (
<div style={{border:"1px solid grey"}}>
<div>this is poc quick view and it is meant to show when mouse hovers over a link</div>
</div>
)
}
export default withForm(pocQuickView);
对我来说,我找到了实现高阶组件模式的灵活解决方案。
当然这取决于功能,但如果其他人正在寻找类似的需求,这很好,这比依赖像克隆这样的原始级别反应代码要好得多。
我积极使用的其他模式是容器模式。一定要读一读,那里有很多文章。
受到以上所有答案的启发,这就是我所做的。我正在传递一些道具,例如一些数据和一些组件。
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}</>;
};
这个答案是w.r.t。反应 v17.x...
将children
作为函数使用,并将props作为render props
模式传递给它,如下所示:-
<ParentComponent {...anyAdditionalProps}>
{
(actualPropsToPass) => <ChildComponent>{children(actualPropsToPass)}</ChildComponent>
}
</ParentComponent>
请确保,实际要投影的内容必须像渲染道具模式中的函数一样添加,以容纳在子函数中作为 prop
传递的参数。
有很多方法可以做到这一点。
您可以将 children 作为道具传递给 parent。
示例 1 :
function Parent({ChildElement}){
return <ChildElement propName={propValue} />
}
return <Parent ChildElement={ChildComponent}/>
将children作为函数传递
示例 2 :
function Parent({children}){
return children({className: "my_div"})
}
OR
function Parent({children}){
let Child = children
return <Child className='my_div' />
}
function Child(props){
return <div {...props}></div>
}
export <Parent>{props => <Child {...props} />}</Parent>
我确实努力让列出的答案起作用但失败了。最终,我发现问题出在正确设置父子关系上。仅仅将组件嵌套在其他组件中并不意味着存在父子关系。
例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 的情况下有效。
万一有人想知道如何在有一个或多个子节点的 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
,甚至允许您根据需要将属性向下传递给子组件。
这是我的版本,适用于单个、多个和无效 children。
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