如何将道具传递给{this.props.children}

plu*_*us- 803 javascript reactjs react-jsx

我正在尝试找到正确的方法来定义一些可以通用方式使用的组件:

<Parent>
  <Child value="1">
  <Child value="2">
</Parent>
Run Code Online (Sandbox Code Playgroud)

当然,父级和子级组件之间的渲染有一个逻辑,你可以想象<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>);
  }
});
Run Code Online (Sandbox Code Playgroud)

问题是每当你{this.props.children}用来定义包装器组件时,如何将一些属性传递给它的所有子组件?

Dom*_*nic 876

您可以使用React.Children迭代子项,然后使用React.cloneElement使用新的props(浅合并)克隆每个元素,例如:

const Child = ({ doSomething, value }) => (
  <div onClick={() => doSomething(value)}>Click Me</div>
);

class Parent extends React.PureComponent {
  doSomething = value => {
    console.log('doSomething called by child with value:', value);
  }

  render() {
    const childrenWithProps = React.Children.map(this.props.children, child =>
      React.cloneElement(child, { doSomething: this.doSomething })
    );

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

ReactDOM.render(
  <Parent>
    <Child value="1" />
    <Child value="2" />
  </Parent>,
  document.getElementById('container')
);
Run Code Online (Sandbox Code Playgroud)

小提琴:https://jsfiddle.net/2q294y43/2/

  • 这个答案不起作用,传递给`doSomething`的`value`丢失了. (10认同)
  • 这对我不起作用.这不是在React.cloneElement()中定义的 (6认同)
  • 如果孩子是通过从单独路线页面加载的路线(v4)加载的,该怎么办? (6认同)
  • @DominicTobias Arg,对不起,我将console.log切换到警报并忘记将两个参数连接成一个字符串. (3认同)

小智 359

要获得更简洁的方法,请尝试:

<div>
    {React.cloneElement(this.props.children, { loggedIn: this.state.loggedIn })}
</div>
Run Code Online (Sandbox Code Playgroud)

注意:这只有在有一个子节点时才有效,并且它是一个有效的React元素.

  • 确切地说,这似乎不适用于多个孩子. (50认同)
  • 所以你可以编写有效的代码,而有人只将一个孩子传递给一个组件,但是当他们添加另一个孩子时,它会崩溃......这在面值上听起来不是很好吗?这似乎是OP的一个陷阱,他特意要求将道具传递给_all_孩子. (17认同)
  • 有人可以解释这是如何工作的(或它实际上做了什么)?阅读[文档](https://facebook.github.io/react/docs/top-level-api.html#react.cloneelement),我无法看到这将如何下降到孩子们并添加该道具每个孩子 - 它的意图是什么?如果确实如此,我们怎么知道它会是什么呢?将一个不透明的数据结构(`this.props.children`)传递给`cloneElement` ...,这对于期望一个......元素来说甚至是有效的. (10认同)
  • @GreenAsJade只要你的组件期待一个孩子就可以了.您可以通过组件propTypes定义它期望单个子项.`React.Children.only`函数返回唯一的子节点或者如果有多个则抛出异常(如果没有用例则不存在). (10认同)
  • 我使用的是评分最高的答案,但这个答案更直接!该解决方案也是他们在react-router示例页面上使用的解决方案. (7认同)
  • 这是因为当只有一个孩子时,`this.props.children`只是一个React组件:https://facebook.github.io/react/tips/children-props-type.html (3认同)
  • Downvoted.它没有回答"如何将一些财产传递给_all_孩子?"的问题.它甚至没有提到如果你试图将它用于一个以上的孩子它会崩溃和燃烧的事实.173人怎么认为这是一个很好的答案? (3认同)
  • 由于@Søren Boisen 提到的同样原因而被否决。至少编辑你的答案并包括这样一个事实:你必须确保你的道具中只有一个子元素,例如在“propTypes”中检查它。 (2认同)

7pu*_*uns 75

试试这个

<div>{React.cloneElement(this.props.children, {...this.props})}</div>
Run Code Online (Sandbox Code Playgroud)

使用react-15.1对我有用.

  • 如果你需要明确强制你只接收一个孩子,你可以做`React.cloneElement(React.Children.only(this.props.children),{... this.props})`这将抛出一个错误,如果它被传递给不止一个孩子.然后你不需要包装div. (4认同)
  • 是否有可能直接返回`React.cloneElement()`而不在`<div>`标签中包围它?因为如果孩子是一个`<span>`(或其他东西)并且我们想保留它的标签元素类型怎么办? (3认同)
  • 对我有用。不包含 &lt;div&gt; 就可以了。 (2认同)
  • 这个答案可能会产生一个 TypeError:循环对象值。除非您希望子级的 props 之一是其本身,否则请使用“let {children, ...acyclalProps} = this.props”,然后使用“React.cloneElement(React.Children.only(children), acyclalProps)”。 (2认同)

Lyu*_*mir 61

传递道具指导孩子.

查看所有其他答案

通过上下文通过组件树传递共享的全局数据

Context旨在共享可被视为React组件树的"全局"数据,例如当前经过身份验证的用户,主题或首选语言.1

免责声明:这是一个更新的答案,前一个使用旧的上下文API

它基于消费者/提供原则.首先,创建您的上下文

const { Provider, Consumer } = React.createContext(defaultValue);
Run Code Online (Sandbox Code Playgroud)

然后使用via

<Provider value={/* some value */}>
  {children} /* potential consumers */
<Provider />
Run Code Online (Sandbox Code Playgroud)

<Consumer>
  {value => /* render something based on the context value */}
</Consumer>
Run Code Online (Sandbox Code Playgroud)

作为提供者后代的所有消费者将在提供者的价值支柱发生变化时重新呈现.从Provider到其后代使用者的传播不受shouldComponentUpdate方法的约束,因此即使祖先组件退出更新,Consumer也会更新. 1

完整的例子,半伪代码.

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>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

1 https://facebook.github.io/react/docs/context.html

  • 与接受的答案不同,即使在Parent下包含其他元素,这也将正常工作.这绝对是最好的答案. (6认同)
  • 道具!=背景 (5认同)

Ken*_*ong 43

通过对React 16.6的更新,您现在可以使用React.createContextcontextType.

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>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

React.createContext闪耀React.cloneElement案例无法处理嵌套组件的位置

class SomeComponent extends React.Component {

  render() {
    return (
      <Parent>
        <Child value={1} />
        <SomeOtherComp><Child value={2} /></SomeOtherComp>
      </Parent>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @itdoesntwork是不正确的。它仅在创建类时创建一个新函数。在渲染功能期间未创建它。 (4认同)
  • 您能解释为什么=&gt;函数是一种不好的做法吗?=&gt;函数有助于绑定事件处理程序以获取`this`上下文 (2认同)

Ali*_*eza 21

您可以使用React.cloneElement,在开始在应用程序中使用它之前,最好知道它是如何工作的.它已经介绍了React v0.13,请继续阅读以获取更多信息,以便为您完成这项工作:

<div>{React.cloneElement(this.props.children, {...this.props})}</div>
Run Code Online (Sandbox Code Playgroud)

因此,请参阅React文档中的内容,以了解它是如何工作的,以及如何使用它们:

在React v0.13 RC2中,我们将引入一个新的API,类似于React.addons.cloneWithProps,具有以下签名:

React.cloneElement(element, props, ...children);
Run Code Online (Sandbox Code Playgroud)

与cloneWithProps不同,这个新函数没有任何魔术内置行为来合并样式和className,原因与我们没有transferPropsTo的那个特性相同.没有人确定魔法事物的完整列表究竟是什么,这使得难以推断代码并且当样式具有不同的签名时难以重用(例如在即将推出的React Native中).

React.cloneElement几乎相当于:

<element.type {...element.props} {...props}>{children}</element.type>
Run Code Online (Sandbox Code Playgroud)

但是,与JSX和cloneWithProps不同,它还保留了refs.这意味着,如果你的孩子有一个参考,你不会意外地从你的祖先窃取它.您将获得与新元素相同的参考.

一种常见的模式是映射您的孩子并添加新的道具.报告了很多关于cloneWithProps丢失引用的问题,因此很难推断出你的代码.现在遵循与cloneElement相同的模式将按预期工作.例如:

var newChildren = React.Children.map(this.props.children, function(child) {
  return React.cloneElement(child, { foo: true })
});
Run Code Online (Sandbox Code Playgroud)

注意:React.cloneElement(child,{ref:'newRef'})DOES会覆盖ref,因此除非使用callback-refs,否则两个父级仍然无法对同一个子进行引用.

这是进入React 0.13的关键特性,因为道具现在是不可变的.升级路径通常是克隆元素,但这样做可能会丢失引用.因此,我们需要一个更好的升级路径.当我们在Facebook上升级callites时,我们意识到我们需要这种方法.我们从社区获得了相同的反馈.因此,我们决定在最终版本发布之前制作另一个RC,以确保我们能够获得此版本.

我们计划最终弃用React.addons.cloneWithProps.我们还没有这样做,但这是一个开始考虑自己的用途并考虑使用React.cloneElement的好机会.在我们实际删除之前,我们一定会发布带有弃用通知的版本,因此不需要立即采取措施.

在这里 ...


Ben*_*arp 14

方法 1 - 克隆孩子

const Parent = (props) => {
   const attributeToAddOrReplace= "Some Value"
   const childrenWithAdjustedProps = React.Children.map(props.children, child =>
      React.cloneElement(child, { attributeToAddOrReplace})
   );

   return <div>{childrenWithAdjustedProps }</div>
}
Run Code Online (Sandbox Code Playgroud)

完整演示

方法 2 - 使用可组合上下文

Context 允许您将 prop 传递给深层子组件,而无需通过中间的组件将其作为 prop 显式传递。

上下文有缺点:

  1. 数据不会以常规方式流动——通过 props。
  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>;
};

Run Code Online (Sandbox Code Playgroud)


小智 12

允许您进行财产转移的最佳方法children就像一个函数

例:

export const GrantParent = () => {
  return (
    <Parent>
      {props => (
        <ChildComponent {...props}>
          Bla-bla-bla
        </ChildComponent>
      )}
    </Parent>
  )
}

export const Parent = ({ children }) => {
    const somePropsHere = { //...any }
    <>
        {children(somePropsHere)}
    </>
}
Run Code Online (Sandbox Code Playgroud)

  • 对我来说,这似乎比公认的答案更直接(而且性能更好?)。 (2认同)
  • 这要求子级成为函数,不适用于深度嵌套的组件 (2认同)
  • 你是对的,深层嵌套的孩子的情况也可以处理`&lt;Parent&gt;{props =&gt; &lt;Nest&gt;&lt;ChildComponent /&gt;&lt;/Nest&gt;}&lt;/Parent&gt;` 而不是(不工作)`&lt;Parent&gt;&lt; Nest&gt;{props =&gt; &lt;ChildComponent /&gt;}&lt;/Nest&gt;&lt;/Parent&gt;` 所以我同意这是最好的答案 (2认同)

ole*_*nak 6

我需要修复上面接受的答案,以使其使用答案而不是指针。在map函数范围内,函数未定义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>
}})
Run Code Online (Sandbox Code Playgroud)

更新:此修补程序适用于ECMAScript 5,在ES6中,不需要var that = this

  • 或只使用`bind()` (13认同)
  • @ plus-我知道这很旧,但是在这里使用bind是一个糟糕的主意,bind创建了一个将上下文绑定到新函数的新函数。基本上是一个调用`apply`方法的函数。每次调用render方法时,在render函数中使用`bind()`都会创建一个新函数。 (4认同)

yea*_*yer 6

你不再需要{this.props.children}.现在,您可以使用renderin 包装子组件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>
Run Code Online (Sandbox Code Playgroud)

  • 这是怎么回答这个问题的? (16认同)
  • 渲染道具现在是React(https://reactjs.org/docs/render-props.html)中的标准,并且值得考虑作为此问题的新接受答案. (2认同)

小智 6

考虑一个或多个孩子的更清洁方式

<div>
   { React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
Run Code Online (Sandbox Code Playgroud)


Nes*_*ric 6

如果你有多个想要传递 props 的孩子,你可以使用 React.Children.map 这样做:

render() {
    let updatedChildren = React.Children.map(this.props.children,
        (child) => {
            return React.cloneElement(child, { newProp: newProp });
        });

    return (
        <div>
            { updatedChildren }
        </div>
    );
}
Run Code Online (Sandbox Code Playgroud)

如果您的组件只有一个子组件,则无需映射,您可以直接使用 cloneElement:

render() {
    return (
        <div>
            {
                React.cloneElement(this.props.children, {
                    newProp: newProp
                })
            }
        </div>
    );
}
Run Code Online (Sandbox Code Playgroud)


Eso*_*ack 6

这是我的版本,适用于单个、多个和无效的孩子。

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);
  }
};
Run Code Online (Sandbox Code Playgroud)

使用示例:

https://codesandbox.io/s/loving-mcclintock-59emq?file=/src/ChildVsChildren.jsx:0-1069


Mak*_*min 5

父.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>
);
Run Code Online (Sandbox Code Playgroud)

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)}/>
);
Run Code Online (Sandbox Code Playgroud)

