简体   繁体   English

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

[英]How to pass props to {this.props.children}

I'm trying to find the proper way to define some components which could be used in a generic way:我试图找到正确的方法来定义一些可以以通用方式使用的组件:

<Parent>
  <Child value="1">
  <Child value="2">
</Parent>

There is a logic going on for rendering between parent and children components of course, you can imagine <select> and <option> as an example of this logic.当然,父子组件之间的渲染是有逻辑的,你可以把<select><option>想象成这个逻辑的一个例子。

This is a dummy implementation for the purpose of the question:出于问题的目的,这是一个虚拟实现:

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

The question is whenever you use {this.props.children} to define a wrapper component, how do you pass down some property to all its children?问题是,每当您使用{this.props.children}定义包装组件时,如何将某些属性传递给它的所有子组件?

Cloning children with new props用新道具克隆孩子

You can use React.Children to iterate over the children, and then clone each element with new props (shallow merged) using React.cloneElement .您可以使用React.Children遍历子元素,然后使用React.cloneElement使用新的道具(浅合并)克隆每个元素。 For example:例如:

 const Child = ({ doSomething, value }) => ( <button onClick={() => doSomething(value)}>Click Me</button> ); function Parent({ children }) { function doSomething(value) { console.log("doSomething called by child with value:", value); } const childrenWithProps = React.Children.map(children, child => { // Checking isValidElement is the safe way and avoids a typescript // error too. if (React.isValidElement(child)) { return React.cloneElement(child, { doSomething }); } return child; }); return <div>{childrenWithProps}</div> } function App() { return ( <Parent> <Child value={1} /> <Child value={2} /> </Parent> ); } ReactDOM.render(<App />, document.getElementById("container"));
 <script src="https://unpkg.com/react@17/umd/react.production.min.js"></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script> <div id="container"></div>

Calling children as a function将孩子作为函数调用

Alternatively, you can pass props to children with render props .或者,您可以使用render props 将 props传递给孩子。 In this approach, the children (which can be children or any other prop name) is a function which can accept any arguments you want to pass and returns the children:在这种方法中,孩子(可以是children或任何其他道具名称)是一个可以接受您想要传递的任何参数并返回孩子的函数:

 const Child = ({ doSomething, value }) => ( <button onClick={() => doSomething(value)}>Click Me</button> ); function Parent({ children }) { function doSomething(value) { console.log("doSomething called by child with value:", value); } // Note that children is called as a function and we can pass args to it. return <div>{children(doSomething)}</div> } function App() { // doSomething is the arg we passed in Parent, which // we now pass through to Child. return ( <Parent> {doSomething => ( <React.Fragment> <Child doSomething={doSomething} value={1} /> <Child doSomething={doSomething} value={2} /> </React.Fragment> )} </Parent> ); } ReactDOM.render(<App />, document.getElementById("container"));
 <script src="https://unpkg.com/react@17/umd/react.production.min.js"></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script> <div id="container"></div>

Instead of <React.Fragment> or simply <> you can also return an array if you prefer.如果您愿意,也可以返回一个数组,而不是<React.Fragment>或简单的<>

For a slightly cleaner way to do it, try:要获得更清洁的方法,请尝试:

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

Edit: To use with multiple individual children (the child must itself be a component) you can do.编辑:要与多个单独的孩子一起使用(孩子本身必须是一个组件),您可以这样做。 Tested in 16.8.6在 16.8.6 中测试

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

Try this尝试这个

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

It worked for me using react-15.1.使用 react-15.1 对我有用。

Use {...this.props} is suggested in https://reactjs.org/docs/jsx-in-depth.html#spread-attributeshttps://reactjs.org/docs/jsx-in-depth.html#spread-attributes中建议使用{...this.props}

Pass props to direct children.将道具传递给指导孩子。

See all other answers查看所有其他答案

Pass shared, global data through the component tree via context通过上下文通过组件树传递共享的全局数据

Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language. Context 旨在共享可被视为 React 组件树“全局”的数据,例如当前经过身份验证的用户、主题或首选语言。 1 1

Disclaimer: This is an updated answer, the previous one used the old context API免责声明:这是一个更新的答案,上一个使用旧的上下文 API

It is based on Consumer / Provide principle.它基于消费者/提供原则。 First, create your context首先,创建你的上下文

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

Then use via然后通过

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

and

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

All Consumers that are descendants of a Provider will re-render whenever the Provider's value prop changes.每当 Provider 的 value 属性发生变化时,所有作为 Provider 后代的 Consumer 都会重新渲染。 The propagation from Provider to its descendant Consumers is not subject to the shouldComponentUpdate method, so the Consumer is updated even when an ancestor component bails out of the update.从 Provider 到其后代 Consumer 的传播不受 shouldComponentUpdate 方法的约束,因此即使祖先组件退出更新,Consumer 也会更新。 1 1

Full example, semi-pseudo code.完整示例,半伪代码。

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 1 https://facebook.github.io/react/docs/context.html

Passing Props to Nested Children将道具传递给嵌套的孩子

With the update to React Hooks you can now use React.createContext and useContext .随着对 React Hooks 的更新,您现在可以使用React.createContextuseContext

import * as React from 'react';

// React.createContext accepts a defaultValue as the first param
const MyContext = React.createContext(); 

functional Parent(props) {
  const doSomething = (value) => {
    // Do something here with value
  };

  return (
     <MyContext.Provider value={{ doSomething }}>
       {this.props.children}
     </MyContext.Provider>
  );
}
 
function Child(props: { value: number }) {
  const myContext = React.useContext(MyContext);

  const onClick = () => {
    myContext.doSomething(props.value);
  }

  return (
    <div onClick={onClick}>{this.props.value}</div>
  );
}

// Example of using Parent and Child

import * as React from 'react';

function SomeComponent() {
  return (
    <Parent>
      <Child value={1} />
      <Child value={2} />
    </Parent>
  );
}

React.createContext shines where React.cloneElement case couldn't handle nested components React.createContextReact.cloneElement案例无法处理嵌套组件的情况下大放异彩

function SomeComponent() {
  return (
    <Parent>
      <Child value={1} />
      <SomeOtherComp>
        <Child value={2} />
      </SomeOtherComp>
    </Parent>
  );
}

The best way, which allows you to make property transfer is children like a function pattern https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9允许您进行财产转移的最佳方法是children类,如函数模式https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9

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

Example:例子:

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

You can use React.cloneElement , it's better to know how it works before you start using it in your application.您可以使用React.cloneElement ,最好在开始在应用程序中使用它之前了解它的工作原理。 It's introduced in React v0.13 , read on for more information, so something along with this work for you:它是在React v0.13中引入的,请继续阅读以获取更多信息,因此与此一起为您工作的东西:

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

So bring the lines from React documentation for you to understand how it's all working and how you can make use of them:因此,请使用 React 文档中的内容来了解​​它是如何工作的以及如何使用它们:

In React v0.13 RC2 we will introduce a new API, similar to React.addons.cloneWithProps, with this signature:在 React v0.13 RC2 中,我们将引入一个新的 API,类似于 React.addons.cloneWithProps,具有以下签名:

React.cloneElement(element, props, ...children);

Unlike cloneWithProps, this new function does not have any magic built-in behavior for merging style and className for the same reason we don't have that feature from transferPropsTo.与 cloneWithProps 不同,这个新函数没有任何用于合并 style 和 className 的神奇内置行为,原因与我们在 transferPropsTo 中没有该功能的原因相同。 Nobody is sure what exactly the complete list of magic things are, which makes it difficult to reason about the code and difficult to reuse when style has a different signature (eg in the upcoming React Native).没有人确切知道魔法事物的完整列表是什么,这使得当样式具有不同的签名时(例如在即将到来的 React Native 中)难以推理代码并且难以重用。

React.cloneElement is almost equivalent to: React.cloneElement 几乎等同于:

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

However, unlike JSX and cloneWithProps, it also preserves refs.但是,与 JSX 和 cloneWithProps 不同的是,它还保留了 refs。 This means that if you get a child with a ref on it, you won't accidentally steal it from your ancestor.这意味着如果你得到一个带有 ref 的孩子,你不会不小心从你的祖先那里偷走它。 You will get the same ref attached to your new element.您将获得附加到新元素的相同 ref。

One common pattern is to map over your children and add a new prop.一种常见的模式是映射你的孩子并添加一个新的道具。 There were many issues reported about cloneWithProps losing the ref, making it harder to reason about your code.有很多关于 cloneWithProps 丢失 ref 的问题报告,这使得你的代码更难推理。 Now following the same pattern with cloneElement will work as expected.现在遵循与 cloneElement 相同的模式将按预期工作。 For example:例如:

var newChildren = React.Children.map(this.props.children, function(child) {
  return React.cloneElement(child, { foo: true })
});

Note: React.cloneElement(child, { ref: 'newRef' }) DOES override the ref so it is still not possible for two parents to have a ref to the same child, unless you use callback-refs.注意:React.cloneElement(child, { ref: 'newRef' }) 确实会覆盖 ref,因此两个父级仍然不可能拥有同一个子级的 ref,除非您使用回调引用。

This was a critical feature to get into React 0.13 since props are now immutable.这是进入 React 0.13 的一个关键特性,因为 props 现在是不可变的。 The upgrade path is often to clone the element, but by doing so you might lose the ref.升级路径通常是克隆元素,但这样做可能会丢失 ref。 Therefore, we needed a nicer upgrade path here.因此,我们需要一个更好的升级路径。 As we were upgrading callsites at Facebook we realized that we needed this method.当我们在 Facebook 升级调用站点时,我们意识到我们需要这种方法。 We got the same feedback from the community.我们从社区得到了同样的反馈。 Therefore we decided to make another RC before the final release to make sure we get this in.因此,我们决定在最终版本之前制作另一个 RC,以确保我们能将其纳入。

We plan to eventually deprecate React.addons.cloneWithProps.我们计划最终弃用 React.addons.cloneWithProps。 We're not doing it yet, but this is a good opportunity to start thinking about your own uses and consider using React.cloneElement instead.我们还没有这样做,但这是开始考虑自己的用途并考虑改用 React.cloneElement 的好机会。 We'll be sure to ship a release with deprecation notices before we actually remove it so no immediate action is necessary.在我们实际删除它之前,我们一定会发布带有弃用通知的版本,因此无需立即采取行动。

more here ...更多在这里...

I needed to fix accepted answer above to make it work using that instead of this pointer.我需要修复上面接受的答案,以使其使用而不是这个指针来工作。 This within the scope of map function didn't have doSomething function defined.在 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>
}})

Update: this fix is for ECMAScript 5, in ES6 there is no need in var that=this更新:此修复适用于 ECMAScript 5,在 ES6 中不需要var that=this

None of the answers address the issue of having children that are NOT React components, such as text strings.没有一个答案解决了拥有不是React 组件的子项的问题,例如文本字符串。 A workaround could be something like this:解决方法可能是这样的:

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

}

