Child-to-Parent Component Communication in React

Child-to-Parent Component Communication in React:

In this article, I am going to discuss Child-to-Parent Component Communication in React (Bottom-up) with Examples. Please read our previous article where we discussed Two-Way Binding in React.

Child-to-Parent Component Communication in React (Bottom-up):

Now, with the changes we made over the last lectures, we are able to gather our expense data, and our user input and combine them into expenseData object and then clear the form. The only problem is that having the data is good but technically, we don’t need it in the expense form component, do we? Instead, we need the data in the NewExpense component or to be precise in the app.js component because there we have our expenses array, and ultimately our goal will be to add the new expense data which is entered by the user to this list of existing expenses.

Child-to-Parent Component Communication in React (Bottom-up)

And we probably also want to enrich it by adding an ID. So, we need to pass the data which we’re collecting and generating in ExpenseForm to the App.js component.

Now, up to this point, we only learned how we can pass the data down. For example, in the Expenses folder, pass the title, amount, and date from the Expenses component to the ExpenseItem component.

Child-to-Parent Component Communication in React (Bottom-up)

So, from parent to child, that’s something we learned. We can do that with props, but how can we do it in the other direction? If data is generated in the ExpenseForm then how can we pass it to the App.js component? Well, we already saw how it works but it’s easy to miss it. So, let us show it to you again.

Child-to-Parent Component Communication in React (Bottom-up)

In the ExpenseForm, we are listening to user input i.e. to change it to the title input. Whenever the user types in title input, the titleChangeHandler function will execute.

Child-to-Parent Component Communication in React (Bottom-up)

Here, we get this default event object, that’s something the browser gives us. Now, we can think about this input element (title) here as a component as well.

Child-to-Parent Component Communication in React (Bottom-up)

It’s not one of our components but it is simply a pre-built component, provided to us by react and translate to the input Dom element but it has the component character (‘/>’) in the end.

Here, we also set some props on this input component including the special onchange prop. The onchange prop isn’t that special, it’s just a prop named onchange which wants a function as a value i.e. titleChangeHandler. Internally, the input element adds this event listener. So, react basically sees that we set a value on this onchange prop and adds that listener to the rendered input element. But that is a pattern we can replicate for our own components as well.

How to Communicate from Child to Parent in React?

We can create our own event props if we want to call it like this, and we can expect functions as values, and that will allow us to pass a function from a parent component to a child component, and then call that function inside of the child component. And when we call that function, we can pass the data to that function as a parameter. That’s how we can communicate from child to parent. let us show it to you.

Let’s say we want to pass the Expense data which we gather in the ExpenseForm component to the NewExpense component as a first step. Because if we want to reach the App component, first of all, we have to reach the NewExpense component because it’s the NewExpense component that uses the ExpenseForm.

How to Communicate from Child to Parent in React?

And then, in the second step later, it’s the app component that uses the NewExpense component.

How to Communicate from Child to Parent in React?

But we can’t skip components in between, that’s also something you learned in the previous section of articles. Props can only be passed from parent to child, we can’t skip intermediate components. So, therefore, as a first step, let’s make sure we can pass the expense data to NewExpense. And we can do it by adding a new prop to ExpenseForm. It’s our components, so we can name it whatever we want. But we will name it onSaveExpenseData,

How to Communicate from Child to Parent in React?

The name is totally up to you; we’ve just named it something because we want to make it clear that the value for this prop should be a function, a function that will eventually be triggered when something happens inside of this component. In this case, when the user saves the entered expense data when the form is submitted.

So that’s again a convention we’re following, you don’t have to name it like this, you can name it whatever you want, It’s totally up to you. It doesn’t have to start with ‘on’. We just do start with ‘on’ to follow the convention and to make it clear that the value for this prop should be a function that can be called inside of the ExpenseForm component. Hence, it’s a function we should define here in NewExpense, just as we did it for the input elements with the functions, we bound to the onchange prop, we defined those i.e. titleChangeHandler, amountChangeHanlder and dateChangeHandler in the expense form before we returned the JSX code.

Step 1: Add a function in NewExpense Component:

Now we’re doing the same here one level above that in the NewExpense component for our own custom expense form component. So here I’ll add a function saveExpenseDataHandler and as a parameter here, we will expect the enteredExpenseData. The name of the parameter is up to you, it’s your function but now we make it clear that this function expects to get this parameter. And then inside the function, we will add our expenseData as another object, inside this object, we will copy the enteredExpenseData which we expect to it be the object which we generated into submit handler. We’ll pull out all the key-value pairs and add them to this new object and then we add a new key, the ID key and simply set that to Math.random.toString(). It’s not a perfect unique ID, theoretically, we can generate the same value twice but it’s good enough for this demo.

Add a function in NewExpense Component

And then we can see what we do with that with console.log.(expenseData). We will pass saveExpenseDataHandler as a value to onSaveExpenseData so that the onSaveExpenseData prop in my custom component receives this saveExpenseDataHandler as a value.

Add a function in NewExpense Component

We’re not executing it here as we don’t add parenthesis. We just point at the function so that the function itself is passed to ExpenseForm. Now that was the first step. The second step now is to use this function inside of our custom component. That’s a step that we didn’t have to do for the inputs because these are built-in components but there also, we pass a function to onchange, and internally react will add a listener and call this function which we pass in whenever that event occurs, that change event. Now, since we’re doing this on our own custom component, we also have to call the passed-in function manually.

Step 2: Extract values in ExpenseForm Component:

So inside of ExpenseForm, we can expect the onSaveExpenseData prop because we’ve set this in the 1st step when we used the ExpenseForm component.

Hence, inside of ExpenseForm, we can extract the value passed for the onSaveExpenseData prop i.e., saveExpenseDataHandler function. So, inside of expense form, we can expect to get some props,

Extract values in ExpenseForm Component

Now, inside of the submit handler, instead of logging our expense data, we will access props.onSaveExpenseData and execute it here.

Extract values in ExpenseForm Component

Here, we can execute it because the value which we get on this onSaveExpenseData key will be a function. We are passing in a function.

Extract values in ExpenseForm Component

So, the saveExpenseDataHandler function is defined in the NewExpense component which we will now execute in a different component, inside of ExpenseForm to be precise. And we can execute the function even though it’s not defined inside of ExpenseForm because we are passing a pointer at it through the onSaveExpenseData prop. This is a super important pattern that you will use a lot in React. This is how you can communicate between components and how you can communicate up, how you can make sure that a child component, for example, the ExpenseForm component, can communicate up to the parent component, the new expense component in this case. We can call a function in the NewExpense component and we can pass the data as a parameter.

So, when we call onSaveExpenseData () in the ExpenseForm, we can pass the expenseData as an argument,

Extract values in ExpenseForm Component

And this’s the value that we’ll receive as a parameter in the NewExpense component,

Extract values in ExpenseForm Component

We hope this flow is clear to you. The trick is that we pass around a pointer in a function. Now, if we save everything and reload the pages, if we enter something in the input fields and then click on add expense button,

Extract values in ExpenseForm Component

You will see that the expenseData object is logged to the console from the NewExpense file line 12,

And we can tell that it’s from there because we also have an ID field there which has the random number that has been generated and converted to a string. So, this is how we can communicate and this is a super important pattern.

Now, we can continue in this chain and we can communicate from inside NewExpense to App.js because it’s the App.js component that needs the NewExpense to add it to the existing list of expenses. So therefore again, in App.js, we can add a function that we defined before the return statement.

Step 3: Define a Function in App.js Component:

Below is the function that we defined in the app.js,

Define a Function in App.js Component

As we haven’t quite yet learned how we can render lists of data dynamically, and therefore, for now, we’re not updating the expenses array, that is something we’ll do and learn about in the next section. For the moment, we just want to confirm that the data has arrived.

So, using the same pattern as before, we can pass a pointer at this function to NewExpense, so inside of NewExpense, we can call this function and pass that expense data up to the app component. So, we can name the prop here ‘onAddExpense’. The name is up to you, it’s your component but we will follow the name convention i.e., starting with ‘on’ to make it clear that it’s a function pointer that has passed as an argument. Then we pass a pointer ‘addExpenseHandler’ to the onAddExpense prop on NewExpense,

Define a Function in App.js Component

And therefore, inside of NewExpense, we can call it. We can accept the props argument here as well,

Define a Function in App.js Component

And in saveExpenseDataHandler, which itself is called when something happens in the ExpenseForm, we can call props.onAddExpense(expenseData),

Define a Function in App.js Component

And if we save everything and reload the page, if we enter something in the input fields and then click on add expense button,

Define a Function in App.js Component

Define a Function in App.js Component

By mistake, we have console.log( expense ) instead of expenses.

Define a Function in App.js Component

So, save this small change, and let’s add the data in the input field again and hit Add expense.

Define a Function in App.js Component

So, this is the user entered Expense data that we successfully logged it onto the console. We’ll use that expense and change the array and output dynamically, we will do this in the next section. But for the moment, we want you to focus on that state and event handling thing and being able to pass data up. Below is the complete code of all the files used in this article.

ExpenseForm.js:
import React, { useState } from "react";

import "./ExpenseForm.css";

const ExpenseForm = (props) => {

  const [enteredTitle, setEnteredTitle] = useState("");
  const [enteredAmount, setEnteredAmount] = useState("");
  const [enteredDate, setEnteredDate] = useState("");

  const titleChangeHandler = (event) => {
    setEnteredTitle(event.target.value);
  };

  const amountChangeHandler = (event) => {
    setEnteredAmount(event.target.value);
  };

  const dateChangeHandler = (event) => {
    setEnteredDate(event.target.value);
  };

  const submitHandler = (event) => {
    event.preventDefault();

    const expenseData = {
      title: enteredTitle,
      amount: enteredAmount,
      date: new Date(enteredDate),
    };

    props.onSaveExpenseData(expenseData);

    //console.log(expenseData);
    setEnteredTitle('');
    setEnteredAmount('');
    setEnteredDate('');
  };

  return (
    <form onSubmit={submitHandler}>
      <div className="new-expense__controls">
        <div className="new-expense__control">
          <label>Title</label>
          <input
            type="text"
            value={enteredTitle}
            onChange={titleChangeHandler}
          />
        </div>
        <div className="new-expense__control">
          <label>Amount</label>
          <input
            type="number"
            value={enteredAmount}
            onChange={amountChangeHandler}
            min="0.01"
            step="0.01"
          />
        </div>
        <div className="new-expense__control">
          <label>Date</label>
          <input
            type="date"
            value={enteredDate}
            onChange={dateChangeHandler}
            min="2023-06-25"
            max="2025-12-31"
          />
        </div>
      </div>
      <div className="new-expense__actions">
        <button type="submit">Add Expense</button>
      </div>
    </form>
  );
};

export default ExpenseForm;
NewExpense.js:
import React from 'react';
import ExpenseForm from './ExpenseForm';
import "./NewExpense.css";

const NewExpense = (props) => {

    const saveExpenseDataHandler = (enteredExpenseData) => {
        const expenseData = {
            ...enteredExpenseData,
            id: Math.random().toString()
        }
        //console.log(expenseData);
        props.onAddExpense(expenseData);
    }

    return <div className='new-expense'>
        <ExpenseForm onSaveExpenseData={saveExpenseDataHandler} />
    </div>
};

export default NewExpense;
App.js:
import React from "react";
import Expenses from "./components/Expenses/Expenses";
import NewExpense from "./components/NewExpense/NewExpense";

const App = () => {
  const expenses = [
    {
      id: "e1",
      title: "Household Expense",
      amount: 25000,
      date: new Date(2023, 4, 27),
    },
    {
      id: "e2",
      title: "Travel Expense",
      amount: 1300,
      date: new Date(2023, 5, 28),
    },
    {
      id: "e3",
      title: "Education fees",
      amount: 5000,
      date: new Date(2023, 6, 29),
    },
  ];

  const addExpenseHandler = expense => {
    console.log('In App.js');
    console.log(expense);
  }

  return (
    <div>
      <NewExpense onAddExpense={addExpenseHandler}/>
      <Expenses items={expenses}/>
    </div>
  )
  
};

// return React.createElement(
//   "div",
//   {},
//   React.createElement("h2", {}, "Hello Coders!"),
//   React.createElement(Expenses, { items: expenses })
// );

export default App;

In the next article, I am going to discuss Lifting the State Up in React with Examples. Here, in this article, I try to explain Child-to-Parent Component Communication in React (Bottom-up) with Examples. I hope you enjoy this Child-to-Parent Component Communication in React (Bottom-up) article.

Leave a Reply

Your email address will not be published. Required fields are marked *