React Pass all/multiple props to dynamic children - preferably without context - reactjs

I've found several examples/posts about how to pass ONE prop to dynamic chidren using React such as:
return React.cloneElement({this.props.children}, { parentValue: this.props.parentValue });
However passing multiple or all props seems to send things into a recursion loop that eventually crashes the app:
return React.cloneElement({this.props.children}, { parentValue1: this.props.parentValue1, parentValue2: this.props.parentValue2});
or..
return React.cloneElement({this.props.children}, {...this.props});
Is there an effective way to pass multiple (or all) props to dynamic children? It would seem that if you can for one or more static children, you should be able to if they happen to be dynamic.

you can do it like this
import React from 'react'
export default class Container extends React.Component {
render() {
return (
<div>
{React.cloneElement(this.props.children, {...this.props})}
</div>
)
}
}
from now any element you put inside container will have container's props
<Container><ContactPage /></Container>
// contactPage will have all props from container

Don't forget to exclude children from props to pass it further.
I spent lots of hours to understand why I have a loop.
You can read more here about this issue
render() {
const { children, ...otherProps } = this.props
return React.cloneElement(this.props.children, otherProps)
}

Related

React render specific children

I have looked through other peoples questions relating to this but cant find a suitable answer. I would like to pass children to a component and then pull out the specific children where I want them, most examples I have seen just have the children render in the same place.
My component looks something like this -
<ParentComponent>
<ChildOne/>
<ChildTwo/>
<ParentComponent/>
When I log the props.children inside the parent component I get an array which contains both children as objects. is there a simple way to pull out the specific child where I need it such as {props.children.ChildOne} at the moment I am using props.children[0] which isn't ideal as we will be passing the children dynamically
in the future and the array length may change.
As always any help is greatly appreciated!
Depending on your exact situation and needs, it might make more sense to pass child components as props than using the special children prop. Then you can render them whichever way you like.
<ParentComponent childOne={ChildOne} childTwo={ChildTwo} />
...
const ParentComponent = ({ childOne, childTwo }) => {
return (
<div>
{childOne}
<div>
{childTwo}
</div>
</div>
);
};
But knowing your exact scenario would help a lot with conceptualising the best way to implement this. Perhaps you can refactor your code to avoid passing an array of children like this.
Actually, the ReactChildren API I was mentioning is useless here.
You can do something like this instead:
import React from 'react';
import { ChildOne } from './YourFile';
export function ParentComponent({children}) {
return children.find(child => child.type === ChildOne)
}
You should define the displayName property for the child components and then use the displayName in the parent to find the specific children from children list and place them where you want it to be.
// define displayName for each component, it can be any string
// You can set the displayName for any react component before exporting as shown
// below
const ChildOne = (props) => { return (<div> Child One </div>)}
ChildOne.displayName = "ChildOne";
export default ChildOne;
const ChildTwo = (props) => { return (<div> Child Two </div>)}
ChildTwo.displayName = "ChildTwo";
export default ChildTwo;
Now in parent component you can filter out the specific child by using their displayName.
const ParentComponent = (props) => {
const getChildByDisplayName = (displayName) => {
const child = React.Children.map(props.children, (child => {
// you can access displayName property by child.type.displayName
if (child.type.displayName === displayName) return child;
return null;
}))
return child;
}
return (
<div>
{/* You can change the order here as per your wish*/}
{getChildByDisplayName("ChildOne")}
{getChildByDisplayName("ChildTwo")}
</div>
)
}
That's it, Now even if you put ChildTwo before ChildOne like below example, parent component will still render the ChildOne first and then ChildTwo because we have defined order in parent.
<ParentComponent>
<ChildTwo/>
<ChildOne/>
<ParentComponent/>
Using the key seems simpler:
whatever is using the parent component:
<ParentComponent>
<ChildOne key="title"/>
<ChildTwo key="picture"/>
<ParentComponent/>
parent component:
export default function ParentComponent(props: any) {
const title = props.children.find((o: any) => o.key === 'title')
const picture = props.children.find((o: any) => o.key === 'picture')
return <div>
<jumbobox>
{title}
</jumbobox>
<fancyframe>
{picture}
</fancyframe>
</div>
}
One way is to control the Child Components being passed to ParentComponent through state/props/redux store management in the component containing ParentComponent and its children.
But to have the solution as per your use case, we can avoid using children prop and use our defined prop on ParentComponent.
<ParentComponent
components={[
{key: 1, component: <div>Hello</div>}
]}
/>
So, we can now filter from the key.
Checkout this demo

how to overwrite children's props in react