Cleaner way considering one or more children考虑一个或多个孩子的更清洁的方式

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

Method 1 - clone children方法 1 - 克隆孩子

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

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

Full Demo完整演示

Method 2 - use composable context方法 2 - 使用可组合上下文

Context allows you to pass a prop to a deep child component without explicitly passing it as a prop through the components in between. Context 允许您将 prop 传递给深层子组件,而无需将其作为 prop 显式传递给它们之间的组件。

Context comes with drawbacks:上下文有缺点:

  1. Data doesn't flow in the regular way - via props.数据不会以常规方式流动——通过道具。
  2. Using context creates a contract between the consumer and the provider.使用上下文创建消费者和提供者之间的合同。 It might be more difficult to understand and replicate the requirements needed to reuse a component.理解和复制重用组件所需的需求可能会更加困难。

Using a composable context使用可组合的上下文

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

Parent.jsx:父.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: 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)}/>
);

and main.jsx:和 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('...')
);

see example here: https://plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview在此处查看示例: https ://plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview

If you have multiple children you want to pass props to, you can do it this way, using the React.Children.map:如果你有多个想要传递道具的孩子,你可以这样做,使用 React.Children.map:

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

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

If your component is having just one child, there's no need for mapping, you can just cloneElement straight away:如果您的组件只有一个孩子,则无需映射,您可以直接 cloneElement :

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

