How to handle inputs added by button in React [duplicate] - javascript

We should avoid method binding inside render because during re-rendering it will create the new methods instead of using the old one, that will affect the performance.
So for the scenarios like this:
<input onChange = { this._handleChange.bind(this) } ...../>
We can bind _handleChange method either in constructor:
this._handleChange = this._handleChange.bind(this);
Or we can use property initializer syntax:
_handleChange = () => {....}
Now lets consider the case where we want to pass some extra parameter, lets say in a simple todo app, onclick of item i need to delete the item from array, for that i need to pass either the item index or the todo name in each onClick method:
todos.map(el => <div key={el} onClick={this._deleteTodo.bind(this, el)}> {el} </div>)
For now just assume that todo names are unique.
As per DOC:
The problem with this syntax is that a different callback is created
each time the component renders.
Question:
How to avoid this way of binding inside render method or what are the alternatives of this?
Kindly provide any reference or example, thanks.

First: A simple solution will be to create a component for the content inside a map function and pass the values as props and when you call the function from the child component you can pass the value to the function passed down as props.
Parent
deleteTodo = (val) => {
console.log(val)
}
todos.map(el =>
<MyComponent val={el} onClick={this.deleteTodo}/>
)
MyComponent
class MyComponent extends React.Component {
deleteTodo = () => {
this.props.onClick(this.props.val);
}
render() {
return <div onClick={this.deleteTodo}> {this.props.val} </div>
}
}
Sample snippet
class Parent extends React.Component {
_deleteTodo = (val) => {
console.log(val)
}
render() {
var todos = ['a', 'b', 'c'];
return (
<div>{todos.map(el =>
<MyComponent key={el} val={el} onClick={this._deleteTodo}/>
)}</div>
)
}
}
class MyComponent extends React.Component {
_deleteTodo = () => {
console.log('here'); this.props.onClick(this.props.val);
}
render() {
return <div onClick={this._deleteTodo}> {this.props.val} </div>
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
EDIT:
Second: The other approach to it would be to use memoize and return a function
constructor() {
super();
this._deleteTodoListener = _.memoize(
this._deleteTodo, (element) => {
return element.hashCode();
}
)
}
_deleteTodo = (element) => {
//delete handling here
}
and using it like
todos.map(el => <div key={el} onClick={this._deleteTodoListener(el)}> {el} </div>)
P.S. However this is not a best solution and will still result in
multiple functions being created but is still an improvement over the
initial case.
Third: However a more appropriate solution to this will be to add an attribute to the topmost div and get the value from event like
_deleteTodo = (e) => {
console.log(e.currentTarget.getAttribute('data-value'));
}
todos.map(el => <div key={el} data-value={el} onClick={this._deleteTodo}> {el} </div>)
However, in this case the attributes are converted to string using toString method and hence and object will be converted to [Object Object] and and array like ["1" , "2", "3"] as "1, 2, 3"

How to avoid this way of binding inside render method or what are the
alternatives of this?
If you care about re-rendering then shouldComponentUpdate and PureComponent are your friends and they will help you optimize rendering.
You have to extract "Child" component from the "Parent" and pass always the same props and implement shouldComponentUpdate or use PureComponent. What we want is a case when we remove a child, other children shouldn't be re-rendered.
Example
import React, { Component, PureComponent } from 'react';
import { render } from 'react-dom';
class Product extends PureComponent {
render() {
const { id, name, onDelete } = this.props;
console.log(`<Product id=${id} /> render()`);
return (
<li>
{id} - {name}
<button onClick={() => onDelete(id)}>Delete</button>
</li>
);
}
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
products: [
{ id: 1, name: 'Foo' },
{ id: 2, name: 'Bar' },
],
};
this.handleDelete = this.handleDelete.bind(this);
}
handleDelete(productId) {
this.setState(prevState => ({
products: prevState.products.filter(product => product.id !== productId),
}));
}
render() {
console.log(`<App /> render()`);
return (
<div>
<h1>Products</h1>
<ul>
{
this.state.products.map(product => (
<Product
key={product.id}
onDelete={this.handleDelete}
{...product}
/>
))
}
</ul>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Demo: https://codesandbox.io/s/99nZGlyZ
Expected behaviour
<App /> render()
<Product id=1... render()
<Product id=2... render()
When we remove <Product id=2 ... only <App /> is re-rendered.
render()
To see those messages in demo, open the dev tools console.
The same technique is used and described in article: React is Slow, React is Fast: Optimizing React Apps in Practice by François Zaninotto.

Documentation encourages to use data-attributes and access them from within evt.target.dataset:
_deleteTodo = (evt) => {
const elementToDelete = evt.target.dataset.el;
this.setState(prevState => ({
todos: prevState.todos.filter(el => el !== elementToDelete)
}))
}
// and from render:
todos.map(
el => <div key={el} data-el={el} onClick={this._deleteTodo}> {el} </div>
)
Also note that this makes sense only when you have performance issues:
Is it OK to use arrow functions in render methods?
Generally speaking, yes, it is OK, and it is often the easiest way to
pass parameters to callback functions.
If you do have performance issues, by all means, optimize!

This answer https://stackoverflow.com/a/45053753/2808062 is definitely exhaustive, but I'd say fighting excessive re-renders instead of just re-creating the tiny callback would bring you more performance improvements. That's normally achieved by implementing a proper shouldComponentUpdate in the child component.
Even if the props are exactly the same, the following code will still re-render children unless they prevent it in their own shouldComponentUpdate (they might inherit it from PureComponent):
handleChildClick = itemId => {}
render() {
return this.props.array.map(itemData => <Child onClick={this.handleChildClick} data={itemData})
}
Proof: https://jsfiddle.net/69z2wepo/92281/.
So, in order to avoid re-renders, the child component has to implement shouldComponentUpdate anyway. Now, the only reasonable implementation is completely ignoring onClick regardless of whether it has changed:
shouldComponentUpdate(nextProps) {
return this.props.array !== nextProps.array;
}

Related

Is this a valid way of updating state that depends on prevState?

I am following along with a video tutorial on using React. The presenter is currently detailing how to add a toggle button to a UI. They said to give it a go first before seeing how they do it, so I implemented it myself. My implementation was a little different to theirs, just the handler was different; but it does seem to work.
Can anyone with more experience using React tell me, is my toggleSideDrawerHandler wrong in some way? Or is it a valid shorter way of setting the state that depends on a previous state?
My implementation:
//Layout.js
class Layout extends Component {
state = {
showSideDrawer: false
};
toggleSideDrawerHandler = prevState => {
let newState = !prevState.showSideDrawer;
this.setState({ showSideDrawer: newState });
};
closeSideDrawerHandler = () => {
this.setState({ showSideDrawer: false });
};
render() {
return (
<Fragment>
<Toolbar drawerToggleClicked={this.toggleSideDrawerHandler} />
<SideDrawer
open={this.state.showSideDrawer}
close={this.closeSideDrawerHandler}
/>
<main className={styles.Content}>{this.props.children}</main>
</Fragment>
);
}
}
//Toolbar.js
const toolbar = props => (
<header className={styles.Toolbar}>
<DrawerToggle clicked={props.drawerToggleClicked} />
<div className={styles.Logo}>
<Logo />
</div>
<nav className={styles.DesktopOnly}>
<NavItems />
</nav>
</header>
);
Tutorial implementation:
toggleSideDrawerHandler = () => {
this.setState(prevState => {
return { showSideDrawer: !prevState.showSideDrawer };
});
};
Your solution works, but I guess in the part, where you call the toggleSideDrawerHandler you probably call it like
() => this.toggleSideDrawerHandler(this.state)
right?
If not, can you please paste the rest of your code (especially the calling part) to see where you get the prevState from?
This works, because you pass the old state to the method.
I would personally prefer the tutorials implementation, because it takes care of dependencies and the "user" (the dev using it) doesn't need to know anything about the expected data.
With the second implementation all you need to do is call the function and not think about getting and passing the old state to it.
Update after adding the rest of the code:
I think the reason, why it works is because the default value for your parameter is the one passed by the event by default, which is an event object.
If you use prevState.showSideDrawer you are calling an unknown element on this event object, that will be null.
Now if you use !prevState.showSideDrawer, you are actually defining it as !null (inverted null/false), which will be true.
This is why it probably works.
Maybe try to toggle your code twice, by showing and hiding it again.
Showing it will probably work, but hiding it again will not.
This is why the other code is correct.
You should stick to the tutorial implementation. There is no point in passing component state to the children and then from them back to the parents. Your state should be only in one place (in this case in Layout).
Child components should be only given access to the information they need which in this case is just showSideDrawer.
You are using this:
toggleSideDrawerHandler = prevState => {
let newState = !prevState.showSideDrawer;
this.setState({ showSideDrawer: newState });
};
This is a conventional way to update state in react, where we are defining the function and updating state inside. Though you are using term prevState but it doesn't holds any value of components states. When you call toggleSideDrawerHandler method you have to pass value and prevState will hold that value. The other case as tutorial is using:
toggleSideDrawerHandler = () => {
this.setState(prevState => {
return { showSideDrawer: !prevState.showSideDrawer };
});
};
This is called functional setStae way of updating state. In this function is used in setState methods first argument. So prevState will have a value equal to all the states in the component.Check the example below to understand the difference between two:
// Example stateless functional component
const SFC = props => (
<div>{props.label}</div>
);
// Example class component
class Thingy extends React.Component {
constructor() {
super();
this.state = {
temp: [],
};
}
componentDidMount(){
this.setState({temp: this.state.temp.concat('a')})
this.setState({temp: this.state.temp.concat('b')})
this.setState({temp: this.state.temp.concat('c')})
this.setState({temp: this.state.temp.concat('d')})
this.setState(prevState => ({temp: prevState.temp.concat('e')}))
this.setState(prevState => ({temp: prevState.temp.concat('f')}))
this.setState(prevState => ({temp: prevState.temp.concat('g')}))
}
render() {
const {title} = this.props;
const {temp} = this.state;
return (
<div>
<div>{title}</div>
<SFC label="I'm the SFC inside the Thingy" />
{ temp.map(value => ( <div>Concating {value}</div> )) }
</div>
);
}
}
// Render it
ReactDOM.render(
<Thingy title="I'm the thingy" />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
So depending on requirement you will use one of the two ways to update the state.

ReactJS - How to expose your Component to external events? [duplicate]

I have two components:
Parent component
Child component
I was trying to call Child's method from Parent, I tried this way but couldn't get a result:
class Parent extends Component {
render() {
return (
<Child>
<button onClick={Child.getAlert()}>Click</button>
</Child>
);
}
}
class Child extends Component {
getAlert() {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
Is there a way to call Child's method from Parent?
Note: Child and Parent components are in two different files.
First off, let me express that this is generally not the way to go about things in React land. Usually what you want to do is pass down functionality to children in props, and pass up notifications from children in events (or better yet: dispatch).
But if you must expose an imperative method on a child component, you can use refs. Remember this is an escape hatch and usually indicates a better design is available.
Previously, refs were only supported for Class-based components.
With the advent of React Hooks, that's no longer the case
Modern React with Hooks (v16.8+)
const { forwardRef, useRef, useImperativeHandle } = React;
// We need to wrap component in `forwardRef` in order to gain
// access to the ref object that is assigned using the `ref` prop.
// This ref is passed as the second parameter to the function component.
const Child = forwardRef((props, ref) => {
// The component instance will be extended
// with whatever you return from the callback passed
// as the second argument
useImperativeHandle(ref, () => ({
getAlert() {
alert("getAlert from Child");
}
}));
return <h1>Hi</h1>;
});
const Parent = () => {
// In order to gain access to the child component instance,
// you need to assign it to a `ref`, so we call `useRef()` to get one
const childRef = useRef();
return (
<div>
<Child ref={childRef} />
<button onClick={() => childRef.current.getAlert()}>Click</button>
</div>
);
};
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
Documentation for useImperativeHandle() is here:
useImperativeHandle customizes the instance value that is exposed to parent components when using ref.
Legacy API using Class Components (>= react#16.4)
const { Component } = React;
class Parent extends Component {
constructor(props) {
super(props);
this.child = React.createRef();
}
onClick = () => {
this.child.current.getAlert();
};
render() {
return (
<div>
<Child ref={this.child} />
<button onClick={this.onClick}>Click</button>
</div>
);
}
}
class Child extends Component {
getAlert() {
alert('getAlert from Child');
}
render() {
return <h1>Hello</h1>;
}
}
ReactDOM.render(<Parent />, document.getElementById('root'));
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
Callback Ref API
Callback-style refs are another approach to achieving this, although not quite as common in modern React:
const { Component } = React;
const { render } = ReactDOM;
class Parent extends Component {
render() {
return (
<div>
<Child ref={instance => { this.child = instance; }} />
<button onClick={() => { this.child.getAlert(); }}>Click</button>
</div>
);
}
}
class Child extends Component {
getAlert() {
alert('clicked');
}
render() {
return (
<h1>Hello</h1>
);
}
}
render(
<Parent />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
You can use another pattern here:
class Parent extends Component {
render() {
return (
<div>
<Child setClick={click => this.clickChild = click}/>
<button onClick={() => this.clickChild()}>Click</button>
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.getAlert = this.getAlert.bind(this);
}
componentDidMount() {
this.props.setClick(this.getAlert);
}
getAlert() {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
What it does is to set the parent's clickChild method when child is mounted. In this way when you click the button in parent it will call clickChild which calls child's getAlert.
This also works if your child is wrapped with connect() so you don't need the getWrappedInstance() hack.
Note you can't use onClick={this.clickChild} in parent because when parent is rendered child is not mounted so this.clickChild is not assigned yet. Using onClick={() => this.clickChild()} is fine because when you click the button this.clickChild should already be assigned.
Alternative method with useEffect:
Parent:
const [refresh, doRefresh] = useState(0);
<Button onClick={() => doRefresh(prev => prev + 1)} />
<Children refresh={refresh} />
Children:
useEffect(() => {
performRefresh(); //children function of interest
}, [props.refresh]);
Here I will give you the four possible combinations that can happen:
Class Parent | Hook Child
Hook Parent | Class Child
Hook Parent | Hook Child
Class Parent | Class Child
1. Class Parent | Hook Child
class Parent extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}
render() {
return (<View>
<Child ref={this.myRef}/>
<Button title={'call me'}
onPress={() => this.myRef.current.childMethod()}/>
</View>)
}
}
const Child = React.forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
childMethod() {
childMethod()
}
}))
function childMethod() {
console.log('call me')
}
return (<View><Text> I am a child</Text></View>)
})
2. Hook Parent | Class Child
function Parent(props) {
const myRef = useRef()
return (<View>
<Child ref={myRef}/>
<Button title={'call me'}
onPress={() => myRef.current.childMethod()}/>
</View>)
}
class Child extends React.Component {
childMethod() {
console.log('call me')
}
render() {
return (<View><Text> I am a child</Text></View>)
}
}
3. Hook Parent | Hook Child
function Parent(props) {
const myRef = useRef()
return (<View>
<Child ref={myRef}/>
<Button title={'call me'}
onPress={() => myRef.current.childMethod()}/>
</View>)
}
const Child = React.forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
childMethod() {
childMethod()
}
}))
function childMethod() {
console.log('call me')
}
return (<View><Text> I am a child</Text></View>)
})
4. Class Parent | Class Child
class Parent extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}
render() {
return (<View>
<Child ref={this.myRef}/>
<Button title={'call me'}
onPress={() => this.myRef.current.childMethod()}/>
</View>)
}
}
class Child extends React.Component {
childMethod() {
console.log('call me')
}
render() {
return (<View><Text> I am a child</Text></View>)
}
}
https://facebook.github.io/react/tips/expose-component-functions.html
for more answers ref here Call methods on React children components
By looking into the refs of the "reason" component, you're breaking encapsulation and making it impossible to refactor that component without carefully examining all the places it's used. Because of this, we strongly recommend treating refs as private to a component, much like state.
In general, data should be passed down the tree via props. There are a few exceptions to this (such as calling .focus() or triggering a one-time animation that doesn't really "change" the state) but any time you're exposing a method called "set", props are usually a better choice. Try to make it so that the inner input component worries about its size and appearance so that none of its ancestors do.
I wasn't satisfied with any of the solutions presented here. There is actually a very simple solution that can be done using pure Javascript without relying upon some React functionality other than the basic props object - and it gives you the benefit of communicating in either direction (parent -> child, child -> parent). You need to pass an object from the parent component to the child component. This object is what I refer to as a "bi-directional reference" or biRef for short. Basically, the object contains a reference to methods in the parent that the parent wants to expose. And the child component attaches methods to the object that the parent can call. Something like this:
// Parent component.
function MyParentComponent(props) {
function someParentFunction() {
// The child component can call this function.
}
function onButtonClick() {
// Call the function inside the child component.
biRef.someChildFunction();
}
// Add all the functions here that the child can call.
var biRef = {
someParentFunction: someParentFunction
}
return <div>
<MyChildComponent biRef={biRef} />
<Button onClick={onButtonClick} />
</div>;
}
// Child component
function MyChildComponent(props) {
function someChildFunction() {
// The parent component can call this function.
}
function onButtonClick() {
// Call the parent function.
props.biRef.someParentFunction();
}
// Add all the child functions to props.biRef that you want the parent
// to be able to call.
props.biRef.someChildFunction = someChildFunction;
return <div>
<Button onClick={onButtonClick} />
</div>;
}
The other advantage to this solution is that you can add a lot more functions in the parent and child while passing them from the parent to the child using only a single property.
An improvement over the code above is to not add the parent and child functions directly to the biRef object but rather to sub members. Parent functions should be added to a member called "parent" while the child functions should be added to a member called "child".
// Parent component.
function MyParentComponent(props) {
function someParentFunction() {
// The child component can call this function.
}
function onButtonClick() {
// Call the function inside the child component.
biRef.child.someChildFunction();
}
// Add all the functions here that the child can call.
var biRef = {
parent: {
someParentFunction: someParentFunction
}
}
return <div>
<MyChildComponent biRef={biRef} />
<Button onClick={onButtonClick} />
</div>;
}
// Child component
function MyChildComponent(props) {
function someChildFunction() {
// The parent component can call this function.
}
function onButtonClick() {
// Call the parent function.
props.biRef.parent.someParentFunction();
}
// Add all the child functions to props.biRef that you want the parent
// to be able to call.
props.biRef {
child: {
someChildFunction: someChildFunction
}
}
return <div>
<Button onClick={onButtonClick} />
</div>;
}
By placing parent and child functions into separate members of the biRef object, you 'll have a clean separation between the two and easily see which ones belong to parent or child. It also helps to prevent a child component from accidentally overwriting a parent function if the same function appears in both.
One last thing is that if you note, the parent component creates the biRef object with var whereas the child component accesses it through the props object. It might be tempting to not define the biRef object in the parent and access it from its parent through its own props parameter (which might be the case in a hierarchy of UI elements). This is risky because the child may think a function it is calling on the parent belongs to the parent when it might actually belong to a grandparent. There's nothing wrong with this as long as you are aware of it. Unless you have a reason for supporting some hierarchy beyond a parent/child relationship, it's best to create the biRef in your parent component.
I hope I'm not repeating anything from above but what about passing a callback prop that sets the function in the parent? This works and is pretty easy. (Added code is between the ////'s)
class Parent extends Component {
/////
getAlert = () => {} // initial value for getAlert
setGetAlertMethod = (newMethod) => {
this.getAlert = newMethod;
}
/////
render() {
return (
<Child setGetAlertMethod={this.setGetAlertMethod}>
<button onClick={this.getAlert}>Click</button>
</Child>
);
}
}
class Child extends Component {
/////
componentDidMount() {
this.props.setGetAlertMethod(this.getAlert);
}
/////
getAlert() => {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}
you can use ref to call the function of the child component from the parent
Functional Component Solution
in functional component, you have to use useImperativeHandle for getting ref into a child like below
import React, { forwardRef, useRef, useImperativeHandle } from 'react';
export default function ParentFunction() {
const childRef = useRef();
return (
<div className="container">
<div>
Parent Component
</div>
<button
onClick={() => { childRef.current.showAlert() }}
>
Call Function
</button>
<Child ref={childRef}/>
</div>
)
}
const Child = forwardRef((props, ref) => {
useImperativeHandle(
ref,
() => ({
showAlert() {
alert("Child Function Called")
}
}),
)
return (
<div>Child Component</div>
)
})
Class Component Solution
Child.js
import s from './Child.css';
class Child extends Component {
getAlert() {
alert('clicked');
}
render() {
return (
<h1>Hello</h1>
);
}
}
export default Child;
Parent.js
class Parent extends Component {
render() {
onClick() {
this.refs.child.getAlert();
}
return (
<div>
<Child ref="child" />
<button onClick={this.onClick}>Click</button>
</div>
);
}
}
I'm using useEffect hook to overcome the headache of doing all this so now I pass a variable down to child like this:
import { useEffect, useState } from "react";
export const ParentComponent = () => {
const [trigger, setTrigger] = useState(false);
return (
<div onClick={() => { setTrigger(trigger => !trigger); }}>
<ChildComponent trigger={trigger}></ChildComponent>
</div>
);
};
export const ChildComponent = (props) => {
const triggerInvokedFromParent = () => {
console.log('TriggerInvokedFromParent');
};
useEffect(() => {
triggerInvokedFromParent();
}, [props.trigger]);
return <span>ChildComponent</span>;
};
We can use refs in another way as-
We are going to create a Parent element, it will render a <Child/> component. As you can see, the component that will be rendered, you need to add the ref attribute and provide a name for it.
Then, the triggerChildAlert function, located in the parent class will access the refs property of the this context (when the triggerChildAlert function is triggered will access the child reference and it will has all the functions of the child element).
class Parent extends React.Component {
triggerChildAlert(){
this.refs.child.callChildMethod();
// to get child parent returned value-
// this.value = this.refs.child.callChildMethod();
// alert('Returned value- '+this.value);
}
render() {
return (
<div>
{/* Note that you need to give a value to the ref parameter, in this case child*/}
<Child ref="child" />
<button onClick={this.triggerChildAlert}>Click</button>
</div>
);
}
}
Now, the child component, as theoretically designed previously, will look like:
class Child extends React.Component {
callChildMethod() {
alert('Hello World');
// to return some value
// return this.state.someValue;
}
render() {
return (
<h1>Hello</h1>
);
}
}
Here is the source code-
Hope will help you !
If you are doing this simply because you want the Child to provide a re-usable trait to its parents, then you might consider doing that using render-props instead.
That technique actually turns the structure upside down. The Child now wraps the parent, so I have renamed it to AlertTrait below. I kept the name Parent for continuity, although it is not really a parent now.
// Use it like this:
<AlertTrait renderComponent={Parent}/>
class AlertTrait extends Component {
// You will need to bind this function, if it uses 'this'
doAlert() {
alert('clicked');
}
render() {
return this.props.renderComponent({ doAlert: this.doAlert });
}
}
class Parent extends Component {
render() {
return (
<button onClick={this.props.doAlert}>Click</button>
);
}
}
In this case, the AlertTrait provides one or more traits which it passes down as props to whatever component it was given in its renderComponent prop.
The Parent receives doAlert as a prop, and can call it when needed.
(For clarity, I called the prop renderComponent in the above example. But in the React docs linked above, they just call it render.)
The Trait component can render stuff surrounding the Parent, in its render function, but it does not render anything inside the parent. Actually it could render things inside the Parent, if it passed another prop (e.g. renderChild) to the parent, which the parent could then use during its render method.
This is somewhat different from what the OP asked for, but some people might end up here (like we did) because they wanted to create a reusable trait, and thought that a child component was a good way to do that.
For functional components easiest way is
Parent Component
parent.tsx
import React, { useEffect, useState, useRef } from "react";
import child from "../../child"
const parent: React.FunctionComponent = () => {
const childRef: any = useRef();
}
const onDropDownChange: any = (event): void => {
const target = event.target;
childRef.current.onFilterChange(target.value);
};
return <child ref={childRef} />
export default parent;
Child Component
child.tsx
import React, { useState, useEffect, forwardRef, useRef, useImperativeHandle, } from "react";
const Child = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
onFilterChange(id) {
console.log("Value from parent", id)
},
}));
})
Child.displayName = "Child";
export default Child;
The logic is simple.
Create a function in parent using child or use ref.
I prefer the creating function in parent using child. There are multiple ways to do it.
When using functional components
In Parent
function Parent(){
const [functionToCall, createFunctionToCall] = useState(()=>()=>{})
return (
<Child createFunctionToCall={createFunctionToCall} />
)
}
In Child
function Child({createFunctionToCall}){
useEffect(()=>{
function theFunctionToCall(){
// do something like setting something
// don't forget to set dependancies properly.
}
createFunctionToCall(()=>theFunctionToCall)
},[createFunctionToCall])
}
This pattern is similar to #brickingup answer. But in this version you can set as many child actions you want.
import { useEffect } from "react";
export const Parent = () => {
const childEvents = { click: () => {} };
return (
<div onClick={() => childEvents.click()}>
<Child events={childEvents}></Child>
</div>
);
};
export const Child = (props) => {
const click = () => {
alert("click from child");
};
useEffect(() => {
if (props.events) {
props.events.click = click;
}
}, []);
return <span>Child Component</span>;
};
We're happy with a custom hook we call useCounterKey. It just sets up a counterKey, or a key that counts up from zero. The function it returns resets the key (i.e. increment). (I believe this is the most idiomatic way in React to reset a component - just bump the key.)
However this hook also works in any situation where you want to send a one-time message to the client to do something. E.g. we use it to focus a control in the child on a certain parent event - it just autofocuses anytime the key is updated. (If more props are needed they could be set prior to resetting the key so they're available when the event happens.)
This method has a bit of a learning curve b/c it's not as straightforward as a typical event handler, but it seems the most idiomatic way to handle this in React that we've found (since keys already function this way). Def open to feedback on this method but it is working well!
// Main helper hook:
export function useCounterKey() {
const [key, setKey] = useState(0);
return [key, () => setKey(prev => prev + 1)] as const;
}
Sample usages:
// Sample 1 - normal React, just reset a control by changing Key on demand
function Sample1() {
const [inputLineCounterKey, resetInputLine] = useCounterKey();
return <>
<InputLine key={inputLineCounterKey} />
<button onClick={() => resetInputLine()} />
<>;
}
// Second sample - anytime the counterKey is incremented, child calls focus() on the input
function Sample2() {
const [amountFocusCounterKey, focusAmountInput] = useCounterKey();
// ... call focusAmountInput in some hook or event handler as needed
return <WorkoutAmountInput focusCounterKey={amountFocusCounterKey} />
}
function WorkoutAmountInput(props) {
useEffect(() => {
if (counterKey > 0) {
// Don't focus initially
focusAmount();
}
}, [counterKey]);
// ...
}
(Credit to Kent Dodds for the counterKey concept.)
Parent component
import Child from './Child'
export default function Parent(props) {
const [childRefreshFunction, setChildRefreshFunction] = useState(null);
return (
<div>
<button type="button" onClick={() => {
childRefreshFunction();
}}>Refresh child</button>
<Child setRefreshFunction={(f) => {
setChildRefreshFunction(f);
}} />
</div>
)
}
Child component
export default function Child(props) {
useEffect(() => {
props.setRefreshFunction(() => refreshMe);
}, []);
function refreshMe() {
fetch('http://example.com/data.json')....
};
return (
<div>
child
</div>
)
}
You can achieve this easily in this way
Steps-
Create a boolean variable in the state in the parent class. Update this when you want to call a function.
Create a prop variable and assign the boolean variable.
From the child component access that variable using props and execute the method you want by having an if condition.
class Child extends Component {
Method=()=>{
--Your method body--
}
render() {
return (
//check whether the variable has been updated or not
if(this.props.updateMethod){
this.Method();
}
)
}
}
class Parent extends Component {
constructor(){
this.state={
callMethod:false
}
}
render() {
return (
//update state according to your requirement
this.setState({
callMethod:true
}}
<Child updateMethod={this.state.callMethod}></Child>
);
}
}
Another way of triggering a child function from parent is to make use of the componentDidUpdate function in child Component. I pass a prop triggerChildFunc from Parent to Child, which initially is null. The value changes to a function when the button is clicked and Child notice that change in componentDidUpdate and calls its own internal function.
Since prop triggerChildFunc changes to a function, we also get a callback to the Parent. If Parent don't need to know when the function is called the value triggerChildFunc could for example change from null to true instead.
const { Component } = React;
const { render } = ReactDOM;
class Parent extends Component {
state = {
triggerFunc: null
}
render() {
return (
<div>
<Child triggerChildFunc={this.state.triggerFunc} />
<button onClick={() => {
this.setState({ triggerFunc: () => alert('Callback in parent')})
}}>Click
</button>
</div>
);
}
}
class Child extends Component {
componentDidUpdate(prevProps) {
if (this.props.triggerChildFunc !== prevProps.triggerChildFunc) {
this.onParentTrigger();
}
}
onParentTrigger() {
alert('parent triggered me');
// Let's call the passed variable from parent if it's a function
if (this.props.triggerChildFunc && {}.toString.call(this.props.triggerChildFunc) === '[object Function]') {
this.props.triggerChildFunc();
}
}
render() {
return (
<h1>Hello</h1>
);
}
}
render(
<Parent />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='app'></div>
CodePen: https://codepen.io/calsal/pen/NWPxbJv?editors=1010
Here my demo: https://stackblitz.com/edit/react-dgz1ee?file=styles.css
I am using useEffect to call the children component's methods. I have tried with Proxy and Setter_Getter but sor far useEffect seems to be the more convenient way to call a child method from parent. To use Proxy and Setter_Getter it seems there is some subtlety to overcome first, because the element firstly rendered is an objectLike's element through the ref.current return => <div/>'s specificity.
Concerning useEffect, you can also leverage on this approach to set the parent's state depending on what you want to do with the children.
In the demo's link I have provided, you will find my full ReactJS' code with my draftwork inside's so you can appreciate the workflow of my solution.
Here I am providing you my ReactJS' snippet with the relevant code only. :
import React, {
Component,
createRef,
forwardRef,
useState,
useEffect
} from "react";
{...}
// Child component
// I am defining here a forwardRef's element to get the Child's methods from the parent
// through the ref's element.
let Child = forwardRef((props, ref) => {
// I am fetching the parent's method here
// that allows me to connect the parent and the child's components
let { validateChildren } = props;
// I am initializing the state of the children
// good if we can even leverage on the functional children's state
let initialState = {
one: "hello world",
two: () => {
console.log("I am accessing child method from parent :].");
return "child method achieve";
}
};
// useState initialization
const [componentState, setComponentState] = useState(initialState);
// useEffect will allow me to communicate with the parent
// through a lifecycle data flow
useEffect(() => {
ref.current = { componentState };
validateChildren(ref.current.componentState.two);
});
{...}
});
{...}
// Parent component
class App extends Component {
// initialize the ref inside the constructor element
constructor(props) {
super(props);
this.childRef = createRef();
}
// I am implementing a parent's method
// in child useEffect's method
validateChildren = childrenMethod => {
// access children method from parent
childrenMethod();
// or signaling children is ready
console.log("children active");
};
{...}
render(){
return (
{
// I am referencing the children
// also I am implementing the parent logic connector's function
// in the child, here => this.validateChildren's function
}
<Child ref={this.childRef} validateChildren={this.validateChildren} />
</div>
)
}
You can apply that logic very easily using your child component as a react custom hook.
How to implement it?
Your child returns a function.
Your child returns a JSON: {function, HTML, or other values} as the example.
In the example doesn't make sense to apply this logic but it is easy to see:
const {useState} = React;
//Parent
const Parent = () => {
//custome hook
const child = useChild();
return (
<div>
{child.display}
<button onClick={child.alert}>
Parent call child
</button>
{child.btn}
</div>
);
};
//Child
const useChild = () => {
const [clickCount, setClick] = React.useState(0);
{/* child button*/}
const btn = (
<button
onClick={() => {
setClick(clickCount + 1);
}}
>
Click me
</button>
);
return {
btn: btn,
//function called from parent
alert: () => {
alert("You clicked " + clickCount + " times");
},
display: <h1>{clickCount}</h1>
};
};
const rootElement = document.getElementById("root");
ReactDOM.render(<Parent />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I tried using createRef or useRef. Somehow they all return null.
Secondly, this answer proposes to pass a prop that sets a function that seems the most reasonable to me. But if your child component is used in multiple places, you should add that extra prop to other places. Also if you want to call a method in the grandchild, this method might be too verbose or mouthful.
So I made my own function store in a very primitive way.
Below is functionStore.js file
const fns = {};
export function setFn(componentName, fnName, fn) {
if (fns[componentName]) {
fns[componentName][fnName] = fn;
} else {
fns[componentName] = { fnName: fn };
}
}
export function callFn(componentName, fnName) {
fns[componentName][fnName]();
}
I just set the functions that need to be called from any component.
import { setFn } from "./functionStore";
export class AComponent extends React.Component {
componentDidMount() {
setFn("AComponent", "aFunc", this.aFunc);
}
aFunc = () => { console.log("aFunc is called!"); };
}
Then I just call it from some other component
import { callFn } from "./functionStore";
export class BComponent extends React.Component {
// just call the function
bFunc = () => {
callFn("AComponent", "aFunc");
};
}
One disadvantage is the function to be called should be parameterless. But this might be fixed somehow as well. Currently, I don't need to pass parameters.
I think that the most basic way to call methods is by setting a request on the child component. Then as soon as the child handles the request, it calls a callback method to reset the request.
The reset mechanism is necessary to be able to send the same request multiple times after each other.
In parent component
In the render method of the parent:
const { request } = this.state;
return (<Child request={request} onRequestHandled={()->resetRequest()}/>);
The parent needs 2 methods, to communicate with its child in 2 directions.
sendRequest() {
const request = { param: "value" };
this.setState({ request });
}
resetRequest() {
const request = null;
this.setState({ request });
}
In child component
The child updates its internal state, copying the request from the props.
constructor(props) {
super(props);
const { request } = props;
this.state = { request };
}
static getDerivedStateFromProps(props, state) {
const { request } = props;
if (request !== state.request ) return { request };
return null;
}
Then finally it handles the request, and sends the reset to the parent:
componentDidMount() {
const { request } = this.state;
// todo handle request.
const { onRequestHandled } = this.props;
if (onRequestHandled != null) onRequestHandled();
}
Here's a bug? to look out for:
I concur with rossipedia's solution using forwardRef, useRef, useImperativeHandle
There's some misinformation online that says refs can only be created from React Class components, but you can indeed use Function Components if you use the aforementioned hooks above. A note, the hooks only worked for me after I changed the file to not use withRouter() when exporting the component. I.e. a change from
export default withRouter(TableConfig);
to instead be
export default TableConfig;
In hindsight the withRouter() is not needed for such a component anyway, but usually it doesn't hurt anything having it in. My use case is that I created a component to create a Table to handle the viewing and editing of config values, and I wanted to be able to tell this Child component to reset it's state values whenever the Parent form's Reset button was hit. UseRef() wouldn't properly get the ref or ref.current (kept on getting null) until I removed withRouter() from the file containing my child component TableConfig

React dynamically rendering component (object assign vs. function return)

I've hit a brick wall here, meaning I can't fully figure out why the next two versions of code behave so differently.
In the first version, when I'm initialising a this.childComponent = (<ChildComp />), its props do not seem to update when I change the Parent's state (via setState()). This happens even though the setState() is actually called, and the Parent's state is updated.
In the second version, when I'm actually initialising a function that returns the component (this.childComponent = () => {return (<ChildComp />)}), everything works like a charm, props are updated.
I am using the second version (since it works), but I'd like to understand why this works and the first one doesn't.
Here's the child component:
class Child extends React.Component {
render() {
return (
<button onClick=(this.props.setValue())>
{this.props.value}
</button>
)
}
}
I have the next two versions of the parent component:
1.
class Parent extends React.Component {
constructor() {
this.state = {
value: 1
}
this.childComponent = (
<Child value={this.state.value}
setValue={() => this.setValue()}/>
)
}
setValue() {
this.setState({value: 2})
}
render () {
return ( {this.childComponent} )
}
}
2. (the this.childComponent is now a function that returns the react Element)
class Parent extends React.Component {
constructor() {
this.state = {
value: 1
}
this.childComponent = () => {
return (
<Child value={this.state.value}
setValue={() => this.setValue()}/>
)
}
}
setValue() {
this.setState({value: 2})
}
render () {
return ( {this.childComponent()} )
}
}
I've tried to simplify the code so my issue is easier to understand.
Thank you in advance
React uses a strategy called reconciliation to efficiently update the DOM every time there's a change in its internal state. Typically, this happens after a setState call.
In your first example, the render method inside the Parent component always returns the same Child component, as it's created only once in the constructor. Because of this, the reconciliation algorithm doesn't find any changes since there aren't any.
I'd like to point out that <Child value={this.state.value} setValue={() => this.setValue()}/> is just syntactic sugar for React.createElement(Child, {value: this.state.value, setValue: () => this.setValue()}, null). createElement simply returns an object.
In your second example, with every render call, you're calling childComponent which in turns create a new Child component.
You don't have a return in the first case since
this.childComponent = (
<Child value={this.state.value}
setValue={() => this.setValue()}/>
)
was defined in the constructor which is executed only once and is a static value now.
whereas it will work as it is a function that is executed everytime it is called.
If you want to go by the first method, define the childcomponent in the render rather than the constructor since render is called on every change. Your code also had a lot of mistakes. See the working snippet below
class Parent extends React.Component {
constructor() {
super();
this.state = {
value: 1
}
}
setValue() {
this.setState({value: 2})
}
render () {
const childComponent = (
<Child value={this.state.value}
setValue={() => this.setValue()}/>
)
return ( <div>{childComponent}</div> )
}
}
class Child extends React.Component {
render() {
return (
<button onClick={this.props.setValue}>
{this.props.value}
</button>
)
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

React different ways of calling a child method

React says we should not use refs where possible and I noticed that you can't use shallow rendering testing with refs so I have tried to remove refs where possible. I have a child component like this:
class Child extends React.Component {
play = () => {
//play the media
},
pause = () => {
//pause the media
},
setMedia = (newMedia) => {
//set the new media
}
}
I then have a parent component that needs to call these methods. For the setMedia I can just use props with the componentWillReceiveProps and call setMedia when the new props come in to the child.
With the play and pause functions I cannot do this.
Ben Alpert replied to this post and said:
In general, data should be passed down the tree via props. There are a few exceptions to this (such as calling .focus() or triggering a one-time animation that doesn't really "change" the state) but any time you're exposing a method called "set", props are usually a better choice. Try to make it so that the inner input component worries about its size and appearance so that none of its ancestors do.
Which is the best way to call a child function?
play() and pause() methods can be called from refs as they do not change the state just like focus() and use props for the other functions that have arguments.
Call the child functions by passing the method name in although this just seems hacky and a lot more complex:
class Child extends React.Component {
play = () => {
//play the media
},
pause = () => {
//pause the media
},
setMedia = (newMedia) => {
//set the new media
},
_callFunctions = (functions) => {
if (!functions.length) {
return;
}
//call each new function
functions.forEach((func) => this[func]());
//Empty the functions as they have been called
this.props.updateFunctions({functions: []});
}
componentWillReceiveProps(nextProps) {
this._callFunctions(nextProps.functions);
}
}
class Parent extends React.Component {
updateFunctions = (newFunctions) => this.setState({functions: newFunctions});
differentPlayMethod = () => {
//...Do other stuff
this.updateFunctions("play");
}
render() {
return (
<Child updateFunctions={this.updateFunctions}/>
);
}
}
Do this in the child component: this.props.updateFunctions({play: this.play});
The problem with this is that we are exposing(copying) a method to another component that shouldn't really know about it...
Which is the best way to do this?
I am using method number 2 at the moment and I don't really like it.
To override child functions I have also done something similar to above. Should I just use refs instead?
Rather than call child functions, try to pass data and functions down from the parent. Alongside your component, you can export a wrapper or higher order function that provides the necessary state / functions.
let withMedia = Wrapped => {
return class extends React.Component {
state = { playing: false }
play() { ... }
render() {
return (
<Wrapped
{...this.state}
{...this.props}
play={this.play}
/>
)
}
}
}
Then in your parent component:
import { Media, withMedia } from 'your-library'
let Parent = props =>
<div>
<button onClick={props.play}>Play</button>
<Media playing={props.playing} />
</div>
export default withMedia(Parent)
Keep the state as localized as you can, but don't spread it over multiple components. If you need the information whether it is currently playing in both the parent and the child, keep the state in the parent.
This leaves you with a much cleaner state tree and props:
class Child extends React.Component {
render() {
return (
<div>
<button onClick={this.props.togglePlay}>Child: Play/Pause</button>
<p>Playing: {this.props.playing ? 'Yes' : 'No'}</p>
</div>
);
}
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.togglePlay = this.togglePlay.bind(this);
this.state = {
playing: false
};
}
togglePlay() {
this.setState({
playing: !this.state.playing
});
}
render() {
return (
<div>
<button onClick={this.togglePlay}>Parent: Play/Pause</button>
<Child togglePlay={this.togglePlay} playing={this.state.playing} />
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app'></div>

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?
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.
See the code comment why I don't recommend this approach.
const Child = ({ childName, sayHello }) => (
<button onClick={() => sayHello(childName)}>{childName}</button>
);
function Parent({ children }) {
// We pass this `sayHello` function into the child elements.
function sayHello(childName) {
console.log(`Hello from ${childName} the child`);
}
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, { sayHello });
}
return child;
});
return <div>{childrenWithProps}</div>
}
function App() {
// This approach is less type-safe and Typescript friendly since it
// looks like you're trying to render `Child` without `sayHello`.
// It's also confusing to readers of this code.
return (
<Parent>
<Child childName="Billy" />
<Child childName="Bob" />
</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 via 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 actual children:
const Child = ({ childName, sayHello }) => (
<button onClick={() => sayHello(childName)}>{childName}</button>
);
function Parent({ children }) {
function sayHello(childName) {
console.log(`Hello from ${childName} the child`);
}
// `children` of this component must be a function
// which returns the actual children. We can pass
// it args to then pass into them as props (in this
// case we pass `sayHello`).
return <div>{children(sayHello)}</div>
}
function App() {
// sayHello is the arg we passed in Parent, which
// we now pass through to Child.
return (
<Parent>
{(sayHello) => (
<React.Fragment>
<Child childName="Billy" sayHello={sayHello} />
<Child childName="Bob" sayHello={sayHello} />
</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>
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>
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
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
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 = React.useCallback((value) => {
// Do something here with value
}, []);
return (
<MyContext.Provider value={{ doSomething }}>
{props.children}
</MyContext.Provider>
);
}
function Child(props: { value: number }) {
const myContext = React.useContext(MyContext);
const onClick = React.useCallback(() => {
myContext.doSomething(props.value);
}, [props.value, myContext.doSomething]);
return (
<div onClick={onClick}>{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>
);
}
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>
)
}
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...
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
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>;
};
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>
}
Cleaner way considering one or more children
<div>
{ React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
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>
);
}
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
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}</>;
};
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
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>
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:
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>
The slickest way to do this:
{React.cloneElement(this.props.children, this.props)}
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:
<element.type {...element.props} {...props}>{children}</element.type>
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>
)
}
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>
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}
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>
)
}
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. 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.
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
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
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>
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

Categories