ChatGPT解决这个技术问题 Extra ChatGPT

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.

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?

I learned a lot from the answers to this question. I think the Context API is the best solution in today's React land. But if you want to use React.cloneElement, one gotcha I faced is not properly iterating the children with React.Children.map(). See more in How To Pass Props to {react.children}

D
Dominic

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. For example:

const Child = ({ doSomething, value }) => ( ); 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

{childrenWithProps}
} function App() { return ( ); } ReactDOM.render(, document.getElementById("container"));

Calling children as a function

Alternatively, you can pass props to children with render 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:

const Child = ({ doSomething, value }) => ( ); 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

{children(doSomething)}
} function App() { // doSomething is the arg we passed in Parent, which // we now pass through to Child. return ( {doSomething => ( )} ); } ReactDOM.render(, document.getElementById("container"));

Instead of <React.Fragment> or simply <> you can also return an array if you prefer.


This doesn't work for me. this is not defined within React.cloneElement()
This answer doesn't work, the value passed to doSomething is lost.
@DominicTobias Arg, sorry, I switched console.log to alert and forgot to concat the two params to a single string.
This answer was super-helpful, but I ran into an issue that isn't mentioned here and I was wondering if it's some new thing that's changed or whether it's something odd on my end. When I cloned my child element, it's child was set to the old element, until I added this.props.children.props.children to the third argument of cloneElement.
What if the child is loaded via a route (v4) thats loaded from a separate route page?
m
mysl

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

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

I was using the most rated answer, but this one is much more straight forward! This solution is also what they use on the react-router examples page.
Could someone explain how this works (or what it actually does)? Reading the docs, I couldn't see how this would descend into the children and add that prop to each child - is that what it is intended to do? If it does, how do we know that this is what it will do? It's not at all obvious that it's even valid to pass an opaque data structure (this.props.children) to cloneElement ... which is expecting an ... element.
Exactly, this doesn't seem to work with more than one children.
So you can write code that works while someone passes only one child into a component, but when they add another, it crashes ... that doesn't sound great on face value? It would seem to be a trap for the OP, who asked specifically about passing props to all children.
@GreenAsJade its fine as long as your component is expecting a single child. You can define via your components propTypes that it expects a single child. React.Children.only function returns the only child or throws an exception if there are multiple (this wouldn't exist if there wasn't a use case).
M
Michael Freidgeim

Try this

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

It worked for me using react-15.1.

Use {...this.props} is suggested in https://reactjs.org/docs/jsx-in-depth.html#spread-attributes


Is it possible to return React.cloneElement() directly without surrounding it in <div> tags? Because what if the child is a <span> (or something else) and we want to preserve its tag element type?
If it's one child you can leave out the wrapper and this solution only works for one child so yes.
Works for me. Without enclosing
is ok.
If you need to explicitly enforce that you only receive one child, you can do React.cloneElement(React.Children.only(this.props.children), {...this.props}) which will throw an error if it is passed more than one child. Then you don't need to wrap in a div.
This answer may produce a TypeError: cyclic object value. Unless you want one of the child's props to be itself, use let {children, ...acyclicalProps} = this.props and then React.cloneElement(React.Children.only(children), acyclicalProps).
M
Mike

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

Disclaimer: This is an updated answer, the previous one used the old context 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. 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. 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


Unlike the accepted answer this will work properly even when there are other elements included under Parent. This is definitely the best answer.
Props != context
you cannot depend on changes propagating through context. Use props when it's possible they change.
Maybe I don't understand but isn't it wrong to say "context makes props available"? When I last used context, it was a separate thing (i.e. this.context)--it didn't magically merge the context with props. You had to intentionally set and use the context, which is a whole other thing.
You understand perfectly, it was incorrect. I've edited my answer.
K
Kenneth Truong

Passing Props to Nested Children

With the update to React Hooks you can now use React.createContext and useContext.

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

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

Can you explain why => functions are a bad practice? The => function helps bind the event handlers to get this context
@KennethTruong because each time it renders it creates a function.
@itdoesntwork that's not true. It only creates a new function when the class is created. Its not being created during the render function..
@KennethTruong reactjs.org/docs/faq-functions.html#arrow-function-in-render i thought you were talking about arrow function in render.
I would remove value={2} in your code. Reason: 1) It's not necessary for explaining the context. 2) It distracts for the actual key point of the code, which is the use of myContext. :)
N
Nick Ovchinnikov

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

Code snippet: 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>
  )
}


This seems way more straightforward (and better for performance?) to me than the accepted answer.
This requires children to be a function and does not work for deply nested components
@digitalillusion, I don't understand what does it mean nested components. React doesn't have nested patterns, only compositions. Yes, children must be a function, there is no any conflicts, because this's valid JSX child. Can you give an example with nesting components ?
You are right the case of deeply nested children can be handled as well <Parent>{props => <Nest><ChildComponent /></Nest>}</Parent> instead of (not working) <Parent><Nest>{props => <ChildComponent />}</Nest></Parent> so I agree this is the best answer
When attempting, I receive the following: TypeError: children is not a function
A
Alireza

You can use React.cloneElement, it's better to know how it works before you start using it in your application. It's introduced in React v0.13, read on for more information, so something along with this work for you:

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

