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

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


当前回答

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

其他回答

允许你进行属性转移的最好方法是像函数模式一样的子元素 https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9

代码片段:https://stackblitz.com/edit/react-fcmubc

例子:

const Parent = ({ children }) => {
    const somePropsHere = {
      style: {
        color: "red"
      }
      // any other props here...
    }
    return children(somePropsHere)
}

const ChildComponent = props => <h1 {...props}>Hello world!</h1>

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

如果有人想知道如何在有一个或多个子节点的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,甚至允许您根据需要将属性传递给子组件。

给孩子们传递道具。

查看所有其他答案

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

Context被设计用来共享React组件树的“全局”数据,比如当前认证的用户、主题或首选语言。1

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

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

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

然后使用via

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

and

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

当提供者的值道具发生变化时,提供者的所有后代消费者都将重新呈现。从提供者到其后代消费者的传播不受shouldComponentUpdate方法的约束,因此即使在祖先组件退出更新时,消费者也会被更新。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

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

作为子组件的功能

用新道具克隆孩子

你可以使用React。子元素遍历子元素,然后使用React.cloneElement用新道具(浅合并)克隆每个元素。

请参阅代码注释,为什么我不推荐这种方法。

const Child = ({childName, sayHello}) => ( <button onClick={() => sayHello(childName)}>{childName}</button> ); 函数父({children}) { //我们将sayHello函数传递给子元素。 函数sayHello(childName) { console.log(' Hello from ${childName} the child '); } const childrenWithProps = React.Children。映射(children, child => { //检查isValidElement是安全的方法,可以避免 // typescript错误。 if (React.isValidElement(child)) { 返回的反应。cloneElement(child, {sayHello}); } 返回的孩子; }); 返回< div > {childrenWithProps} < / div > } 函数App() { //这种方法不太类型安全,Typescript不友好 //看起来像你试图渲染' Child '没有' sayHello '。 //这也会让代码的读者感到困惑。 回报( <父> <Child childName="Billy" /> <Child childName="Bob" /> 父> < / ); } ReactDOM。render(<App />, document.getElementById("container")); < script src = " https://unpkg.com/react@17 umd格式/ react.production.min.js " > < /脚本> < script src = " https://unpkg.com/react-dom@17 umd格式/ react-dom.production.min.js " > < /脚本> < div id = "容器" > < / div >

将子函数作为函数调用

或者,你可以通过渲染道具将道具传递给孩子们。在这种方法中,children(可以是children或任何其他道具名)是一个函数,它可以接受你想传递的任何参数,并返回实际的子函数:

const Child = ({childName, sayHello}) => ( <button onClick={() => sayHello(childName)}>{childName}</button> ); 函数父({children}) { 函数sayHello(childName) { console.log(' Hello from ${childName} the child '); } //这个组件的children必须是一个函数 //返回实际的子节点。我们可以通过 //它的参数然后传递给他们作为道具(在这个 //如果我们传递' sayHello ')。 返回< div >{孩子(sayHello)} < / div > } 函数App() { // sayHello是我们在Parent中传递的参数 //我们现在传递到Child。 回报( <父> {(sayHello) => ( <反应。片段> <Child childName="Billy" sayHello={sayHello} /> <Child childName="Bob" sayHello={sayHello} /> < /反应。片段> )} 父> < / ); } ReactDOM。render(<App />, document.getElementById("container")); < script src = " https://unpkg.com/react@17 umd格式/ react.production.min.js " > < /脚本> < script src = " https://unpkg.com/react-dom@17 umd格式/ react-dom.production.min.js " > < /脚本> < div id = "容器" > < / div >