Got inspired by all the answers above and this is what I have done.受到上述所有答案的启发,这就是我所做的。 I am passing some props like some data, and some components.我正在传递一些道具,比如一些数据和一些组件。

import React from "react";

const Parent = ({ children }) => {
  const { setCheckoutData } = actions.shop;
  const { Input, FieldError } = libraries.theme.components.forms;

  const onSubmit = (data) => {
    setCheckoutData(data);
  };

  const childrenWithProps = React.Children.map(
    children,
    (child) =>
      React.cloneElement(child, {
        Input: Input,
        FieldError: FieldError,
        onSubmit: onSubmit,
      })
  );

  return <>{childrenWithProps}</>;
};

Further to @and_rest answer, this is how I clone the children and add a class.除了@and_rest 答案,这就是我克隆孩子并添加课程的方式。

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

Maybe you can also find useful this feature, though many people have considered this as an anti-pattern it still can be used if you're know what you're doing and design your solution well.也许您还可以找到有用的此功能,尽管许多人认为这是一种反模式,但如果您知道自己在做什么并很好地设计解决方案,它仍然可以使用。

Function as Child Components 作为子组件的功能

I think a render prop is the appropriate way to handle this scenario我认为渲染道具是处理这种情况的合适方法

You let the Parent provide the necessary props used in child component, by refactoring the Parent code to look to something like this:您可以让 Parent 提供在子组件中使用的必要道具,方法是重构 Parent 代码,使其看起来像这样:

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

  return children({ doSomething })
}

