如何将道具传递给{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}定义包装器组件时,如何将某些属性传递给其所有子组件?

Tony村村2020/03/09 22:32:40

原因React.children不为我工作。这对我有用。

我只想给孩子增加一堂课。类似于更换道具

 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您可以通过类似的方式传递任何道具

斯丁Davaid2020/03/09 22:32:40

渲染道具是解决此问题的最准确方法。与其将子组件作为子道具传递给父组件,不如让父组件手动渲染子组件。Render是内置的props在react中,它带有功能参数。在此函数中,您可以使用自定义参数让父组件呈现任何所需内容。基本上,它与儿童道具具有相同的功能,但是更可定制。

class Child extends React.Component {
  render() {
    return <div className="Child">
      Child
      <p onClick={this.props.doSomething}>Click me</p>
           {this.props.a}
    </div>;
  }
}

class Parent extends React.Component {
  doSomething(){
   alert("Parent talks"); 
  }

  render() {
    return <div className="Parent">
      Parent
      {this.props.render({
        anythingToPassChildren:1, 
        doSomething: this.doSomething})}
    </div>;
  }
}

class Application extends React.Component {
  render() {
    return <div>
      <Parent render={
          props => <Child {...props} />
        }/>
    </div>;
  }
}

Codepen示例

米亚神奇2020/03/09 22:32:40

这是您所需要的吗?

var Parent = React.createClass({
  doSomething: function(value) {
  }
  render: function() {
    return  <div>
              <Child doSome={this.doSomething} />
            </div>
  }
})

var Child = React.createClass({
  onClick:function() {
    this.props.doSome(value); // doSomething is undefined
  },  
  render: function() {
    return  <div onClick={this.onClick}></div>
  }
})
猴子小宇宙2020/03/09 22:32:40

最精巧的方法是:

    {React.cloneElement(this.props.children, this.props)}
乐米亚2020/03/09 22:32:40

对于任何具有单个子元素的人,都应该这样做。

{React.isValidElement(this.props.children)
                  ? React.cloneElement(this.props.children, {
                      ...prop_you_want_to_pass
                    })
                  : null}
乐猪猪2020/03/09 22:32:40

方法1-克隆孩子

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

   return <div>{childrenWithAdjustedProps }</div>
}

方法2-使用可组合上下文

上下文允许您将prop传递给深子组件,而无需将其作为prop显式传递给中间的组件。

上下文具有缺点:

  1. 数据不是以常规方式流动-通过道具。
  2. 使用上下文会在消费者和提供者之间建立契约。理解和复制重用组件所需的需求可能会更加困难。

使用可组合的上下文

export const Context = createContext<any>(null);

export const ComposableContext = ({ children, ...otherProps }:{children:ReactNode, [x:string]:any}) => {
    const context = useContext(Context)
    return(
      <Context.Provider {...context} value={{...context, ...otherProps}}>{children}</Context.Provider>
    );
}

function App() {
  return (
      <Provider1>
            <Provider2> 
                <Displayer />
            </Provider2>
      </Provider1>
  );
}

const Provider1 =({children}:{children:ReactNode}) => (
    <ComposableContext greeting="Hello">{children}</ComposableContext>
)

const Provider2 =({children}:{children:ReactNode}) => (
    <ComposableContext name="world">{children}</ComposableContext>
)

const Displayer = () => {
  const context = useContext(Context);
  return <div>{context.greeting}, {context.name}</div>;
};

小宇宙路易2020/03/09 22:32:40

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

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

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>
达蒙西里2020/03/09 22:32:40

根据文档 cloneElement()

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

克隆并使用element作为起点返回一个新的React元素。生成的元素将具有原始元素的道具,而新的道具将被浅层合并。新的孩子将替换现有的孩子。原始元素的key和ref将被保留。

React.cloneElement() 几乎等同于:

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

但是,它也保留引用。这意味着,如果您得到一个带有裁判的孩子,就不会意外地从祖先那里偷走它。您将获得与新元素相同的引用。

因此,cloneElement是用来为子项提供自定义道具的东西。但是,组件中可以有多个子代,您需要对其进行循环。其他答案建议您使用来映射它们React.Children.map但是,React.Children.mapReact.cloneElement更改元素的键不同,附加键和附加键.$作为前缀。检查此问题以获取更多详细信息:React.Children.map中的React.cloneElement导致元素键更改

如果您希望避免这种情况,则应改用forEach类似

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

}
Stafan路易2020/03/09 22:32:40

如果您有多个孩子想要传递道具,则可以使用React.Children.map这样实现:

render() {
    let updatedChildren = React.Children.map(this.props.children,
        (child) => {
            return React.cloneElement(child, { newProp: newProp });
        });

    return (
        <div>
            { updatedChildren }
        </div>
    );
}

如果您的组件只有一个孩子,则无需进行映射,您可以直接克隆cloneElement:

render() {
    return (
        <div>
            {
                React.cloneElement(this.props.children, {
                    newProp: newProp
                })
            }
        </div>
    );
}
蛋蛋L2020/03/09 22:32:40

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

用作子组件

樱GO2020/03/09 22:32:40

您不再需要{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>
神无达蒙JinJin2020/03/09 22:32:40

没有一个答案解决拥有 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>

}
斯丁Jim2020/03/09 22:32:40

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

<div>
   { React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
村村AL2020/03/09 22:32:40

允许您进行财产转移的最佳方法children就像一个函数

例:

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

export const Parent = ({ children }) => {
    const somePropsHere = { //...any }
    <>
        {children(somePropsHere)}
    </>
}
村村伽罗Mandy2020/03/09 22:32:40

尝试这个

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

它使用react-15.1为我工作。

卡卡西路易前端2020/03/09 22:32:40

对于更简洁的方法,请尝试:

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

编辑:可以与多个单独的子项(子项本身必须是组件)一起使用。在16.8.6中测试

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