ReactJS - How to expose your Component to external events? [duplicate] - javascript
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
Related
How to access a function in a child class component from a stateless parent?
I have a redux-store with objects of initial values. And this store will get updated at a few places within the child component. I created a stateless functional component as parent const Parent = () => { const store = useSelector(state => state); const getInitState = () => { depends on store, it will return an object as initial state for child component } let initState = getInitState(); //it has to be let instead of const, it could be changed during useEffect useEffect(() => { some initialization on mount }, []) return ( // return is simplified here <Child initState={iniState} /> ) } export default Parent; I have a class child component something like below class Child extends Component { state = { componentState: this.props.initState } .... } export default Child; I can't modify the child component It's a very complex component with many sub components which I dont handle. Now I need to access setState function of child component from parent. Or I need to change the state of child from parent, is there a way to do that? Yes, I understand a new design should be consider since it's anti-pattern, but I am just wondering if I can do it under current setting. Thank you all in advance. ============================================================== Edit: For whoever runs into the same problem, functional component does not support constructor. So I have included a breif correction to the answer. Define parent as below import React, { useRef } from "react"; const Parent = () => { const childRef = useRef(null); return ( <Child ref={childRef} /> ) } export default Parent; Then you are able to use childRef.current to access all function from child component.
The best way is using a react Context , and set state in parent then the child consume state of parent (using react hooks would be so easy than class component) but in your case as you mentiened (I wonder I can do it under current setting) you can use react refs : first put ref prop in your rendered component tag then use it in parent to execute function that's declared inside child as below : inside parent component : const Parent = () => { . . . constructor() { //create react ref for our component this.childComponent = React.createRef(); } callChildFunction() { // here using refs you can access function in you child refenrenced component this.childComponent.cuurent.doSomeUpdateStateStuff(newState); } return ( // return is simplified here <Child ref={this.childComponen} initState={iniState} /> ) ... } and your child : class Child extends Component { state = { componentState: this.props.initState } doSomeUpdateStateStuff(state) { // stuff updating state callled from parent } .... }
Manage Focus on stateless Components - React
Having a container Component which hold state. it renders a number of stateless components. I want to get access to all of their DOM nodes, so i can call the focus method on demand. I am trying the ref approach as it is encouraged by the react documentation. I'm getting the following error: Warning: Stateless function components cannot be given refs. Attempts to access this ref will fail. What is the recommended way to get around this error? preferably, without extra dom elements wrappers like exra divs. Here is my Current Code: Container Component - responsible for rendering the stateless components. import React, { Component } from 'react'; import StatelessComponent from './components/stateless-component.jsx' class Container extends Component { constructor() { super() this.focusOnFirst = this.focusOnFirst.bind(this) this.state = { words: [ 'hello', 'goodbye', 'see ya' ] } } focusOnFirst() { this.node1.focus() } render() { return ( <div> { this.state.words.map((word,index)=>{ return <StatelessComponent value={word} ref={node => this[`node${index}`] = node}/> }) } <button onClick={this.focusOnFirst}>Focus on First Stateless Component</button> </div> ) } } Stateless Component - for sake of simplicity, just display a text inside a div. import React from 'react'; export default function StatelessComponent(props) { return <div>props.value</div> }
Stateless (functional) components can't expose refs directly. However, if their internal components can use refs, you can pass a function from the parent (the grandparent) of the stateless component (the parent) as a ref via ref forwarding. Use the function as the ref target of the DOM element. Now the grandparent has direct access to the DOM element ref. See Exposing DOM Refs to Parent Components in React's documentation. const StatelessComponent = React.forwardRef((props, ref) => ( <div> <input ref={ref} {...props} /> </div> )); class Container extends React.Component { itemRefs = [] componentDidMount() { this.focusOnFirst(); } focusOnFirst = () => this.itemRefs[0].focus() inputRef = (ref) => this.itemRefs.push(ref) render() { return ( <div> <StatelessComponent ref={this.inputRef} /> <StatelessComponent ref={this.inputRef} /> </div> ) } } ReactDOM.render( <Container />, demo ) <script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script> <div id="demo"></div>
Try this, basically you pass a callback as ref to the stateless components that gets the input instance an adds it to an array owned by the container class Container extends Component { constructor() { super() this._inputs = []; this.focusOnFirst = this.focusOnFirst.bind(this) this.state = { words: [ 'hello', 'goodbye', 'see ya' ] } } focusOnFirst() { this._inputs[0].focus() } refCallback(ref) { this._inputs.push(ref) } render() { return ( <div> { this.state.words.map((word,index)=>{ return <StatelessComponent value={word} refCallback={this.refCallback}/> }) } <button onClick={this.focusOnFirst}>Focus on First Stateless Component</button> </div> ) } } And the stateless get modified a little too function StatelessComponent({refCallback, value}) { return <input ref={refCallback} value={value}/> } Here's a working plunker
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 can I update the parent's state in React?
My structure looks as follows: Component 1 - |- Component 2 - - |- Component 4 - - - |- Component 5 Component 3 Component 3 should display some data depending on state of Component 5. Since props are immutable, I can't simply save its state in Component 1 and forward it, right? And yes, I've read about Redux, but I don't want to use it. I hope that it's possible to solve it just with react. Am I wrong?
For child-parent communication you should pass a function setting the state from parent to child, like this class Parent extends React.Component { constructor(props) { super(props) this.handler = this.handler.bind(this) } handler() { this.setState({ someVar: 'some value' }) } render() { return <Child handler = {this.handler} /> } } class Child extends React.Component { render() { return <Button onClick = {this.props.handler}/ > } } This way the child can update the parent's state with the call of a function passed with props. But you will have to rethink your components' structure, because as I understand components 5 and 3 are not related. One possible solution is to wrap them in a higher level component which will contain the state of both component 1 and 3. This component will set the lower level state through props.
This is how to do it with the new useState hook. Method - Pass the state changer function as a props to the child component and do whatever you want to do with the function: import React, {useState} from 'react'; const ParentComponent = () => { const[state, setState]=useState(''); return( <ChildComponent stateChanger={setState} /> ) } const ChildComponent = ({stateChanger, ...rest}) => { return( <button onClick={() => stateChanger('New data')}></button> ) }
I found the following working solution to pass the onClick function argument from the child to the parent component: Version with passing a method() //ChildB component class ChildB extends React.Component { render() { var handleToUpdate = this.props.handleToUpdate; return (<div><button onClick={() => handleToUpdate('someVar')}> Push me </button> </div>) } } //ParentA component class ParentA extends React.Component { constructor(props) { super(props); var handleToUpdate = this.handleToUpdate.bind(this); var arg1 = ''; } handleToUpdate(someArg){ alert('We pass argument from Child to Parent: ' + someArg); this.setState({arg1:someArg}); } render() { var handleToUpdate = this.handleToUpdate; return (<div> <ChildB handleToUpdate = {handleToUpdate.bind(this)} /></div>) } } if(document.querySelector("#demo")){ ReactDOM.render( <ParentA />, document.querySelector("#demo") ); } Look at JSFiddle Version with passing an Arrow function //ChildB component class ChildB extends React.Component { render() { var handleToUpdate = this.props.handleToUpdate; return (<div> <button onClick={() => handleToUpdate('someVar')}> Push me </button> </div>) } } //ParentA component class ParentA extends React.Component { constructor(props) { super(props); } handleToUpdate = (someArg) => { alert('We pass argument from Child to Parent: ' + someArg); } render() { return (<div> <ChildB handleToUpdate = {this.handleToUpdate} /></div>) } } if(document.querySelector("#demo")){ ReactDOM.render( <ParentA />, document.querySelector("#demo") ); } Look at JSFiddle
I want to thank the most upvoted answer for giving me the idea of my own problem basically the variation of it with arrow function and passing param from child component: class Parent extends React.Component { constructor(props) { super(props) // without bind, replaced by arrow func below } handler = (val) => { this.setState({ someVar: val }) } render() { return <Child handler = {this.handler} /> } } class Child extends React.Component { render() { return <Button onClick = {() => this.props.handler('the passing value')}/ > } } Hope it helps someone.
I like the answer regarding passing functions around. It's a very handy technique. On the flip side you can also achieve this using pub/sub or using a variant, a dispatcher, as Flux does. The theory is super simple. Have component 5 dispatch a message which component 3 is listening for. Component 3 then updates its state which triggers the re-render. This requires stateful components, which, depending on your viewpoint, may or may not be an anti-pattern. I'm against them personally and would rather that something else is listening for dispatches and changes state from the very top-down (Redux does this, but it adds additional terminology). import { Dispatcher } from 'flux' import { Component } from 'React' const dispatcher = new Dispatcher() // Component 3 // Some methods, such as constructor, omitted for brevity class StatefulParent extends Component { state = { text: 'foo' } componentDidMount() { dispatcher.register( dispatch => { if ( dispatch.type === 'change' ) { this.setState({ text: 'bar' }) } } } render() { return <h1>{ this.state.text }</h1> } } // Click handler const onClick = event => { dispatcher.dispatch({ type: 'change' }) } // Component 5 in your example const StatelessChild = props => { return <button onClick={ onClick }>Click me</button> } The dispatcher bundles with Flux is very simple. It simply registers callbacks and invokes them when any dispatch occurs, passing through the contents on the dispatch (in the above terse example there is no payload with the dispatch, simply a message id). You could adapt this to traditional pub/sub (e.g., using the EventEmitter from events, or some other version) very easily if that makes more sense to you.
I found the following working solution to pass the onClick function argument from the child to the parent component with a parameter: Parent class: class Parent extends React.Component { constructor(props) { super(props) // Bind the this context to the handler function this.handler = this.handler.bind(this); // Set some state this.state = { messageShown: false }; } // This method will be sent to the child component handler(param1) { console.log(param1); this.setState({ messageShown: true }); } // Render the child component and set the action property with the handler as value render() { return <Child action={this.handler} /> }} Child class: class Child extends React.Component { render() { return ( <div> {/* The button will execute the handler function set by the parent component */} <Button onClick={this.props.action.bind(this,param1)} /> </div> ) } }
Whenever you require to communicate between a child to the parent at any level down, then it's better to make use of context. In the parent component define the context that can be invoked by the child, such as: In the parent component, in your case component 3, static childContextTypes = { parentMethod: React.PropTypes.func.isRequired }; getChildContext() { return { parentMethod: (parameter_from_child) => this.parentMethod(parameter_from_child) }; } parentMethod(parameter_from_child){ // Update the state with parameter_from_child } Now in the child component (component 5 in your case), just tell this component that it wants to use the context of its parent. static contextTypes = { parentMethod: React.PropTypes.func.isRequired }; render() { return( <TouchableHighlight onPress = {() => this.context.parentMethod(new_state_value)} underlayColor='gray' > <Text> update state in parent component </Text> </TouchableHighlight> )} You can find the Demo project in this GitHub repository.
It seems that we can only pass data from parent to child as React promotes unidirectional data flow, but to make the parent update itself when something happens in its "child component", we generally use what is called a "callback function". We pass the function defined in the parent to the child as "props" and call that function from the child triggering it in the parent component. class Parent extends React.Component { handler = (Value_Passed_From_SubChild) => { console.log("Parent got triggered when a grandchild button was clicked"); console.log("Parent->Child->SubChild"); console.log(Value_Passed_From_SubChild); } render() { return <Child handler = {this.handler} /> } } class Child extends React.Component { render() { return <SubChild handler = {this.props.handler}/ > } } class SubChild extends React.Component { constructor(props){ super(props); this.state = { somethingImp : [1,2,3,4] } } render() { return <button onClick = {this.props.handler(this.state.somethingImp)}>Clickme<button/> } } React.render(<Parent />,document.getElementById('app')); HTML ---- <div id="app"></div> In this example we can make data pass from sub child → child → parent by passing function to its direct child.
Most of the answers given previously are for React.Component-based designs. If you are using useState in the recent upgrades of the React library, then follow this answer.
I've used a top rated answer from this page many times, but while learning React, I've found a better way to do that, without binding and without an inline function inside props. Just look here: class Parent extends React.Component { constructor() { super(); this.state = { someVar: value } } handleChange = (someValue) => { this.setState({someVar: someValue}) } render() { return <Child handler={this.handleChange} /> } } export const Child = ({handler}) => { return <Button onClick={handler} /> } The key is in an arrow function: handleChange = (someValue) => { this.setState({someVar: someValue}) } You can read more here.
Simply pass the parent's setState function via props to the child component. function ParentComp() { const [searchValue, setSearchValue] = useState(""); return <SearchBox setSearchValue={setSearchValue} searchValue={searchValue} />; } then in child component: function SearchBox({ searchValue, setSearchValue }) { return ( <input id="search-post" type="text" value={searchValue} onChange={(e) => setSearchValue(e.target.value)} placeholder="Search Blogs ..." /> ) } A second example to handle click from child component: // We've below function and component in parent component const clickHandler = (val) => { alert(`httpRequest sent. \nValue Received: ${val}`); }; // JSX <HttpRequest clickHandler={clickHandler} /> this is how you get function from parent component then pass a value and fire clickHandler through it. function HttpRequest({ clickHandler }) { const [content, setContent] = useState("initialState"); return ( <button onClick={() => clickHandler(content)}> Send Request </button> ); } export default HttpRequest;
We can create ParentComponent and with a handleInputChange method to update the ParentComponent state. Import the ChildComponent and we pass two props from the parent to the child component i.e., the handleInputChange function and count. import React, { Component } from 'react'; import ChildComponent from './ChildComponent'; class ParentComponent extends Component { constructor(props) { super(props); this.handleInputChange = this.handleInputChange.bind(this); this.state = { count: '', }; } handleInputChange(e) { const { value, name } = e.target; this.setState({ [name]: value }); } render() { const { count } = this.state; return ( <ChildComponent count={count} handleInputChange={this.handleInputChange} /> ); } } Now we create the ChildComponent file and save it as ChildComponent.jsx. This component is stateless because the child component doesn't have a state. We use the prop-types library for props type checking. import React from 'react'; import { func, number } from 'prop-types'; const ChildComponent = ({ handleInputChange, count }) => ( <input onChange={handleInputChange} value={count} name="count" /> ); ChildComponent.propTypes = { count: number, handleInputChange: func.isRequired, }; ChildComponent.defaultProps = { count: 0, }; export default ChildComponent;
If you want to update the parent component, class ParentComponent extends React.Component { constructor(props){ super(props); this.state = { page: 0 } } handler(val){ console.log(val) // 1 } render(){ return ( <ChildComponent onChange={this.handler} /> ) } } class ChildComponent extends React.Component { constructor(props) { super(props); this.state = { page: 1 }; } someMethod = (page) => { this.setState({ page: page }); this.props.onChange(page) } render() { return ( <Button onClick={() => this.someMethod()} > Click </Button> ) } } Here onChange is an attribute with "handler" method bound to its instance. We passed the method handler to the Child class component, to receive via the onChange property in its props argument. The attribute onChange will be set in a props object like this: props = { onChange: this.handler } and passed to the child component. So the child component can access the value of name in the props object like this props.onChange. It's done through the use of render props. Now the child component has a button “Click” with an onclick event set to call the handler method passed to it via onChange in its props argument object. So now this.props.onChange in the child holds the output method in the parent class. Reference and credits: Bits and Pieces
If this same scenario is not spread everywhere you can use React's context, especially if you don't want to introduce all the overhead that state management libraries introduce. Plus, it's easier to learn. But be careful; you could overuse it and start writing bad code. Basically you define a Container component (that will hold and keep that piece of state for you) making all the components interested in writing/reading that piece of data to/from its children (not necessarily direct children). Context - React You could also use a plain React properly instead. <Component5 onSomethingHappenedIn5={this.props.doSomethingAbout5} /> Pass doSomethingAbout5 up to Component 1: <Component1> <Component2 onSomethingHappenedIn5={somethingAbout5 => this.setState({somethingAbout5})}/> <Component5 propThatDependsOn5={this.state.somethingAbout5}/> <Component1/> If this is a common problem, you should starting thinking moving the whole state of the application to somewhere else. You have a few options, the most common are: Redux Flux Basically, instead of managing the application state in your component you send commands when something happens to get the state updated. Components pull the state from this container as well so all the data is centralized. This doesn't mean you can't use local state any more, but that's a more advanced topic.
We can set the parent state from a child component by passing a function into the child component as props as below: class Parent extends React.Component{ state = { term : ''} onInputChange = (event) => { this.setState({term: event.target.value}); } onFormSubmit = (event) => { event.preventDefault(); this.props.onFormSubmit(this.state.term); } render(){ return ( <Child onInputChange={this.onInputChange} onFormSubmit= {this.onFormSubmit} /> ) } } class Child extends React.Component{ render(){ return ( <div className="search-bar ui segment"> <form className="ui form" onSubmit={this.props.onFormSubmit}> <div class="field"> <label>Search Video</label> <input type="text" value={this.state.term} onChange= {this.props.onInputChange} /> </div> </form> </div> ) } } This way, the child will update the parent state onInputChange and onFormSubmit are props passed from parents. This can be called from event listeners in the child, hence the state will get updated there.
Parent Component function Parent() { const [value, setValue] = React.useState(""); function handleChange(newValue) { setValue(newValue); } // We pass a callback to Child return <Child value={value} onChange={handleChange} />; } Child Component function Child(props) { function handleChange(event) { // Here, we invoke the callback with the new value props.onChange(event.target.value); } return <input value={props.value} onChange={handleChange} /> }
Here is a short snippet to get two ways binding data. The counter show the value from the parent and is updated from the child class Parent extends React.Component { constructor(props) { super(props) this.handler = this.handler.bind(this) this.state = { count: 0 } } handler() { this.setState({ count: this.state.count + 1 }) } render() { return <Child handler={this.handler} count={this.state.count} /> } } class Child extends React.Component { render() { return <button onClick={this.props.handler}>Count {this.props.count}</button> } } ReactDOM.render(<Parent />, document.getElementById("root")); <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="root"></div>
This is the way I do it: type ParentProps = {} type ParentState = { someValue: number } class Parent extends React.Component<ParentProps, ParentState> { constructor(props: ParentProps) { super(props) this.state = { someValue: 0 } this.handleChange = this.handleChange.bind(this) } handleChange(value: number) { this.setState({...this.state, someValue: value}) } render() { return <div> <Child changeFunction={this.handleChange} defaultValue={this.state.someValue} /> <p>Value: {this.state.someValue}</p> </div> } } type ChildProps = { defaultValue: number, changeFunction: (value: number) => void} type ChildState = { anotherValue: number } class Child extends React.Component<ChildProps, ChildState> { constructor(props: ChildProps) { super(props) this.state = { anotherValue: this.props.defaultValue } this.handleChange = this.handleChange.bind(this) } handleChange(value: number) { this.setState({...this.state, anotherValue: value}) this.props.changeFunction(value) } render() { return <div> <input onChange={event => this.handleChange(Number(event.target.value))} type='number' value={this.state.anotherValue}/> </div> } }
As per your question, I understand that you need to display some conditional data in Component 3 which is based on the state of Component 5. Approach: The state of Component 3 will hold a variable to check whether Component 5's state has that data An arrow function which will change Component 3's state variable. Passing an arrow function to Component 5 with props. Component 5 has an arrow function which will change Component 3's state variable An arrow function of Component 5 called on loading itself <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> Class Component3 extends React.Component { state = { someData = true } checkForData = (result) => { this.setState({someData : result}) } render() { if(this.state.someData) { return( <Component5 hasData = {this.checkForData} /> //Other Data ); } else { return( //Other Data ); } } } export default Component3; class Component5 extends React.Component { state = { dataValue = "XYZ" } checkForData = () => { if(this.state.dataValue === "XYZ") { this.props.hasData(true); } else { this.props.hasData(false); } } render() { return( <div onLoad = {this.checkForData}> //Conditional Data </div> ); } } export default Component5;
To set state of parent in the child you can use callback. const Child = ({handleClick}) => ( <button on click={() => handleClick('some vale')}>change value</button> ) const parent = () => { const [value, setValue] = useState(null) return <Child handleClick={setValue} /> } In your structure it seems Components 1 an 3 are brothers. So you has 3 options: 1- Put the state into the parent of them(not recommended for 4 layer parent-child). 2- Use useContext and useRducer(or useState) together. 3- Use state managers like redux, mobx ...
This seem to work for me Parent: ... const [open, setOpen] = React.useState(false); const handleDrawerClose = () => { setOpen(false); }; ... return ( <PrimaryNavigationAccordion handleDrawerClose={handleDrawerClose} /> ); Child: ... export default function PrimaryNavigationAccordion({ props, handleDrawerClose, }) ... <Link to={menuItem.url} component={RouterLink} color="inherit" underline="hover" onClick={() => handleDrawerClose()} > {menuItem.label} </Link>
You can do it by passing a reference for the parent to child, as: Having a parent component A in A.js with a method updateAState Having a child component B in B.js Having a wrapper function that renders <A><B></B></A> in C.js In C.js you can use useRef as following: import React, { useRef } from "react"; export default function C() { const parentARef = useRef(); const handleChildBClick = () => parentARef.current.updateAState(); return ( <A ref={parentARef}> <B onClick={handleChildBClick}> </B> </A> ); } Guidance Reference: https://stackoverflow.com/a/56496607/1770571
Data cannot be passed from child to parent in React. Data must be passed from parent to child. In this case, you can use either the built-in Context API or a third-party state management solution such as Redux, Mobx, or Apollo GraphQL. However, if your app structure is too small, you can store your data in your parent element and then send it to your child via prop drilling. But if your project is larger, it will be messy.
<Footer action={()=>this.setState({showChart: true})} /> <footer className="row"> <button type="button" onClick={this.props.action}>Edit</button> {console.log(this.props)} </footer> Try this example to write inline setState, it avoids creating another function.
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