Communication between Components in React

Pass data between a parent and a child component

Pass data from parent component to its child

Use props

Parent.js

<Child name={childName}></Child>

Child.js

<p>Receive from parent by props: {this.props.name}</p>

Pass data from child component to its parent

Pass function from parent component to its child.

The Child component call the function with parameters.

A complete example

Click to expand!

Parent.js

import { PureComponent } from 'react';
import { Input } from 'antd';

class Parent extends PureComponent {

constructor(props) {
super(props);
this.state = ({
childName: "default child name",
name: "default parent name"
});

}

handleInputChange = (e) => {
const name = e.target.value;
this.setState({
childName: name
})
}

handleParentValueChange = (value) => {
this.setState({
name: value
})
}

render() {
return (
<div>
<h2>I'm the parent page</h2>
<p>Receive from child: {this.state.name}</p>
Enter data to pass to child: <Input onChange={this.handleInputChange}></Input>
<Child name={this.state.childName} onParentNameChange={this.handleParentValueChange}></Child>
</div>
)
}
}

export default Parent;

Child.js

import { PureComponent } from 'react';
import { Input } from 'antd';

class Child extends PureComponent {

constructor(props) {
super(props);
this.state = ({});
}

handleInputChange = (e) => {
const name = e.target.value;
this.props.onParentNameChange(name);
}

render() {
return (
<div style={{backgroundColor: "lightgray", padding: "10px 10px 10px 10px"}}>
<h2>I'm the child page</h2>
<p>Receive from parent by props: {this.props.name}</p>
Enter data to pass to parent: <Input onChange={this.handleInputChange}></Input>
</div>
)
}
}

export default Child;

Pass functions between a parent and a child component

Pass functions to child components

If you need to have access to the parent component in the handler, you also need to bind the function to the component instance (see below).

There are several ways to make sure functions have access to component attributes like this.props and this.state.

Bind in Constructor (ES2015)

class Foo extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}

Note: Make sure you aren’t calling the function when you pass it to the component

render() {
// Wrong: handleClick is called instead of passed as a reference!
// The function being called every time the component renders.
return <button onClick={this.handleClick()}>Click Me</button>
}

Class Properties (ES2022)

class Foo extends Component {
handleClick = () => {
console.log('Click happened');
};
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}

Note: Make sure you aren’t calling the function when you pass it to the component

render() {
// Wrong: handleClick is called instead of passed as a reference!
// The function being called every time the component renders.
return <button onClick={this.handleClick()}>Click Me</button>
}

Bind in Render

class Foo extends Component {
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={this.handleClick.bind(this)}>Click Me</button>;
}
}

Pass a parameter to an event handler in parent component’s render method. Values passed by child components will be ignored.

<button onClick={this.handleClick.bind(this, id)} />

Note: Using Function.prototype.bind in render creates a new function each time the component renders, which may have performance implications.

Arrow Function in Render

class Foo extends Component {
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={() => this.handleClick()}>Click Me</button>;
}
}

Pass a parameter to an event handler

<button onClick={() => this.handleClick(id)} />

Note: Using an arrow function in render creates a new function each time the component renders, which may break optimizations based on strict identity comparison.

Call child functions in a parent component


refs

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

Hook parent and hook child (Functional Component Solution)

const { forwardRef, useRef, useImperativeHandle } = React;

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

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

Legacy API using Class Components (>= react@16.4)

Class parent and class child (Class Component Solution)

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

Class component and Hook

Class parent and 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>)
})

Hook parent and 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>)
}
}

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

Others

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

Share data between components with Redux

A basic example:

Click to expand!
import { createStore } from 'redux'

/**
* This is a reducer - a function that takes a current state value and an
* action object describing "what happened", and returns a new state value.
* A reducer's function signature is: (state, action) => newState
*
* The Redux state should contain only plain JS objects, arrays, and primitives.
* The root state value is usually an object. It's important that you should
* not mutate the state object, but return a new object if the state changes.
*
* You can use any conditional logic you want in a reducer. In this example,
* we use a switch statement, but it's not required.
*/
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 }
case 'counter/decremented':
return { value: state.value - 1 }
default:
return state
}
}

// Create a Redux store holding the state of your app.
// Its API is { subscribe, dispatch, getState }.
let store = createStore(counterReducer)

// You can use subscribe() to update the UI in response to state changes.
// Normally you'd use a view binding library (e.g. React Redux) rather than subscribe() directly.
// There may be additional use cases where it's helpful to subscribe as well.

store.subscribe(() => console.log(store.getState()))

// The only way to mutate the internal state is to dispatch an action.
// The actions can be serialized, logged or stored and later replayed.
store.dispatch({ type: 'counter/incremented' })
// {value: 1}
store.dispatch({ type: 'counter/incremented' })
// {value: 2}
store.dispatch({ type: 'counter/decremented' })
// {value: 1}

Pass data to redirect component

React Router

Pass data with <Redirect>:

<Redirect to={{
pathname: '/nav',
state: { id: '123' }
}}
/>

Pass data with history.push():

history.push({
pathname: '/about',
search: '?the=search',
state: { some: 'state' }
})

Access data:

this.props.location.state.id

UmiJS

Pass data with query string

import { history, router } from 'umi';

history.push('/path?field=value&field2=value2')
// or
history.push({
pathname: '/path',
query: {
field: value
}
})
// or
router.push('/path?field=value&field2=value2')
// or
router.push({
pathname: '/path',
query: {
field: value
}
})

Access query string

import { useLocation } from 'umi';

const location = useLocation();
console.log(location.query)

URL query string

Pass data to components use URL query string: url?field=value&field2=value2

Get query string parameters

const params = new Proxy(new URLSearchParams(window.location.search), {
get: (searchParams, prop) => searchParams.get(prop),
});
// Get the value of "some_key" in eg "https://example.com/?some_key=some_value"
let value = params.some_key; // "some_value"

References