In React v0.13 RC2 we will introduce a new API, similar to React.addons.cloneWithProps, with this signature:

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. 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 (e.g. in the upcoming React Native). React.cloneElement is almost equivalent to:

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

However, unlike JSX and cloneWithProps, it also preserves refs. This means that if you get a child with a ref on it, you won't accidentally steal it from your ancestor. You will get the same ref attached to your new element. 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. Now following the same pattern with cloneElement will work as expected. 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. This was a critical feature to get into React 0.13 since props are now immutable. The upgrade path is often to clone the element, but by doing so you might lose the ref. Therefore, we needed a nicer upgrade path here. As we were upgrading callsites at Facebook we realized that we needed this method. 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. We plan to eventually deprecate 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. We'll be sure to ship a release with deprecation notices before we actually remove it so no immediate action is necessary.

more here...


o
olenak

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.

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


or just use bind()
or use an arrow function which binds to lexical scope, I updated my answer
what if doSomething took an object, like doSomething: function(obj) { console.log(obj) } and in the Child you'd call this.props.doSomething(obj) to log out "obj"
@plus- i know this is old, but using bind here is a terrible idea, bind creates a new function that binds the context to a new one. basically a function calling the apply method. using bind() in the render function will create a new function each time the render method is called.
p
poynting

None of the answers address the issue of having children that are NOT React components, such as text strings. 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>

}

p
pkoch

Cleaner way considering one or more children

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

This one doesn't work for me, it gives an error: children not defined.
@Deelux this.props.children instead of children
this passes the child as its own children in this.props. In general I'd only recommend cloning with specific props, not the whole shebang.
Passing {...this.props} didn't work for me, is the way {...child.props} correct?
For functional components: React.Children.map(children, child => React.cloneElement(child, props))
B
Ben Carp

Method 1 - clone children

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

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 comes with drawbacks:

Data doesn't flow in the regular way - via props. 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>;
};


A bit late, but could you explain the notation in {children}:{children:ReactNode}?
@camille, It's a Typescript thing. Looking at it now, I would just answer with Javascript, and even if I would write Typescript, I'd do it differently. Might edit it in the future.
@camille, basically it means that the value which has the key "children" is of type ReactNode
@Ben Carp by using method 1 how can I access attributeToAddOrReplace this into children file ?
Hi @SamikshaJagtap, I added a demo to more clearly show how it is done. Hope this helps! stackblitz.com/edit/react-te35fc?file=src/App.js
M
Maksim Kostromin

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

and 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


N
Nesha Zoric

If you have multiple children you want to pass props to, you can do it this way, using the 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:

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

D
DonKoko

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


it's useless to write <>{childrenWithProps}</> since childrenWithProps is already an Array. Return childrenWithProps is suffice. BTW, this answer is identical to the selected one from 6 years ago - both use React.Children.map and then cloneElement
s
sidonaldson

Further to @and_rest answer, this is how I clone the children and add a class.

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

A
Alexandr Cherednichenko

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


Z
ZEE

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:

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:

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

what if the parent is just a wrapper for a {children} that is a textarea in another class component?
n
nitte93

The slickest way to do this:

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

Does this not copy this.props.children into this.props.children of the child? and in effect copying the child into itself?
S
Shubham Khatri

According to the documentation of cloneElement()

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

Clone and return a new React element using element as the starting point. 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. React.cloneElement() is almost equivalent to: {children} However, it also preserves refs. This means that if you get a child with a ref on it, you won’t accidentally steal it from your ancestor. You will get the same ref attached to your new element.

So cloneElement is what you would use to provide custom props to the children. 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. However React.Children.map unlike React.cloneElement changes the keys of the Element appending and extra .$ as the prefix. Check this question for more details: React.cloneElement inside React.Children.map is causing element keys to change

If you wish to avoid it, you should instead go for the forEach function like

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

}

y
yeasayer

You no longer need {this.props.children}. Now you can wrap your child component using render in Route and pass your props as usual:

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

Render props are now standard in React (reactjs.org/docs/render-props.html) and are worth considering as a new accepted answer for this question.
How is this an answer to the question?
Yeah... this doesn't answer the question because the question isn't about react-router. It did, however, answer my question related to this question that is specific to react-router. This basic info is not clear anywhere on react-router's website that I can find. Certainly not mentioned in their upgrade notes (which are terribly incomplete). This answer should be moved to its own question.
u
user10884362

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}

p
pyjamas

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


a
amaster

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

a
amit_183

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

Nope, I don't want to constraint the content of my wrapper to some specific content.
K
Kalpesh Popat

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.


M
MHD

In case anyone is wondering how to do this properly in TypeScript where there are one or multiple child nodes. 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.

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.


a
aWebDeveloper

Some reason React.children was not working for me. 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. You can pass any prop in a similar manner


o
omeralper

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. Render is built-in props in react, which takes function parameter. 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


V
Vivek sharma

There are lot of ways to do this.

You can pass children as props in parent.

example 1 :

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

return <Parent ChildElement={ChildComponent}/>

Pass children as Function

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

n
nomadus

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;

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:

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.

The article below explains it https://medium.com/@justynazet/passing-props-to-props-children-using-react-cloneelement-and-render-props-pattern-896da70b24f6