The Great Blog

Moving Arguments from Child to Parent Component in React

July 28, 2019

To understand the code easier, React implemented unidirectional data flow, also called ‘one-way data-binding’ when passing data from parent component to the child. However, often, we need to pass some data back to the parent from the child, for example, think of forms, when the user’s input effects parent component. For the newcomers to React sometimes, it is hard to grasp the patterns of how to send data back to from child. This article explains how to do it the easy way.

Use case

Let’s imagine we have a parent element, that renders three child elements. Each child element has a button, and each time the user press it, the parent should show which color in the button was selected.

function Parent() {
  return (
    <>
      <h1>Selected color: </h1> // show selected color
      {['green','red','blue'].map((color) => (
        <Child color={color} ... />
      ))}
    </>
  )
}

function Child({ color }) {
  return (
    <button value={color}>{color} button</button>
  )
}

Passing argument from top to bottom is easy via props, but to send data back might seem tricky.

Callback to the rescue

Let’s reverse engineer it from bottom to top:

  1. To capture the button click event, we need to add a handler
function Child({ color }) {
  function handleClick(event) {
    // do something meaningful
  }
  return (
    <button name={color} onClick={handleClick}>{color}</button>
  )
}
  1. Inside the handler is the perfect place to call another function, a callback, passed from parent component by props - onChildClick. Note that we didn’t create or passed it yet, but do it later on. A callback can receive any arguments, and parent component will have access to them. In this case, we will pass an argument name from the button.
function Child({ color,  onChildClick }) {
  function handleClick(event) {
    onChildClick(event.target.name); // pass any argument to the callback
  }
  return (
    <button name={color} onClick={handleClick}>{color}</button>
  )
}
  1. The last step will be to read the arguments from the callback and save them to the parent component state for later use.

    • Create callback function handleChildClick and pass it to the Child component through prop onChildClick.
    • Add useState hook, assign state variable color and a function setColor to update it.
    • Read an argument from the handleChildClick function, passed from the child component, and call setColor function to update the state with new value.
function Parent() {
  const [color, setColor] = useState('');
  function handleChildClick(color) {
    setColor(color);
  }
  return (
    <>
      <h1>selected color: {color}</h1>
      {['green','red','blue'].map((color) => (
        <Child color={color} onChildClick={handleChildClick} ... />
      ))}
    </>
  )
}

That’s pretty much it, on every button click we call event handler, inside it we call a callback function from props and inside the callback function (in this case handleChildClick) setting the state to the parent component.

Where you might struggle

  • Calling callback directly from event and passing an argument. It will invoke function instantly for all rendered button elements and will not be functional.
<button onClick={onChildClick(color)} ... />
  • Passing argument to event handler will also invoke function instantly for all rendered button elements and will not be functional.
<button onClick={handleClick(color)} ... />
  • Using inline arrow function and calling callback inside it will create a new arrow function each time button is rendered, also, you will lose event object if you do not explicitly pass it to the callback. Possible, but not efficient.
<button onClick={(event) => onChildClick(color, event)} ... />
  • If using class component and a method as an event handler, do not forget to bind the context. With bind all further arguments, like event will be forwarded.
<button onClick={this.handleClick.bind(this, color)} .../>

Summing-up

Passing arguments from child to parent is not that confusing, it just might be a little bit tricky to figure out the right place to call the callbacks. I hope this article will clear some of the confusion.


Linas Spukas

Hi there! My name is Linas Spukas, I am a full stack web developer and this is my blog. About stuff and things... in development. Enjoy.