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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何将道具传递给{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}定义包装器组件,如何将一些属性传递给它的所有子组件?

答案

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

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

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

您还可以使用render props将道具传递给儿童。

另一答案

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

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

这些答案都没有解决让孩子成为非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>

}
另一答案

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

另一答案

根据cloneElement()的文件

React.cloneElement(
  element,
  [props],
  [...children]
)

使用element作为起点克隆并返回一个新的React元素。结果元素将具有原始元素的道具,新道具以浅层方式合并。新的孩子将取代现有的孩子。 key和ref将保留原始元素。

React.cloneElement()几乎相当于:

<element.type {...element.props} {...props}>{children}</element.type>

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

因此,cloneElement将用于为子项提供自定义道具。但是,组件中可能有多个子项,您需要循环它。其他答案建议您使用React.Children.map映射它们。然而,与React.Children.map不同的React.cloneElement更改了Element附加的键和额外的.$作为前缀。查看此问题以获取更多详细信息:React.cloneElement inside React.Children.map is causing element keys to change

如果你想避免它,你应该去像forEach函数

render() {
    const newElements = [];
    React.Children.forEach(this.props.children, 
              child => newElements.push(
                 React.cloneElement(
                   child, 
                   {...this.props, ...customProps}
                )
              )
    )
    return (
        <div>{newElements}</div>
    )

}
另一答案

也许你也可以找到有用的这个功能,虽然很多人都认为这是一种反模式,如果你知道自己在做什么并设计好你的解决方案,它仍然可以使用。

Function as Child Components

另一答案

如果你有多个孩子想要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>
    );
}

如果你的组件只有一个孩子,则不需要映射,你可以立即克隆元素:

render() {
    return (
        <div>
            {
                React.cloneElement(this.props.children, {
                    newProp: newProp
                })
            }
        </div>
    );
}
另一答案

最简洁的方法:

    {React.cloneElement(this.props.children, this.props)}
另一答案

继@and_rest回答之后,这就是我克隆孩子并添加一个类的方法。

<div className="parent">
    {React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
</div>
另一答案

对于任何一个拥有单个子元素的人来说,这应该是这样做的。

{React.isValidElement(this.props.children)
                  ? React.cloneElement(this.props.children, {
                      ...prop_you_want_to_pass
                    })
                  : null}
另一答案

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

您让Parent提供子组件中使用的必要道具,通过重构Parent代码来查找如下所示:

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

  return children({ doSomething })
}

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

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>
另一答案

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

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

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

另一答案

Method 1 - clone children

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

   return <div>{childrenWithAdjustedProps }</div>
}
以上是关于如何将道具传递给{this.props.children}的主要内容,如果未能解决你的问题,请参考以下文章

如何将道具传递给递归子组件并保留所有道具

如何将道具传递给组件的故事?

如何防止将道具传递给内部样式组件

如何将组件中的道具传递给 FlatList renderItem

如何将 Django 变量传递给 Vue 道具

如何将道具传递给来自不同来源的组件?