Then in the child Component you can access the function provided by the parent this way:然后在子组件中,您可以通过这种方式访问​​父组件提供的功能:

class Child extends React {

  onClick() => { this.props.doSomething }

  render() { 
    return (<div onClick={this.onClick}></div>);
  }

}

Now the fianl stucture will look like this:现在 fianl 结构将如下所示:

<Parent>
  {(doSomething) =>
   (<Fragment>
     <Child value="1" doSomething={doSomething}>
     <Child value="2" doSomething={doSomething}>
    <Fragment />
   )}
</Parent>

最巧妙的方法是:

    {React.cloneElement(this.props.children, this.props)}

You no longer need {this.props.children} .您不再需要{this.props.children} Now you can wrap your child component using render in Route and pass your props as usual:现在你可以在Route中使用render来包装你的子组件并像往常一样传递你的 props:

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

According to the documentation of cloneElement()根据cloneElement()的文档

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

Clone and return a new React element using element as the starting point.使用 element 作为起点克隆并返回一个新的 React 元素。 The resulting element will have the original element's props with the new props merged in shallowly.结果元素将具有原始元素的道具和新道具的浅层合并。 New children will replace existing children.新的孩子将取代现有的孩子。 key and ref from the original element will be preserved.原始元素的 key 和 ref 将被保留。

React.cloneElement() is almost equivalent to: React.cloneElement()几乎等同于:

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

However, it also preserves refs.但是,它也保留了 refs。 This means that if you get a child with a ref on it, you won't accidentally steal it from your ancestor.这意味着如果你得到一个带有 ref 的孩子,你不会不小心从你的祖先那里偷走它。 You will get the same ref attached to your new element.您将获得附加到新元素的相同 ref。

So cloneElement is what you would use to provide custom props to the children.所以 cloneElement 是你用来为孩子们提供自定义道具的东西。 However there can be multiple children in the component and you would need to loop over it.但是,组件中可以有多个子组件,您需要对其进行循环。 What other answers suggest is for you to map over them using React.Children.map .其他答案建议您使用React.Children.map映射它们。 However React.Children.map unlike React.cloneElement changes the keys of the Element appending and extra .$ as the prefix.然而,与React.Children.map不同的React.cloneElement改变了 Element appending 的键和额外的.$作为前缀。 Check this question for more details: React.cloneElement inside React.Children.map is causing element keys to change检查此问题以获取更多详细信息: React.Children.map 内的 React.cloneElement 导致元素键发生变化

If you wish to avoid it, you should instead go for the forEach function like如果你想避免它,你应该改为使用forEach函数,如

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

}

For any one who has a single child element this should do it.对于任何拥有单个子元素的人来说,都应该这样做。

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

Here's my version that works with single, multiple, and invalid 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);
  }
};

Usage example:使用示例:

https://codesandbox.io/s/loving-mcclintock-59emq?file=/src/ChildVsChildren.jsx:0-1069 https://codesandbox.io/s/loving-mcclintock-59emq?file=/src/ChildVsChildren.jsx:0-1069

When using functional components, you will often get the TypeError: Cannot add property myNewProp, object is not extensible error when trying to set new properties on props.children .使用功能组件时,在尝试在props.children上设置新属性时,经常会遇到TypeError: Cannot add property myNewProp, object is not extensible错误。 There is a work around to this by cloning the props and then cloning the child itself with the new props.可以通过克隆道具然后用新道具克隆孩子本身来解决这个问题。

const MyParentComponent = (props) => {
  return (
    <div className='whatever'>
      {props.children.map((child) => {
        const newProps = { ...child.props }
        // set new props here on newProps
        newProps.myNewProp = 'something'
        const preparedChild = { ...child, props: newProps }
        return preparedChild
      })}
    </div>
  )
}

Is this what you required?这是你要求的吗?

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

I came to this post while researching for a similar need, but i felt cloning solution that is so popular, to be too raw and takes my focus away from the functionality.我在研究类似需求时来到这篇文章,但我觉得克隆解决方案非常流行,太原始并且让我的注意力从功能上移开。

I found an article in react documents Higher Order Components我在反应文档高阶组件中找到了一篇文章

Here is my sample:这是我的示例:

import React from 'react';

const withForm = (ViewComponent) => {
    return (props) => {

        const myParam = "Custom param";

        return (
            <>
                <div style={{border:"2px solid black", margin:"10px"}}>
                    <div>this is poc form</div>
                    <div>
                        <ViewComponent myParam={myParam} {...props}></ViewComponent>
                    </div>
                </div>
            </>
        )
    }
}

