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

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


当前回答

这个答案是w.r.t. React v17.x…

使用子函数作为一个函数,并将道具传递给它作为渲染道具模式,如下所示

 <ParentComponent {...anyAdditionalProps}>
   {
     (actualPropsToPass) => <ChildComponent>{children(actualPropsToPass)}</ChildComponent>
   }
 </ParentComponent>

只是要确保,实际的,要投影的内容必须像渲染道具模式中的函数一样添加,以适应在子函数中作为道具传递的参数。

其他回答

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

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

你不再需要{this.props.children}。现在你可以在Route中使用render来包装你的子组件,并像往常一样传递你的道具:

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

一些原因。孩子不适合我。这对我来说很管用。

我只想给这个child添加一个类。类似于更换道具

 var newChildren = this.props.children.map((child) => {
 const className = "MenuTooltip-item " + child.props.className;
    return React.cloneElement(child, { className });
 });

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

这里的技巧是React.cloneElement。你可以用类似的方式通过任何道具

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

如果有人想知道如何在有一个或多个子节点的TypeScript中正确地做到这一点。我使用uuid库为子元素生成唯一的键属性,当然,如果只克隆一个元素,则不需要这些属性。

export type TParentGroup = {
  value?: string;
  children: React.ReactElement[] | React.ReactElement;
};

export const Parent = ({
  value = '',
  children,
}: TParentGroup): React.ReactElement => (
  <div className={styles.ParentGroup}>
    {Array.isArray(children)
      ? children.map((child) =>
          React.cloneElement(child, { key: uuidv4(), value })
        )
      : React.cloneElement(children, { value })}
  </div>
);

如您所见,此解决方案负责呈现ReactElement的数组或单个ReactElement,甚至允许您根据需要将属性传递给子组件。