i want to render a list of react components.The props of each component vary,but every element's props will be overwritten by parents props.i tried something like this
render(){
let children=this.props.children
let dis;
if(Array.isArray(children)){
dis=[]
children.forEach(c=>{let l=Object.assign(c,Object.assign(c.props,this.props))
dis.push(l)})
}
return(
{dis}
)
}
}
but got error that props in read only.
It sounds like what you're trying to do is have a parent component inject additional settings into the children it is going to render.
Take a look here:
https://stackoverflow.com/a/50786569/1541237
Basically, you would render like this:
render() {
return (
React.Children.map(this.props.children, child =>
React.cloneElement(child, { ...this.props }));
);
}

Rendering a component as the parent of the actual component

I have 2 components, I would like the first component to be a render prop in the second component, which will encapsulate the content of the second component if it is defined. I don't quite understand how to write this, and the documentation I find on render props tends to be difficult to understand. Has anyone written a similar implementation?
The general idea i'm after is that you pass in the props for component1 in the component1 prop for component2, and it renders <Component1> with it's props inside component2.
Rough code example of what i'm trying to do (It isn't meant to work)
interface Component1 {
id?: string;
children: React.ReactNode;
}
const Component1 = (props: Component1) => {
const { children } = props;
return (<div className="component1">{children}</div>)
}
interface Component2 {
component1?: (propsForComponent1) => <Component1 {...propsForComponent1}>
}
const Component2 = (props: Component2) => {
const {component1} = props;
if (component1) {
return {component1({id: 'exampleId', children: <div className="component2">Stuff for component 2</div>)}}
};
return (<div className="component2">Stuff for component 2</div>);
}
Edit: Have changed the example because the intention is confusing people.
Edit: Will just pass the first component as a prop into the second component for now. I think the general answer is to not try to use a component as two components, and just stick to children.
What is actually happening in your code sample, is that even if you are passing down a whole component (Component1) as a prop, you are not rendering it, but using an imported (or accessible inside the scope) Component1.
Anyways - why would you want to pass down a whole component as a prop, even if you can basically just import it?
What I would suggest - use a prop, but not a component, but a boolean flag that determines if the component should be wrapped or not.
interface Component2 {
shouldBeWrapped?: boolean;
}
const Component2 = ({ shouldBeWrapped }: Component2) => {
if (shouldBeWrapped) {
return (
<Component1 with props given in on component1 property>
<div className="component2">Stuff for component 2</div>
</Component1>
);
}
return (<div className="component2">Stuff for component 2</div>);
}

Setting children in defaultProps

I am developing a React component that contains some child components. I would like it to have a default set of children, if its element is created without specifying children (i.e. <MyContainerComponent ... /> should be equivalent to <MyContainerComponent ...><!-- some default child elements --></MyContainerComponent>).
From https://github.com/facebook/react/blob/v0.14.8/src/isomorphic/classic/element/ReactElement.js#L135, I understand that props.children will be undefined if no children are specified. Thus, I would like to rely on this and set children in the defaultProps of MyContainerComponent.
I am not sure, however, if I am coupling my decision too much with React's implementation (i.e. by design, as a user of React, is it acceptable to treat children just like any other prop that would be undefined if you did not explicitly define it). All other materials I read on children treat is as either a single element, or an array, but not potentially undefined.
I'd really appreciate a more experienced opinion.
The React project hasn't done a great job of documenting how props.children is treated, but I would say that:
it [is] acceptable to treat children just like any other prop that would be undefined if you did not explicitly define it
References:
Comments by Sebastian Markbåge:
The type of this.props.children is whatever your component's propTypes says it is.
And:
We recommend that most of the time you use the general ReactNode type for your this.props.children.
Where the definition of ReactNode includes undefined.
My interpretation is that undefined is a valid value for props.children and should be the default in general. But note that with JSX even whitespace might affect the value:
<MyContainerComponent> </MyContainerComponent>
All of this being said, as indicated by the linked GitHub issue I'm trying to get this better documented so the API contract is clear :)
Set default props for children using getDefaultProps. If react changes the definition of empty props === undefined, they will also do so in the getDefaultProps life cycle method (fiddle):
var Parent = React.createClass({
getDefaultProps: function() {
return {
children: <span>default child</span> // set children in default props
};
},
render: function() {
return <div>I am a { this.props.children }</div>; // if we have children use them, if not use default from state
}
});
ReactDOM.render(
<Parent />, // without children
document.getElementById('container1')
);
ReactDOM.render(
<Parent>non a default child</Parent>, // with children
document.getElementById('container2')
);
You can use React.createElement as default value for your children props
https://reactjs.org/docs/react-api.html#createelement
example:
import React, { PureComponent } from 'react'
import { element } from 'prop-types'
export class Example extends PureComponent {
static propTypes = { children: element }
static defaultProps = { children: React.createElement('div') }
render () {
const { children } = this.props
return <div className='parent'>{children}</div>
}
}