export default withForm;


const pocQuickView = (props) => {
    return (
        <div style={{border:"1px solid grey"}}>
            <div>this is poc quick view and it is meant to show when mouse hovers over a link</div>
        </div>
    )
}

export default withForm(pocQuickView);

For me i found a flexible solution in implementing the pattern of Higher Order Components.对我来说,我找到了一个灵活的解决方案来实现高阶组件的模式。

Of course it depends on the functionality, but it is good if someone else is looking for a similar requirement, it is much better than being dependent on raw level react code like cloning.当然,这取决于功能,但如果其他人正在寻找类似的需求,那很好,这比依赖于原始级别的反应代码(如克隆)要好得多。

Other pattern that i actively use is the container pattern.我积极使用的其他模式是容器模式。 do read about it, there are many articles out there.一定要阅读它,那里有很多文章。

In case anyone is wondering how to do this properly in TypeScript where there are one or multiple child nodes.如果有人想知道如何在有一个或多个子节点的 TypeScript 中正确执行此操作。 I am using the uuid library to generate unique key attributes for the child elements which, of course, you don't need if you're only cloning one element.我正在使用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>
);

As you can see, this solution takes care of rendering an array of or a single ReactElement , and even allows you to pass properties down to the child component(s) as needed.如您所见,此解决方案负责渲染一个数组或单个ReactElement ,甚至允许您根据需要将属性向下传递给子组件。

Some reason React.children was not working for me.某些原因 React.children 不适合我。 This is what worked for me.这对我有用。

I wanted to just add a class to the child.我只想为孩子添加一个类。 similar to changing a prop类似于更改道具

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

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

The trick here is the React.cloneElement .这里的诀窍是React.cloneElement You can pass any prop in a similar manner您可以以类似的方式传递任何道具

Render props is most accurate approach to this problem.渲染道具是解决此问题的最准确方法。 Instead of passing the child component to parent component as children props, let parent render child component manually.与其将子组件作为子 props 传递给父组件,不如让父组件手动渲染子组件。 Render is built-in props in react, which takes function parameter. Render是 react 内置的 props,它带有函数参数。 In this function you can let parent component render whatever you want with custom parameters.在此函数中,您可以让父组件使用自定义参数呈现您想要的任何内容。 Basically it does the same thing as child props but it is more customizable.基本上它与子道具做同样的事情,但它更可定制。

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

Example at codepen codepen 上的示例

There are lot of ways to do this.有很多方法可以做到这一点。

You can pass children as props in parent.您可以将孩子作为道具传递给父母。

example 1 :示例 1

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

return <Parent ChildElement={ChildComponent}/>

Pass children as Function将孩子作为函数传递

example 2 :示例 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>

I did struggle to have the listed answers work but failed.我确实很难让列出的答案起作用,但失败了。 Eventually, I found out that the issue is with correctly setting up the parent-child relationship.最终,我发现问题在于正确设置父子关系。 Merely nesting components inside other components does not mean that there is a parent-child relationship.仅仅将组件嵌套在其他组件中并不意味着存在父子关系。

Example 1. Parent-child relationship;示例 1. 亲子关系;

function Wrapper() {
  return (
    <div>
      <OuterComponent>
        <InnerComponent />
      </OuterComponent>
    </div>
  );
}
function OuterComponent(props) {
  return props.children;
}
function InnerComponent() {
  return <div>Hi! I'm in inner component!</div>;
}
export default Wrapper;

Example 2. Nested components:示例 2. 嵌套组件:

function Wrapper() {
  return (
    <div>
      <OuterComponent />
    </div>
  );
}
function OuterComponent(props) {
  return <InnerComponent />
}
function InnerComponent() {
  return <div>Hi! I'm in inner component!</div>;
}
export default Wrapper;

As I said above, props passing works in Example 1 case.正如我上面所说,道具传递在示例 1 的情况下有效。

The article below explains it https://medium.com/@justynazet/passing-props-to-props-children-using-react-cloneelement-and-render-props-pattern-896da70b24f6下面的文章解释它https://medium.com/@justynazet/passing-props-to-props-children-using-react-cloneelement-and-render-props-pattern-896da70b24f6

This answer is wrt React v17.x...这个答案是 wrt React v17.x ......

Use the children as a function and pass props to it as a render props pattern, as below: -children用作函数并将道具作为render props模式传递给它,如下所示: -

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

Just make sure, the actual, to be projected content must be added like a function in render props pattern, to accommodate the argument passed as a prop within the children function.只需确保必须像渲染道具模式中的函数一样添加实际要投影的内容,以适应子函数中作为prop传递的参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM