React - Communicate between Parent and Child Functional Components

Author: Marty Ballard

 marty331b

Category: React

Functional Component

Before we get started with how to communicate between Functional components, let's define what a Funcitonal component is and isn't. 

One other thing to note, I'm using TypeScript.  If you aren't useing TypeScript you should really consider it as it will greatly reduce the number of errors you may inadvertantly inject into your codebase.

A Functional component is simply a function that returns a React fragment.  Here's the simplest example:

import React from 'react';

const Simple: React.FC<React.FunctionComponent> = () => {
  return(
    <>
      <h1>Simple Functional Component</h1>
    </>
  )
};

export default Simple;

This Functional component named Simple, takes no props and returns a React element containing an h1 with an innerText value of "Simple Functional Component".  The <> and </> tags are shorthand for React Fragment, this could have also returned the following:

<React.Fragment>
  <h1>Simple Functional Component</h1>
</React.Fragment>

A React Functional component does not have to explicitly return an React.Fragment, but it must return a single element that contains zero or more sub-elements.

Typically you will have Functional components that contain other React components, when that is the case then the component that contains other components is considered the parent and the components that are included are considered to be child components.

Communiction Between Functional Components

Functional components communicate by setting props to variables or functions.  These functional props are then executed on the receiving functional component.  And the good news is, this can go from a Parent to a Child or from a Child to a Parent.  Let's start off with a very simple example and then we'll move to a more complicated setup.

Parent to Child Communication

In this example we'll simplly send the title value from the parent to the child.

export default function Parent() {
  const [child2Title, setChild2] = useState('Child2')

  return (
    <div className="parent_container">
      <h1>Parent Component</h1>
      <div className="child_container">
        <Child2 
          title={child2Title}
        />
      </div>
    </div>
  )
}
-----------------------
interface Child2Props {
  title: string;
}

const  Child2: React.FC<Child2Props> = (props) => {
  return(
    <div className="child2">
      <h2>{props.title}</h2>
    </div>
  )
}

As you can see, we create a variable named child2Title when the Parent component is initialized.  The Parent component sends the child2Title variable to the Child2 component set as the title prop.  Then we create an interface named Child2Props with a string named title.  The Child2 component is created using the Child2Props interface and the title in the h2 element is set to props.title. 

The parent sent the child a variable that was then used to set a value in the Child2 component.  Both components are functional components.  No constructors were needed, the code is very simple and clean.  Easy.

Bidirectional Communication

Now we'll move into a more complicated scenario.  What if we wanted to have the h1 value updateable in the Parent and Child2 components?  And we want to do it from Child1.  Is that even possible?  Is it possible to update a value from a child to a parent?  Is it possible to update a value from a child and send it through the parent to another child?  It is!

Let's dive into the code and see if we can do this and then I'll explain once we're done.

-------------------
Child2
-------------------
import React from "react";

interface Child2Props {
  title: string;
}

const  Child2: React.FC<Child2Props> = (props) => {
  return(
    <div className="child2">
      <h2>{props.title}</h2>
    </div>
  )
}

export default Child2;

First off let's look at Child2, there are no changes needed.  So above you see Child2 in it's entirety.

Next we'll dive into the Parent, a few changes will be needed.

import React from "react";
import { useState } from "react";
import Child1 from './Child1';
import Child2 from "./Child2";

export default function Parent() {
  const [title, setTitle] = useState('Parent!')
  const [child2Title, setChild2] = useState('Child2')

  function updateTitle(newTitle: string){
    setTitle(newTitle)
  }
  function setChild2Title(child2Title: string) {
    setChild2(child2Title)
  }
  return (
    <div className="parent_container">
      <h1>{title}</h1>
      <div className="child_container">
        <Child2 
          title={child2Title}
        />
        <Child1 
          updateChild2Title={setChild2Title}
          updateParentTitle={updateTitle}
        />
      </div>
    </div>
  )
}

On the Parent we are now using a useState hook for title and setTitle.  We are using the title variable in our h1 for the main title of the Parent.  We have created a two functions - updateTitle to handle calling setTitle with the new value that will be received from Child1 and setChild2Title to update the value received from Child1 to the new child2Title by calling setChild2.

We also added in the Child1 element in our React Fragment, Child1 has two props, updateChild2Title which is set to equal the function setChild2Title and updateParentTitle which is set to equal the function updateTitle.  This is interesting and a bit more complicated but still much simpler than a class based component.

Now let's build out that Child1 component and see if we can actually update values on the Parent and Chid2.


interface child1props {
  updateParentTitle(title: string): any;
  updateChild2Title(title: string): any;
}

We'll start with just the interface.  The interface has two values - updateParentTitle and updateChild2Title, these are the same values that are sent as props from the Parent component.  They are also both functions that take a string named title and return any.

const [titleVal, setTitleVal] = useState('')
const [childTitleVal, setChildTitle] = useState('')

Child1 will also use useState hooks to control the values for the parent title and child2 title, both values are set to emply strings when the component is initialized as these values will be connected to input elements.

<div className="grid">
   <label htmlFor="parent_title">Parent Title Input</label>
   <input type="text" name="titles" id="parent_title" placeholder="Parent Title" onChange={handleInput}/>
   <button onClick={setParent}>Update parent title</button>
</div>

function setParent(){
    if (titleVal.length > 1) {
      props.updateParentTitle(titleVal)
    } 
  }

const handleInput = (event: React.ChangeEvent<HTMLInputElement>) => {
    setTitleVal(event.target.value)
  }

This is a bit out of order but I want to show you the bits and pieces before the entire component is shown to make it easier to understand. 

We setup the input and button to update the Parent title.  The input has an onChange function that is connected to the handleInput function.  The handleInput function takes the value from the input and calls the setTtitleVal function to update the titleVal.  At this point the state has changed inside the Child1 component but has not been sent to the parent.  To send to the parent there is an onClick on the button that calls the setParent function.  The setParent function then calls props.updateParentTitle and sends the titleVal up to the parent.  Remember, the updateParentTitle function was set as a prop on the Child1 component from the Parent component it then calls the updateTitle function on the Parent component which calls the setTitle useState hook.  So at this point we can send a value from the Child1 component to the Parent component.  But we still need to send from Child1 to Parent to Child2, so let's knock that functionality out.

<div className="grid">
     <label htmlFor="child2_title">Child2 Title Input</label>
     <input type="text" name="titles" id="child2_title" placeholder="Child2 Title" onChange={handleChild2Input}/>
    <button onClick={setChild2}>Update Child2 title</button>
</div>

function setChild2(){
    if (childTitleVal.length > 1){
      props.updateChild2Title(childTitleVal)
    }
  }

const handleChild2Input = (event: React.ChangeEvent<HTMLInputElement>) => {
    setChildTitle(event.target.value)
  }

The good news is you already know how to do this.  We are using the same pattern for sending the updated title for Child2.  We create an input and button.  The input has an onChange listener that passes the value of the intput to the setChildTittle function.  Then the button has an onClick function that calls the setChild2 function which calls props.updateChild2Title.  updateChild2Title was set as a prop on Child1 from the Parent component, which then calls the function setChild2Title.  setChild2Title calls the useState hook setChild2 which updates the child2Title value and pushes that state value down to Child2.

Below is Child1 in it's entirety.  Take a few minutes to read through it as it's more complicated looking than the other components but once you understand how the small bits and pieces work then it's very digestible.

import React, { useState } from "react";

interface child1props {
  updateParentTitle(title: string): any;
  updateChild2Title(title: string): any;
}

const Child1: React.FC<child1props> = (props) => {
  const [titleVal, setTitleVal] = useState('')
  const [childTitleVal, setChildTitle] = useState('')

  function setParent(){
    if (titleVal.length > 1) {
      props.updateParentTitle(titleVal)
    } 
  }

  function setChild2(){
    if (childTitleVal.length > 1){
      props.updateChild2Title(childTitleVal)
    }
  }

  const handleInput = (event: React.ChangeEvent<HTMLInputElement>) => {
    setTitleVal(event.target.value)
  }

  const handleChild2Input = (event: React.ChangeEvent<HTMLInputElement>) => {
    setChildTitle(event.target.value)
  }


  return(
    <div>
      <h2>I'm Child1.</h2>
      <div className="child1_container">
        <div className="grid">
          <label htmlFor="parent_title">Parent Title Input</label>
          <input type="text" name="titles" id="parent_title" placeholder="Parent Title" onChange={handleInput}/>
          <button onClick={setParent}>Update parent title</button>
        </div>
        
        <div className="grid">
          <label htmlFor="child2_title">Child2 Title Input</label>
          <input type="text" name="titles" id="child2_title" placeholder="Child2 Title" onChange={handleChild2Input}/>
          <button onClick={setChild2}>Update Child2 title</button>
        </div>
      </div>
    </div>
  )
}

export default Child1;

Summary

Building React functional components that communicate from parent to child, child to parent, or child to child is relatively simple.  The key to communication is done through sending functions as props.  I hope this article was helpful, if so please share!

Here is the repo for the code:

https://github.com/marty331/react_parent_child


There is 1 comment below.

Post your comment


Oct. 8, 2022, 2:54 p.m. - Crytoguado  
One click of the robot can bring you thousands of bucks. http://go.gepekaep.com/0j35
Reply