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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32370994/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 07:58:19  来源:igfitidea点击:

How to pass props to {this.props.children}

javascriptreactjsreact-jsx

提问by plus-

I'm trying to find the proper way to define some components which could be used in a generic way:

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

<Parent>
  <Child value="1">
  <Child value="2">
</Parent>

There is a logic going on for rendering between parent and children components of course, you can imagine <select>and <option>as an example of this logic.

当然,在父组件和子组件之间渲染有一个逻辑,你可以想象<select><option>作为这个逻辑的一个例子。

This is a dummy implementation for the purpose of the question:

这是出于问题目的的虚拟实现:

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

The question is whenever you use {this.props.children}to define a wrapper component, how do you pass down some property to all its children?

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

回答by Dominic

Cloning children with new props

用新道具克隆孩子

You can use React.Childrento iterate over the children, and then clone each element with new props (shallow merged) using React.cloneElemente.g:

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

import React, { Children, isValidElement, cloneElement } from 'react';

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

function Parent({ children }) {
  function doSomething(value) {
    console.log('doSomething called by child with value:', value);
  }

  render() {
    const childrenWithProps = Children.map(children, child => {
      // Checking isValidElement is the safe way and avoids a TS error too.
      if (isValidElement(child)) {
        return cloneElement(child, { doSomething })
      }

      return child;
    });

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

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

Fiddle: https://jsfiddle.net/2q294y43/2/

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

Calling children as a function

将孩子作为函数调用

You can also pass props to children with render props. In this approach the children (which can be childrenor any other prop name) is a function which can accept any arguments you want to pass and returns the children:

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

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

function Parent({ children }) {
  function doSomething(value) {
    console.log('doSomething called by child with value:', value);
  }

  render() {
    // Note that children is called as a function and we can pass args to it
    return <div>{children(doSomething)}</div>
  }
};

ReactDOM.render(
  <Parent>
    {doSomething => (
      <React.Fragment>
        <Child doSomething={doSomething} value="1" />
        <Child doSomething={doSomething} value="2" />
      </React.Fragment>
    )}
  </Parent>,
  document.getElementById('container')
);

Instead of <React.Fragment>or simply <>you can also return an array if you prefer.

如果您愿意,您也可以代替<React.Fragment>或简单地<>返回一个数组。

Fiddle: https://jsfiddle.net/ferahl/y5pcua68/7/

小提琴:https: //jsfiddle.net/ferahl/y5pcua68/7/

回答by Andres F Garcia

For a slightly cleaner way to do it, try:

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

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

Edit: To use with multiple individual children (the child must itself be a component) you can do. Tested in 16.8.6

编辑:要与多个单独的孩子一起使用(孩子本身必须是一个组件),您可以这样做。在 16.8.6 中测试

<div>
    {React.cloneElement(props.children[0], { loggedIn: true, testingTwo: true })}
    {React.cloneElement(props.children[1], { loggedIn: true, testProp: false })}
</div>

回答by 7puns

Try this

尝试这个

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

It worked for me using react-15.1.

它使用 react-15.1 对我有用。

回答by Lyubomir

Pass props to direct children.

将道具传递给指导儿童。

See all other answers

查看所有其他答案

Pass shared, global data through the component tree via 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

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

Disclaimer: This is an updated answer, the previous one used the old context API

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

It is based on Consumer / Provide principle. First, create your context

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

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

Then use via

然后使用通过

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

and

<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

每当 Provider 的 value 属性发生变化时,所有作为 Provider 后代的 Consumer 都会重新渲染。从 Provider 到其后代 Consumer 的传播不受 shouldComponentUpdate 方法的影响,因此即使祖先组件退出更新,Consumer 也会更新。1

Full example, semi-pseudo code.

完整示例,半伪代码。

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

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

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

回答by Kenneth Truong

Passing Props to Nested Children

将道具传递给嵌套的孩子

With the update to React 16.6you can now use React.createContextand contextType.

随着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.createContextshines where React.cloneElementcase couldn't handle nested components

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

class SomeComponent extends React.Component {

  render() {
    return (
      <Parent>
        <Child value={1} />
        <SomeOtherComp><Child value={2} /></SomeOtherComp>
      </Parent>
    );
  }
}

回答by Alireza

You can use React.cloneElement, it's better to know how it works before you start using it in your application. It's introduced in React v0.13, read on for more information, so something along with this work for you:

您可以使用React.cloneElement,最好在开始在应用程序中使用它之前了解它的工作原理。它在 中介绍React v0.13,请继续阅读以获取更多信息,因此与这项工作一起为您准备了一些东西:

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

So bring the lines from React documentation for you to understand how it's all working and how you can make use of them:

因此,请从 React 文档中获取这些内容,以了解它是如何工作的以及如何使用它们:

In React v0.13 RC2 we will introduce a new API, similar to React.addons.cloneWithProps, with this signature:

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

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:

与 cloneWithProps 不同,这个新函数没有任何用于合并 style 和 className 的神奇内置行为,原因与 transferPropsTo 中没有该功能的原因相同。没有人确定魔法事物的完整列表到底是什么,这使得代码难以推理,并且当样式具有不同的签名时(例如在即将到来的 React Native 中)难以重用。

React.cloneElement 几乎相当于:

<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:

但是,与 JSX 和 cloneWithProps 不同的是,它还保留了 refs。这意味着如果你得到一个带有 ref 的孩子,你不会意外地从你的祖先那里偷走它。您将获得附加到新元素的相同参考。

一种常见的模式是映射你的孩子并添加一个新道具。有很多关于 cloneWithProps 丢失引用的问题报告,这使得你的代码更难推理。现在遵循与 cloneElement 相同的模式将按预期工作。例如:

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.

注意:React.cloneElement(child, { ref: 'newRef' }) 确实会覆盖 ref,因此两个父级仍然不可能拥有对同一个子级的 ref,除非您使用回调引用。

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

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

more here...

更多在这里...

回答by Nick Ovchinnikov

The best way, which allows you to make property transfer is childrenlike a function

最好的方法,它允许你进行财产转移children就像一个函数

Example:

例子:

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

export const Parent = ({ children }) => {
    const somePropsHere = { //...any }
    <>
        {children(somePropsHere)}
    </>
}

回答by and_rest

Cleaner way considering one or more children

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

<div>
   { React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>

回答by olenak

I needed to fix accepted answer above to make it work using thatinstead of thispointer. Thiswithin the scope of map function didn't have doSomethingfunction defined.

我需要修复上面接受的答案以使其使用而不是这个指针工作。在 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>
}})

Update: this fix is for ECMAScript 5, in ES6 there is no need in var that=this

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

回答by yeasayer

You no longer need {this.props.children}. Now you can wrap your child component using renderin Routeand pass your props as usual:

你不再需要{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>