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

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


当前回答

下面是我的版本,适用于单个、多个和无效的子节点。

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中传递子元素作为道具。

例1:

function Parent({ChildElement}){
   return <ChildElement propName={propValue} />
}

return <Parent ChildElement={ChildComponent}/>

将子函数作为函数传递

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

根据cloneElement()的文档

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

克隆并返回一个新的React元素,使用element作为起始元素 点。生成的元素将具有原始元素的道具 新道具浅浅地融合在一起。新的孩子将取代 现有的儿童。原元素的Key和ref将为 保存了下来。 React.cloneElement()几乎等同于: <元素。{…元素类型。道具道具}{…}>{孩子}< / element.type > 然而,它也保留了裁判。这意味着如果你有了孩子 有裁判在上面,你就不会不小心从你的祖先那里偷了它。 你会得到相同的引用附加到你的新元素。

因此,您将使用cloneElement为儿童提供自定义道具。然而,组件中可能有多个子组件,您需要循环遍历它。其他答案建议你使用react。children。map来映射它们。然而React. children .map不同于React。cloneElement更改Element附加项和额外的。$作为前缀的键值。查看这个问题了解更多细节:React。React.Children.map中的cloneElement会导致元素键的改变

如果您希望避免它,您应该改用forEach函数,如

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

}

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

作为子组件的功能

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

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

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.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>