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

IT技术 javascript reactjs react-jsx
2021-02-07 05:17:04

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

<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}定义一个包装组件时,你如何将一些属性传递给它的所有子组件?

6个回答

用新props克隆孩子

您可以使用React.Children迭代子项,然后使用新的props(浅合并)克隆每个元素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>

将孩子作为函数调用

或者,您可以使用render props 将 props传递给孩子在这种方法中,子项(可以是children或任何其他props名称)是一个可以接受您想要传递的任何参数并返回子项的函数:

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>或简单地<>返回一个数组。

这对我不起作用。这不是在 React.cloneElement() 中定义的
2021-03-10 05:17:04
这个答案不起作用,value传递给doSomething丢失了。
2021-03-12 05:17:04
如果通过从单独的路由页面加载的路由 (v4) 加载子级怎么办?
2021-03-14 05:17:04
@DominicTobias Arg,抱歉,我将 console.log 切换为 alert 并且忘记将两个参数连接到一个字符串。
2021-03-18 05:17:04
这个答案非常有帮助,但我遇到了一个这里没有提到的问题,我想知道它是否是一些改变的新事物,或者它是否对我来说很奇怪。当我克隆我的子元素时,它的子元素被设置为旧元素,直到我将 this.props.children.props.children 添加到 cloneElement 的第三个参数。
2021-03-20 05:17:04

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

<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>
确切地说,这似乎不适用于不止一个孩子。
2021-03-10 05:17:04
有人可以解释这是如何工作的(或它实际上是做什么的)?阅读文档,我看不出这会如何影响孩子并将该道具添加到每个孩子 - 这就是它的意图吗?如果是这样,我们怎么知道它会做什么?将一个不透明的数据结构 ( this.props.children) 传递给cloneElement... 期待一个 ... 元素甚至是有效的,这一点并不明显
2021-03-19 05:17:04
@GreenAsJade 只要您的组件期待一个孩子就可以了。你可以通过你的组件 propTypes 定义它期望一个孩子。React.Children.only如果有多个,函数返回唯一的孩子或抛出异常(如果没有用例,这将不存在)。
2021-03-22 05:17:04
因此,您可以编写代码,当有人只将一个子组件传递给组件时,该代码仍然有效,但是当他们添加另一个子组件时,它会崩溃……从表面上看,这听起来不太好?这似乎是 OP 的一个陷阱,他专门询问了将道具传递给所有孩子的问题。
2021-03-23 05:17:04
我使用的是评分最高的答案,但这个答案更直接!这个解决方案也是他们在 react-router 示例页面上使用的。
2021-04-08 05:17:04

尝试这个

<div>{React.cloneElement(this.props.children, {...this.props})}</div>

它使用 react-15.1 对我有用。

{...this.props}https://reactjs.org/docs/jsx-in-depth.html#spread-attributes 中建议使用

这个答案可能会产生一个 TypeError: 循环对象值。除非您希望孩子的道具之一成为它自己,否则请使用let {children, ...acyclicalProps} = this.props和 然后React.cloneElement(React.Children.only(children), acyclicalProps)
2021-03-19 05:17:04
如果您需要明确强制您只接收一个孩子,您可以这样做React.cloneElement(React.Children.only(this.props.children), {...this.props}),如果它传递了多个孩子,则会引发错误。那么你不需要用一个 div 包装。
2021-03-23 05:17:04
是否可以React.cloneElement()直接返回而不用<div>标签包围它因为如果孩子是一个<span>(或其他东西)并且我们想要保留它的标签元素类型怎么办?
2021-03-30 05:17:04
如果它是一个孩子,您可以省略包装器,并且此解决方案仅适用于一个孩子,所以是的。
2021-03-31 05:17:04
为我工作。不包含 <div> 就可以了。
2021-04-07 05:17:04

将props传递给直接的孩子。

查看所有其他答案

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

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

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

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

const { Provider, Consumer } = React.createContext(defaultValue);

然后使用通过

<Provider value={/* some value */}>
  {children} /* potential consumers */
</Provider>

<Consumer>
  {value => /* render something based on the context value */}
</Consumer>

每当 Provider 的 value 属性发生变化时,所有作为 Provider 后代的 Consumer 都会重新渲染。从 Provider 到其后代 Consumer 的传播不受 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>
    );
  }
}

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

你完全理解,这是不正确的。我已经编辑了我的答案。
2021-03-14 05:17:04
道具 != 上下文
2021-03-22 05:17:04
你不能依赖于通过上下文传播的变化。在道具可能发生变化时使用它们。
2021-03-26 05:17:04
也许我不明白,但说“上下文使道具可用”不是错误吗?当我上次使用上下文时,它是一个单独的东西(即this.context)——它没有神奇地将上下文与道具合并。你必须有意识地设置和使用上下文,这是另一回事。
2021-03-29 05:17:04
与接受的答案不同,即使父项下包含其他元素,这也能正常工作。这绝对是最好的答案。
2021-04-01 05:17:04

将props传递给嵌套的孩子

随着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>
    );
  }
}

React.createContextReact.cloneElement案例无法处理嵌套组件的地方大放异彩

class SomeComponent extends React.Component {

  render() {
    return (
      <Parent>
        <Child value={1} />
        <SomeOtherComp><Child value={2} /></SomeOtherComp>
      </Parent>
    );
  }
}
你能解释为什么 => 函数是一种不好的做法吗?=> 函数有助于绑定事件处理程序以获取this上下文
2021-03-17 05:17:04
@KennethTruong 因为每次渲染它都会创建一个函数。
2021-03-17 05:17:04
@itdoesntwork 那不是真的。它仅在创建类时创建一个新函数。它不是在渲染功能期间创建的。
2021-03-18 05:17:04
@KennethTruong reactjs.org/docs/faq-functions.html#arrow-function-in-render我以为你在谈论渲染中的箭头函数。
2021-03-20 05:17:04