Moving Arguments from Child to Parent Component in React
Tags: react
Published: 2019-07-28 19:18:01

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.
```javascript
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
```javascript
function Child({ color }) {
  function handleClick(event) {
    // do something meaningful
  }
  return (
    <button name={color} onClick={handleClick}>{color}</button>
  )
}
```
2. 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.

```javascript
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>
  )
}
```
3. 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.
```javascript
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.
```javascript
<button onClick={onChildClick(color)} ... />
```
* Passing argument to event handler will also invoke function instantly for all rendered button elements and will not be functional.
```javascript
<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.
```javascript
<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.
```javascript
<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.