is there any way to access the parent component instance in React?

I know it's not a functional approach to be able to do something like this.parent in a React component, and I can't seem to find any properties on a React component instance that lead to the parent, but I'm just looking to be able to do some custom things where I need this.
Before anyone wastes their time explaining it's not the functional React "way," understand that I need this because of the following I'm trying to achieve:
Build a transpiler for Meteor's Spacebars templating engine, whose rendering model does take into consideration parent components/templates.
I've already built a transpiler that modifies the output jsx to achieve this. I do this by passing in parent={this} in all child components composed. However, after the fact it occurred to me that maybe I simply don't know of something that will give me a way to access the parent component instance without additional transpilation modifications.
Any tips would be much appreciated.
There's nothing wrong if you need to access the parent's props and functions from the children.
The point is that you should never use React internals and undocumented APIs.
First of all, they are likely to change (breaking your code) and, most importantly, there are many other approaches which are cleaner.
Passing props to children
class Parent extends React.Component {
constructor(props) {
super(props)
this.fn = this.fn.bind(this)
}
fn() {
console.log('parent')
}
render() {
return <Child fn={this.fn} />
}
}
const Child = ({ fn }) => <button onClick={fn}>Click me!</button>
Working example
Using context (if there's no direct parent/child relation)
class Parent extends React.Component {
constructor(props) {
super(props)
this.fn = this.fn.bind(this)
}
getChildContext() {
return {
fn: this.fn,
}
}
fn() {
console.log('parent')
}
render() {
return <Child fn={this.fn} />
}
}
Parent.childContextTypes = {
fn: React.PropTypes.func,
}
const Child = (props, { fn }) => <button onClick={fn}>Click me!</button>
Child.contextTypes = {
fn: React.PropTypes.func,
}
Working example
Update for React 0.13 and newer
Component._owner was deprecated in React 0.13, and _currentElement no longer exists as a key in this._reactInternalInstance. Therefore, using the solution below throws Uncaught TypeError: Cannot read property '_owner' of undefined.
The alternative is, as of React 16, this._reactInternalFiber._debugOwner.stateNode.
You've already recognized that this is not a good thing to do almost always, but I'm repeating it here for people that don't read the question very well: this is generally an improper way to get things done in React.
There's nothing in the public API that will allow you to get what you want. You may be able to get to this using the React internals, but because it's a private API it's liable to break at any time.
I repeat: you should almost certainly not use this in any sort of production code.
That said, you can get the internal instance of the current component using this. _reactInternalInstance. In there, you can get access to the element via the _currentElement property, and then the owner instance via _owner._instance.
Here's an example:
var Parent = React.createClass({
render() {
return <Child v="test" />;
},
doAThing() {
console.log("I'm the parent, doing a thing.", this.props.testing);
}
});
var Child = React.createClass({
render() {
return <button onClick={this.onClick}>{this.props.v}</button>
},
onClick() {
var parent = this._reactInternalInstance._currentElement._owner._instance;
console.log("parent:", parent);
parent.doAThing();
}
});
ReactDOM.render(<Parent testing={true} />, container);
And here's a working JSFiddle example: http://jsfiddle.net/BinaryMuse/j8uaq85e/
Tested with React 16
I was playing around with something similar using context, tho to anyone reading this, for most usual cases, accessing the parent is not advised!
I created a holder that when used, would always have a reference to the first holder up the display list, so its 'parent' if you will. Looked something like this:
const ParentContext = React.createContext(null);
// function to apply to your react component class
export default function createParentTracker(componentClass){
class Holder extends React.PureComponent {
refToInstance
render(){
return(
<ParentContext.Consumer>
{parent => {
console.log('I am:', this, ' my parent is:',parent ? parent.name : 'null');
return(
<ParentContext.Provider value={this}>
<componentClass ref={inst=>refToInstance=inst} parent={parent} {...this.props} />
</ParentContext.Provider>
)}
}
</ ParentContext.Consumer>
)
}
}
// return wrapped component to be exported in place of yours
return Holder;
}
Then to use it you would pass your react component to the method when you export it like so:
class MyComponent extends React.Component {
_doSomethingWithParent(){
console.log(this.props.parent); // holder
console.log(this.props.parent.refToInstance); // component
}
}
// export wrapped component instead of your own
export default createParentTracker(MyComponent);
This way any component exporting the function will get its parent's holder passed in as a prop (or null if nothing is further up the hierarchy). From there you can grab the refToInstance. It will be undefined until everything is mounted though.

Resources