Intro to State in React - Example Walkthrough
Tags: webdev, javascript, react, beginners
Published: 2023-11-06 14:57:58

Table of Contents:
{%- # TOC start -%}

- [React data flow](#reacts-data-flow)
- [Definition of state](#definition-of-state)
- [Deciding where to hold state](#deciding-where-to-hold-state)
- [Setting up state](#setting-up-state)
- [Updating state](#updating-state)
- [Working demo](#heres-a-quick-demo-of-our-working-app)


{%- # TOC end -%}
I've spent the last few weeks getting a handle on React and can already see how much easier my life as a developer will be because of it. While there are myriad topics I could delve into regarding React, today I'll focus on one of the most important - state. If you already have a general understanding of React components (read [here](https://react.dev/learn/your-first-component) if not), this post will provide an introduction on how to take advantage of the power of state in your application. I will also be using [destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) throughout my code snippets.


## React's data flow
React's data flow runs one way down the component hierarchy, from Parent to Child component. Props are pieces of data that are passed from Parent to Child component. For example, say you have an `App` that contains two child components- `SongTitles` and `SongArtists`.  `App` might have a `songs` array of objects representing each song, where keys are the song titles and values are the corresponding artists. If we want to render the titles and artists in their respective components, we might want to pass that information as `props` to `SongTitles` and `SongArtists`. This might not be the most elegant design, but here's a snippet of what our components could look like:

```javascript
//App.js
function App() {
  const songs = [
    { "Blank Space": "Taylor Swift" },
    { "Tiffany Blews": "Fall Out Boy" },
    { "Knee Socks": "Arctic Monkeys" },
  ];

  const titles = songs.map((song) => Object.keys(song)[0]);
  const artists = songs.map((song) => Object.values(song)[0]);

  return (
    <div className="App">
      <h1>React State Demo</h1>
      <SongTitles titles={titles} />
      <SongArtists artists={artists} />
    </div>
  );
}
```
```javascript
//SongTitles.js
function SongTitles({ titles }) {
  const songList = titles.map((title) => (
    <p key={title}>
      <button>X</button> {title}
    </p>
  ));
  return (
    <div className="titles">
      <h2>Song Titles</h2>
      {songList}
    </div>
  );
}
```
```javascript
//SongArtists.js
function SongArtists({ artists }) {
  const artistList = artists.map((artist) => <p key={artist}>{artist}</p>);
  return (
    <div className="artists">
      <h2>Song Artists</h2>
      {artistList}
    </div>
  );
}
```

Here's what the App looks like: 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z7tsnblad4315qeos3dh.png)


## Definition of state
This looks good, but what happens if we want to change the `songs` object dynamically and remove a song from the screen when we click a delete button? Here's where state comes in. State data can change throughout a component's lifetime as the user interacts with the app, unlike props. If we want to be able to delete a song (and its corresponding artist) from inside the `SongTitles` component, we can easily do this by setting state. If we make the `songs` array stateful rather than static, it will update dynamically and we won't have to refresh the page each time we want to render a change.

## Deciding where to hold state
Before we create our state variables, we should figure out where to hold `songs`'s state. To figure out where state should live, it's helpful to decide which components need access to that state. In this case, `SongTitles` and `SongArtists` both need access to `songs` to render information. Sibling components can't pass data to each other (remember the data flow) so we should store `songs`'s state in a common parent component (`App` in this case). This means that `songs` can continue living in `App`, but you can imagine that without the `SongArtists` component, we could just store this state directly in `SongTitles`. 

## Setting up state
Now that we've decided where the `songs` state will live, let's import React's `useState` hook in App.js: `import {useState} from "react"`

The [`useState` function](https://react.dev/reference/react/useState) takes in the initial value of our state variable as a parameter and returns two things: 
1) the state's current value 
2) a function that can be used to set (or change) that state

To use state with our `songs` array, we can replace our old array with the following lines inside of our `App` function: ```javascript 
// App.js
function App() {
  const [songs, setSongs] = useState([
    { "Blank Space": "Taylor Swift" },
    { "Tiffany Blews": "Fall Out Boy" },
    { "Knee Socks": "Arctic Monkeys" },
  ]);
//...
}
```
Here, `songs` is initially set to the the same 3 songs from our initial array, but we also now have a function to dynamically change `songs` called `setSongs`. 
> **_NOTE:_**  This is the naming convention for setter functions, but you could name it something other than `setSongs` if you prefer. 

## Updating state
Our next step is to create a function in `App` that will remove a song from `songs`. Given a song title that we want to delete, our function could look like this: 
```javascript
// App.js
function App() {
//...
  function handleDeleteSong(title){
    const updatedSongs = songs.filter(song=>!(title in song));
  }
//...
}
```
Right now this function is creating a new array of songs. It contains all of our original songs *except* for the one with the title we want to delete. Your first thought might be to just set `songs` equal to `updatedSongs`. However, because we want to modify an internal state living in `App`, this is where our setter function comes in handy.

```javascript
// App.js
function App() {
//...
  function handleDeleteSong(title){
    const updatedSongs = songs.filter(song=>!(title in song));
    setSongs(updatedSongs); //Updating songs state using our new songs array
  }
//...
}
```
We are using `setSongs` to *update our internal state* to a new array of songs, with our deleted song removed.

We're not quite done yet! We haven't actually called this function anywhere. We want `handleDeleteSong` to run when we click the delete button next to a song's title. We'll need to access this function from within `SongTitles` and use it as a callback in a click event listener. We can pass a reference to `handleDeleteSong` as a prop down to `SongTitles`. I'm going to call this prop `onDeleteSong`.

```javascript
// App.js
function App() {
//...
  return (
    <div className="App">
      <h1>React State Demo</h1>
      <SongTitles titles={titles} onDeleteSong={handleDeleteSong}/>
      <SongArtists artists={artists} />
    </div>
  );
}
```

Now within `SongTitles`, we can call `onDeleteSong` every time we click a delete button.
```javascript
//SongTitles.js
function SongTitles({ titles, onDeleteSong }) {
  //Added onDeleteSong prop
  const songList = titles.map((title) => (
    <p key={title}>
      {/* Adding click event listener below, passing in title we want to delete */}
      <button onClick={(e) => onDeleteSong(title)}>X</button> {title}
    </p>
  ));
  return (
    <div className="titles">
      <h2>Song Titles</h2>
      {songList}
    </div>
  );
}
```

This inverses the data flow and triggers a state change in `App` from its child component `SongTitles`. This is generally a good strategy to use when you want to update a parent's state from a child. 

One great thing about React is that it automatically re-renders a component when its state changes. This means that when we click a delete button, `App` and its children, `SongTitles` and `SongArtists`, will re-render with the updated state.

> **_NOTE:_**  This works because `updatedSongs` from our `handleDeleteSong` function is an entirely new array. Because we used the `filter` method, `updatedSongs` points to a different place in memory than `songs`. React does not re-render a component if you attempt to set state with a shallowly unchanged object. If we tried to set state by directly modifying `songs` (e.g. by using splice() to remove a song from `songs`), `setSongs(songs)` would be unsuccessful. Even if we successfully removed an element from `songs` this way, state would not update and therefore our screen would not be updated to reflect our changes.


## Here's a quick demo of our working app:
![](https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExanU0b2RjOXJ6c2w4MGIwc3B1NmQ4dDh3bXZybDFjZzM5aGVwb3F5aCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/2UBGYQ9EymbhpeMPxC/giphy.gif)


If you'd like to see the source code for our 3 components, you can check out this [github repository](https://github.com/khamerling-potts/phase-2-blog-demo).


You can read more about state [here](https://react.dev/learn/state-a-components-memory).