React State Management Basics
Tags: programming, javascript, react, roadtodotnetfullstac
Published: 2023-10-29 06:50:26

---
title: React State Management Basics
published: true
date: 2023-10-29 06:49:10 UTC
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lvyt7ztp73z54pfqieql.jpg
tags: Programming,javascript,react,roadtodotnetfullstac
canonical_url: https://www.codejourney.net/react-state-management-basics/
---

If I were to point out a single concept that is critical to understand in React development, it would be state management.

So what is React state management? How is it different from managing data in C# or Java? Let’s find out! 🙂

## State

If you are a backend or desktop developer, you may have never come across the term of state. At least, I never used it before learning frontend web development. Surely not in the context it is used in frameworks like React. The term more familiar to me at that time was _data binding_ known from [WPF](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/overview) or [WinForms](https://learn.microsoft.com/en-us/dotnet/desktop/winforms/overview).

This is what [WPF documentation](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data) says:

> <cite>Data binding is the process that establishes a connection between the app UI and the data it displays. If the binding has the correct settings and the data provides the proper notifications, when the data changes its value, the elements that are bound to the data reflect changes automatically</cite>

In case of WPF, the crucial part to me is  **If the binding has the correct settings and the data provides the proper notifications**  **(…)**. If you have ever worked with WPF, you know how frustrating can these _notifications_ be (any `INotifyPropertyChanged` fans here?).

WPF’s state management it not that bad. However, React has something better 😉

React introduces us to a concept of _state_. In React components, you do not set anything directly, like calling some code to update an input’s value (this is possible, we’re still in [JavaScript world](https://www.codejourney.net/10-most-shocking-javascript-features-for-csharp-developers/), but it’s not a _React way_ of doing stuff). You don’t directly bind your UI controls with data like you would in WPF or WinForms. Instead, you keep the data in memory, which is a current _state_ of the UI (the whole app or a piece of it).

**When the state changes, your UI is updated by React**. In other words – **in React, UI is a function of state** :

 
```
UI = fn(state)
```
 
 
 

It means that your React app (or its individual pieces called _components_) take some state and output the actual, rendered UI.

This is React state management in a nutshell. Let’s explore it a bit more 😉

_Small disclaimer: _I will focus on state in React functional componets. I consider [class components somewhat legacy](https://react.dev/reference/react/Component). I think this meme summarizes it best 😀:

[![setState meme](https://i0.wp.com/www.codejourney.net/wp-content/uploads/2023/10/react-setstate-meme.jpg?resize=500%2C381&ssl=1)](https://www.freecodecamp.org/news/functional-setstate-is-the-future-of-react-374f30401b6b/)

### The Naive Way

Let’s try to approach React state management as C# developer. In React, the basic piece of UI we create is a component. Let’s add one:

 
```tsx
import Button from "react-bootstrap/Button";

export const NaiveComponent = () => {
  return (
    <>
      <div>I am a naive component</div>
      <Button variant="success">Click me!</Button>
    </>
  );
};
```
 

For now, we render some text and a button which does nothing. What we want is to have some data displayed, like a number, which is incremented on every button click. Let’s try to implement that naively:

 
```tsx
export const NaiveComponent = () => {
  let myCount = 0;

  return (
    <>
      <div>I am a naive component</div>
      <div>Current count: {myCount}</div>
      <Button
        variant="success"
        onClick={() => {
          myCount++;
        }}
      >
        Click me!
      </Button>
    </>
  );
};
```
 

Now, let’s check if it works:

![Naive component - not working](https://i0.wp.com/www.codejourney.net/wp-content/uploads/2023/10/chrome_wkZlXfCKTf.gif?resize=536%2C285&ssl=1)

It looks nothing happens 🥹 To investigate that, we can add a `console.log` (the most common debugging technique in JavaScript world 😅) to the `onClick` event:

 
```tsx
onClick={() => {
          myCount++;
          console.log(myCount);
        }}
```

 

How does that look in the console now?

![React state management - naive way (like C# dev), not working](https://i0.wp.com/www.codejourney.net/wp-content/uploads/2023/10/chrome_g62ao8n7FW.gif?resize=1071%2C591&ssl=1)

Woooow, something is really going wrong here! The data is updated in memory, but not reflected in the UI 🤔

Why does that happen? Because `myCount` is not a state. It’s just some variable we created hoping that it will work fine as part of the UI. Remember what we have discussed in the previous section – we need to start _thinking in React_. This is _The_ _React way_, not _Java way_ or _C# way_ anymore.

### The React Way

So, how to use React state management properly in our example? We need to make `myCount` be a part of component’s state. The simplest way to do that looks as follows:

 
```tsx
export const TheReactWayComponent = () => {
  const [myCount, setMyCount] = useState(0);

  return (
    <>
      <div>I am a React Way component</div>
      <div>Current count: {myCount}</div>
      <Button
        variant="success"
        onClick={() => {
          setMyCount(myCount + 1);
        }}
      >
        Click me!
      </Button>
    </>
  );
};
```
 

First, instead of declaring a “normal” variable for storing our UI-related data, we used the `useState` [hook](https://react.dev/reference/react#state-hooks). As you can see, it returns an array with two things: our state variable (`myCount) `and a function to update its value` `(`setMyCount`). We also set the initial value to `0`. A good practice is to always use a construct like `[myCount, setMyCount]` to destructure what `useState` returns into named, separate objects.

Now we are managing our component’s state _The React Way_. Does it work? Let’s see:

![React state management - The React Way](https://i0.wp.com/www.codejourney.net/wp-content/uploads/2023/10/chrome_OJMhjuDMmd.gif?resize=464%2C215&ssl=1)

Great! We have just learned how the basic form of state management works in React. That was easy, wasn’t it? 😉

Now, let us discuss one more aspect related to the state itself.

### Reference vs value

We know the React Way for state management now. We feel confident. However, we are also used to how data is managed in C# or Java, especially in terms of [handling reference and value types](https://dev.to/dsibinski/net-internals-02-stack-and-heap-net-data-structures-4i7p-temp-slug-7161613). This knowledge will come very handy now 🙂

Let’s try to create a bit more complex component:

 
```tsx
type Person = {
  id: number;
  name: string;
  age: number;
};

export const ObjectHolderComponent = () => {
  const johnInitialValue: Person = {
    id: 1,
    name: "John Doe",
    age: 30,
  };

  const [john, setJohn] = useState<Person>(johnInitialValue);

  return (
    <>
      <div>ID: {john.id}</div>
      <div>Name: {john.name}</div>
      <div>Age: {john.age}</div>
      <Button
        variant="success"
        onClick={() => {
          const currentJohnObject = john;
          currentJohnObject.age = currentJohnObject.age + 1;
          console.log(currentJohnObject);
          setJohn(currentJohnObject);
        }}
      >
        Make John older
      </Button>
    </>
  );
};
```
 

Now, instead of storing a single value in our component’s state, we store a whole object of type `Person`. The object has a few properties: `id`, `name` and `age`.

There is also a button. We want this button to take the current `john` object, increment the value of `age` on it and update the state. We do it The React Way using `setJohn` function returned by `useState`. Just to be sure, we also have the `console.log(john)` just before updating the state. Does that work as we expected?

![Changing an object's (reference) internal variable does not trigger a re-render, even we use useState](https://i0.wp.com/www.codejourney.net/wp-content/uploads/2023/10/chrome_hlgFthtHBT.gif?resize=873%2C656&ssl=1)

Eh, what’s happening here this time? We can clearly see that `age` is updated on the object on each click, but why isn’t the UI re-rendered? 🤔

As I mentioned before, it’s critical to understand the difference between reference and value types here. If you don’t know it, [go and make up for it now](https://dev.to/dsibinski/net-internals-02-stack-and-heap-net-data-structures-4i7p-temp-slug-7161613).

The reason it does not work is that we haven’t updated the actual content of `john` state variable by calling `setJohn(currentJohnObject)`. **What is really being hold in `john` variable is the reference (pointer, address in memory) to the actual object**. And this is what React tracks. We didn’t really update it, because we assigned the same instance of the object to the state. It doesn’t matter that we updated its internal property `age` – the reference itself wasn’t updated.

How do we solve that? Well, in current solution we’d need to create a copy of `john` object before using `setJohn` to alter the state. In effect, we’ll get a new instance of the object in memory and `john` variable will hold a new reference (address) to it.

So, if we only change our `onClick` implementation to this one:

 
```tsx
onClick={() => {
          const johnCopy = { ...john };
          johnCopy.age = john.age + 1;
          setJohn(johnCopy);
        }}
```
 

Everything starts to work as expected:

![Changing a reference state by creating a copy of it works fine in React](https://i0.wp.com/www.codejourney.net/wp-content/uploads/2023/10/chrome_yfmHz6z0Xn.gif?resize=333%2C236&ssl=1)

I think you now understand how React state management works. I also hope that you see the issues managing state in such a way can bring. Even in this trivial example, we create a new instance of an object on every button click. Apart from memory usage considerations, this just looks ugly. Imagine manually creating copies of much more complex objects, like classes storing arrays or dictionaries. This gets pretty wild 🤪

We will see how this issue can be addressed by the end of this article. But first, let’s discuss `props` – another concept critical to really understand React state management.

## Props

Apart from `state`, we also use `props` to control the state of our React components. The name is a short for _properties_, which quite well describes its purpose. `Props` are the data input provided to a component from outside (from a parent component). They are immutable and cannot be changed inside the component which receives them. Let’s see some examples.

### Sharing state between components

The most common usage for `props` is sharing state between components. Imagine that you have an input, where the user manually enters some data. You want to use the current value of this input in two other components. This is where you need to share your component’s state (the value of the input) with these two other components. We often call it _lifting the state up_ – you keep your state higher in the components tree, so the child components can use it.

Let’s try to see an example of connecting `state` with `props`:

 
```tsx
export const PersonData = () => {
  const minimumAge = 5;
  const maximumAge = 100;
  const [age, setAge] = useState<number>(minimumAge);

  const handleAgeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const enteredAge = parseInt(event.target.value);
    if (enteredAge >= minimumAge && enteredAge <= maximumAge) {
      setAge(enteredAge);
    }
  };

  return (
    <>
      <Form>
        <Form.Group controlId="age">
          <Form.Label>Your age</Form.Label>
          <Form.Control
            type="number"
            value={age}
            onChange={handleAgeChange}
            min={minimumAge}
            max={maximumAge}
          />
        </Form.Group>
      </Form>
      <DaysLiving currentAge={age} />
      <YearsUntilCentenarian currentAge={age} />
    </>
  );
};
```
 

As you can see, at lines 4 and 9 we manage the state variable `age`. This is the old stuff we have already seen.

However, at lines 27 and 28 we are passing the `age` state variable into `DaysLiving` and `YearsUntilCentenarian` children components as `currentAge` prop. This is how the `DaysLiving` component is implemented:

 
```tsx
type DaysLivingProps = {
  currentAge: number;
};

export const DaysLiving = (props: DaysLivingProps) => {
  const daysLived = props.currentAge * 365;

  return (
    <div>
      <p>You have lived for {daysLived} days.</p>
    </div>
  );
};
```
 

Notice that `props` is simply a typed object you pass to a function component. To make things easier, you can also use object destructuring and accept the props in the following way:

 
```tsx
export const DaysLiving = ({currentAge}: DaysLivingProps) => {
  const daysLived = currentAge * 365;
  /// ... rest of the code
```
 

That way, you don’t need to write `props.currentAge` every time you want to use this property inside the component.

### Does changing props always trigger a re-render?

Well, generally the answer to this question is _yes_. Every time a prop of a component changes, this component will get re-rendered. However, this is not _entirely_ true 🤪

To make this belief true, we need to assume that this prop is changed _in a React way_. So, to take things literally, consider this component we saw before:

 
```tsx
export const DaysLiving = ({currentAge}: DaysLivingProps) => {
  const daysLived = currentAge * 365;

  return (
    <div>
      <p>You have lived for {daysLived} days.</p>
    </div>
  );
};
```
 

From its perspective only, it accepts a `currentAge` property. For this component to re-render, it’s not enough that the `currentAge` property changes in any way. **Its value must be changed by mutating the state in the parent component**.

So, if the value provided as `currentAge` to `DaysLiving` component changes in the parent component by properly mutating the state (with `setAge` mutation callback, in our case):

 
```tsx
export const PersonData = () => {
  // ...
  const [age, setAge] = useState<number>(minimumAge);

  const handleAgeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
      // ...
      setAge(enteredAge);
    }
  };

  return (
    <>
      // ...
      <DaysLiving currentAge={age} />
      // ...
    </>
  );
};
```

the child component (`DaysLiving`) will get re-rendered.

This is because when a parent component changes, React by default re-renders the parent component itself and all of its children.

If you modified the `age` variable in the parent compont directly, without using `setAge` (which we already know it’s incorrect), the `currentAge` prop would technically change, but the `DaysLiving` component will not get re-rendered.

### state vs props

This is a question that people oftern ask: what’s the difference between `props` and `state` in React? 🤔

I would start with addressing this differently: what do `props` and `state` have in common?

As you already know, both are used for React state management. Both of them influence when the components will be re-rendered by React. Props and state are the absolute foundations of React.

The main difference is that `state` is used internally in a component, while `props` are used to pass information from parent to children components. `State` is also naturally mutable within a component where it’s defined, but `props` are immutable and cannot be changed in a component that receives and uses them.

## React state management libraries

That’s basically everything you need to know to be able to comfortably work with React state management. However, I mentioned before that managing state using only built-in React mechanisms actually sucks 🤪 That’s why I’d like to make a short point on state management libraries.

Maybe you heard about [Redux](https://redux.js.org/), [Zustand](https://github.com/pmndrs/zustand) or [MobX](https://mobx.js.org/README.html). All of them are state management libraires for React. But why would you even need one? And should you learn one now?

In my opinion, you can easily start working with React using the built-in state management mechanisms we discussed today. It’s good to know how it works in practice. However, the truth is that as your application grows and you want to keep it scalable and maintenable, you will need a state management library at some point. As soon as your components tree gets bigger, sharing data and state will not be as easy as “passing props to a children component”. You will need to share state between multiple components in the tree, sometimes not only between parent and children, but also with another components defined on the same level or even in a totally different place of the tree.

This is where React state management libraries come very handy. They solve many of the React’s issues that come to the surface as your state gets complex. But even if you use one, you will still always need `state` and `props`. That’s why it’s critical to understand these two basic concepts first and play with them for some time.

If you’re starting out, it’s good to know that state management libraries exist and that one day you will have to get your hands on one (or more 😉) of them. I hope to share my thoughts on state management libraries in a separate article one day.

## Summary

That’s it! Now you should know how React manages state of its components. Of course, we didn’t cover everything related to state management in React, but I hope you got the basics 😉

You can find all of the source code used in this article [here](https://github.com/dsibinski/codejourney/tree/main/react-state-management).

**How do you manage state in your React apps? Do you use a state management library? If yes, which one and why? Let me know in the comments below!**

The post [React State Management Basics](https://www.codejourney.net/react-state-management-basics/) appeared first on [CodeJourney.net](https://www.codejourney.net).