Tutorial: communication between sibling components using state, and controlled forms
Tags: javascript, webdev, beginners, react
Published: 2022-07-18 23:06:29

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iasro4o6a8d0ote8r91y.png)
 
Component hierarchy: 
```
Parent Component: App.js
... Child component: ExampleForm.js
... Child component: SubmittedFormInfo.js
```

### Goal
I have an example form in the child component `ExampleForm` to receive input on a name, image url, and a price for a new item. I want this information to immediately be displayed in another child component `SubmittedFormInfo`, without the need to refresh the page.   

Both of these components are children of `App`, but information cannot be directly passed between sibling components such as these. How can we take the information from the form, and then immediately display that information in the other component? 

### Walkthrough
This issue depends on the use of state and setter functions passed down as props to each of the components. Since information cannot be directly passed between sibling components, we need to make use of state in the parent component, which will pass down information to each of the two components so that the data can be displayed. 

**App component**
Begin by utilizing the useState hook in the `App` component, and use an initial value of an empty object, which will eventually contain the information from our example form: 
```javascript
function App() {

const [newItem, setNewItem] = useState({})
```

We are not too concerned with the actual value of `newItem` just yet. Instead, begin by passing down the setter function `setNewItem` to the `ExampleForm` component. The first goal here is that we want to change the value of `newItem` upon form submission using the setter function passed down: 
```javascript
<ExampleForm setNewItem={setNewItem}/>
```
```javascript
function ExampleForm({ setNewItem }) {
```

**ExampleForm component**
Before going further, we need to use a controlled form to keep track of the data submitted by the user. For simplicity, declare three initial values as empty strings for each input field on the form using the useState hook:
```javascript
function ExampleForm({ setNewItem }) {

  const [name, setName] = useState('')
  const [image, setImage] = useState('')
  const [price, setPrice] = useState('')
```

These will be used as values for each input in the example form, as follows:
```javascript
<form>
    <input type="text" name="name" placeholder="Name" value={name} />
    <input type="text" name="image" placeholder="Image URL" value={image} />
    <input type="number" name="price" placeholder="Price" value={price} />
    <button type="submit">Add Item</button>
</form>
```

For controlled forms, every change that the user makes to the input field should update state to keep track on the information entered by the user. This is especially useful for immediately making use of the information, such as when you want matching items to display in the DOM using a search bar as the user types. Even though we only need this information upon submission, it is still helpful to use a controlled form. To make this form controlled, begin by declaring three separate functions to handle a change to each of the input fields. Within each function, we want to make use of the `setName`, `setImage`, and `setPrice` setter functions from the state in this component. Each function should update state using the event object, to access data on each letter entered to the form: 
```javascript
function handleNameChange(event) {
    setName(event.target.value)
  }

function handleImageChange(event) {
    setImage(event.target.value)
  }

function handlePriceChange(event) {
    setPrice(event.target.value)
  }
```

To call these functions when the user inputs data, use these functions as callbacks for onChange events in each of the form input fields: 
```javascript
<form>
    <input type="text" name="name" placeholder="Name" value={name} onChange={handleNameChange}/>
    <input type="text" name="image" placeholder="Image URL" value={image} onChange={handleImageChange}/>
    <input type="number" name="price" placeholder="Price" value={price} onChange={handlePriceChange}/>
    <button type="submit">Add Item</button></form>
```

The general idea is that as the user types, each of these functions will be called to update state. Since we are using state variables as the input values in the form, the form values will update as the state updates with the use of the handle functions. Once the user finishes typing, we will have complete information available to use in each of the `name`, `image`, and `price` state variables.

When the user submits the form, we want to change the value of newItem declared in `App`, using the information entered by the user. We can do this by calling the setter function `setNewItem`, which was passed down as a prop to the form component. Begin by declaring a handleSubmit function, which should be called when the user submits the form using onSubmit in the opening form tag. In the handleSubmit function, we want to create a new object, specifying key/value pairs using state variables as each value, as so:
```javascript
const formData = {
      name: name,
      image: image,
      price: parseInt(price)
}
```

Then call `setNewItem`, using the formData object as the specified value: 
```javascript
setNewItem(formData)
```

Optionally, we can prevent a refresh of the page and set the form values back to empty strings to receive new data from the user. The final handleSubmit function should look something similar to this: 
```javascript
function handleSubmit(event) {
    event.preventDefault();

    const formData = {
      name: name,
      image: image,
      price: parseInt(price)
    }

    setNewItem(formData)

    setName('')
    setImage('')
    setPrice('')
 }
```

The primary line of code to focus on here is `setNewItem(formData)`. This updates state in the parent `App` component, which allows us to then pass that form data to `SubmittedFormInfo` as a child of `App`.

**SubmittedFormInfo component**
To finally display the form data in our application, in the `App` component, pass down `newItem` as a prop to `SubmittedFormInfo`: 
```javascript
<SubmittedFormInfo newItem={newItem}/>
```

`newItem` now contains an object with the name, image url, and price of the item added by the user.  Have `SubmittedFormInfo` receive the prop, and optionally destructure newItem to more easily use the information contained in the newItem object.
```javascript
function SubmittedFormInfo({ newItem }) {

  const {name, image, price} = newItem
```

All that is left to do is display the name, image, and price variables in the DOM: 
```javascript
return (
    <header>
      <h2>
        Submitted Form Data
      </h2>
      <p>Name: {name}</p>
      <p>Image url: {image}</p>
      <p> Price: ${price}</p>
    </header>
  );
}
```

With this addition, once the user submits the form, the information entered by the user should now display automatically in the DOM. This occurs because of state updates. Since `SubmittedFormInfo` depends on the variable `newItem` in state, once the value of `newItem` updates, this will cause the `SubmittedFormInfo` component to re-render, immediately displaying the information entered by the user. 

### Conclusion
We used `newItem` and its setter function to update the application. We began by passing down 'setNewItem' to `ExampleForm`, which was called when the user submitted the form. As the user typed, state in the form component updated, keeping track of the values entered by the user. Upon submission, we set the value of `newItem` to the data entered by the user. This caused a state update for `newItem`, which was passed down to our display container as a prop. This component then re-rendered upon submission, displaying the information entered by the user immediately below the form, with no need to refresh the page.