和 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('...')
);
Run Code Online (Sandbox Code Playgroud)

请参阅此处的示例:https : //plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview


sid*_*son 5

除了@and_rest 答案之外,这就是我克隆子级并添加一个类的方法。

<div className="parent">
    {React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
</div>
Run Code Online (Sandbox Code Playgroud)


小智 5

没有一个答案解决拥有 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>

}
Run Code Online (Sandbox Code Playgroud)


小智 5

也许您还可以发现此功能很有用,尽管许多人认为这是一种反模式,但如果您知道自己在做什么并很好地设计了解决方案,那么它仍然可以使用。

作为子组件的函数


Ze *_*eus 5

我认为渲染道具是处理这种情况的适当方法

您可以通过重构父级代码以使其看起来像这样,让父级提供子组件中使用的必要道具:

const Parent = ({children}) => {
  const doSomething(value) => {}

  return children({ doSomething })
}
Run Code Online (Sandbox Code Playgroud)

然后在子组件中您可以通过以下方式访问父组件提供的功能:

class Child extends React {

  onClick() => { this.props.doSomething }

  render() { 
    return (<div onClick={this.onClick}></div>);
  }

}
Run Code Online (Sandbox Code Playgroud)

现在 fiianl 结构将如下所示:

<Parent>
  {(doSomething) =>
   (<Fragment>
     <Child value="1" doSomething={doSomething}>
     <Child value="2" doSomething={doSomething}>
    <Fragment />
   )}
</Parent>
Run Code Online (Sandbox Code Playgroud)


小智 5

受到上述所有答案的启发,这就是我所做的。我正在传递一些道具,如一些数据和一些组件。

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}</>;
};

Run Code Online (Sandbox Code Playgroud)