Different options for managing state in a React app (2020 edition)

Subscribe to my newsletter and never miss my upcoming articles

Hello folks πŸ‘‹ !

I've noticed this really interested topic about react state in RFA section here on Hashnode and it immediately caught my attention since for the past 3 years, I've wrote around 10k+ react components, it was hundreds of thousands of lines of code and obviously, I used the state really often while doing that.

So hopefully this article will be valuable for you. And no matter if you're just starting out with react or have been coding for a while now, I will try strive to put up a very comprehensive guide that will get you started or will disclose some new information about react state that you might not know of.

I prepared this simple table of contents below so you can simply skip the part you're already familiar with and start with the one that interests you the most.

Table of contents:

  1. What is react state?
    • What is a react component?
    • How does the state work?
  2. How to define state in my components?
    • State in classes {}
    • State in hooks
    • Advanced approaches to handling state
  3. Frequent misconceptions about react state
  4. Recommendations
  5. Wrapping up

..and now's the time to dive right in! Let's start with a question:

1. What is react state?

Is a great question! I remember when I was starting out, I had really hard time grasping what the state actually means. Because yes, it is just a JavaScript variable that when its value gets changed, it will re-render the html code within the component - but that's not enough to know.

So in my opinion you can treat "react state" as some kind of heart and soul of a react component. React state is basically what gives life to your components.

1.1 What is a component in react?

I think to really understand react state you should know how to define what a word "component" means, because the only place where you are going to be using state is within the components only.

So what's the component? A component is basically everything a website or a web application consists of. It all starts with <html /> element of a website. That is one large component. Then, as you go deeper, there are two direct child components, <head /> and <body />.

The <body /> component for example, might have child-components as well, for example <header />, <main /> etc...

So as you see, every single part of a website can be a component - the only distinction is, from what perspective you're looking at it. If you want to rebuild a website, go look at the <body /> component and re-arrange things. If you would like to change a background colour of a call to action button, you're going to be changing a child component of some parent component and so on.

1.2 How does the react state work

Now that we know what the state and component are, we can put it all together. You already know, that the state can be found only in components. The question is, in which components can be a state defined? The answer is in every single one component!

Getting back to the example above, imagine you you're reading an article (actually you're reading one now so no need for imagination here πŸ˜† ) and below the article, there is a button with a label "Load comments". You click the button, there will be shown an animated loader while the comments are loading and after the comments are loaded, there will be shown a list of comments alongside with a form for adding a new comment.

Now my question is: what has happened there? Let's quickly look into that:

  • an animated loader was shown/hidden
  • a button to load comments was hidden
  • a new component with a list of comments was displayed

So basically a bunch of specific actions happened and somewhere in our application those actions had to be managed. DO you think those actions were managed on one place?

What would happen, if the uppermost component, in this case <html /> would manage each action that happens inside a document? Well, in a larger application it would be a mess.

That's why every single component in react has a possibility of declaring state inside it and you can make use of it.

So getting back to the example above, in the <html /> component's state, there would be only an article title and the article body. Then, in a child component <CommentsList /> we would store an information about whether the comments has been loaded and if they were rendered.

2. How to define state in my components?

Now that we know, what the react state is and how does it work, let's have a look how to actually use it πŸ˜‰ .

In react, whether we like it or not, there are multiple ways of how to define and use the state. As it usually goes, "hundred people, hundred tastes" and the same applies here. I will strive to provide you with an objective information about each way of declaring state and at the end of the article, I will provide my recommendation for each method (I also invite you to share in the comment your experience what method of declaring state worked the best for you!).

2.1 State in classes {}

The very first and the oldest way of declaring state in react is to use a class component. Check out the example below.:

import React from 'react';

class MyComponent extends React.Component {

    state = {
        isLoaded: false,
    };

    componentDidMount() {
        this.setState({isLoaded: true});
    }

    render() {
        if(!this.state.isLoaded) {
            return null;
        }

        return (<div></div>);
    }
}

In class components, the state is declared using a class property state which is a simple JavaScript Object with key/value pairs. You can put into the state any data type you like, for example:

import React from 'react';

class MyComponent extends React.Component {

    state = {
        isLoaded: false,
        currentDate: new Date(),
        items: [{}, {}, {},],
        largeNumber: 5456785647568767543645654321n
    };
}

2.1.1 Updating the state via object

Updating the state within class components is done by calling a function this.setState() while passing an object of values that you want to have updated. If you have large amounts of data in your state and you want to update only a single key, no problem with that, you can pass just an object with the key you want to update, check out the example below:

import React from 'react';

class MyComponent extends React.Component {

    state = {
        isLoaded: false,
        currentDate: new Date(),
        items: [{}, {}, {},],
        largeNumber: 5456785647568767543645654321n
    };

    componentDidMount() {
        this.setState({isLoaded: true}); // => will update only key `isLoaded`
    }
}

2.1.2 Updating the state via a function

When using this.setState(), instead of passing an object, you can pass a function that will return an object.

The reason for this option is simple, but let's look at examples:

state = {
    counter: 0,
};
this.setState({counter: this.state.counter + 1});
this.setState({counter: this.state.counter + 1});
this.setState({counter: this.state.counter + 1});
this.setState({counter: this.state.counter + 1});

setTimeout(() => {
    //2 seconds passed and we can pre pretty sure, that the counter equals 4
    console.log(this.state.counter)
}, 2000);

Do you think the value of counter within the state will be 4? Well, hold your horses, because that's not guaranteed πŸ˜‰ .

So what is going on then?

Well, the nature of the function setState() within a class component is asynchronous, and it means, that react gives you no guarantee, that after calling it, it will be immediately updated. There are lot more performance benefits into that, but it is out of scope of this article.

The question now is, what if I really need to call setState() multiple times in a row? Is it impossible?

No, it's very possible and it can be done with the function declaration. Have a look at the example below:

state = {
    counter: 0,
};
this.setState((prevState) => {
    return {counter: prevState.counter + 1};
});
this.setState((prevState) => {
    //this will set state to 2
    return {counter: prevState.counter + 1};
});
this.setState((prevState) => {
    //this will set state to 3
    return {counter: prevState.counter + 1};
});
this.setState((prevState) => {
    //this will set state to 4
    return {counter: prevState.counter + 1};
});

Interesting right? And that's the whole point of setting the state inside class components with a functional argument - because you will get access to previous state.

2.2 State in hooks

Another great way of using state in react component is using hooks. So if you don't like developing your react application with JavaScript classes, react gives you an ability of declaring the components with a simple functions only.

Up until 2019 this was a big pain for javascript developer who hadn't liked classes, because until 2019, there was no way of using state in functional components. Facebook's React team had this issue as well and they decided to do something about it. So in simple terms, they developed an ability for functional components to maintain state and they called it hooks.

So let's have a look of how to actually use it.

An example of simple functional component:

import React from 'react';

function ExampleComponent() {

  return (
    <div>
      <p>You clicked 0 times on the button</p>
      <button>Click me</button>
    </div>
  );
}

This component is basically dead though. It doesn't do anything. And that's why let's update the component and add some state to it so as it is more interactive.

import React, { useState } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times on the button</p>
      <button onClick={() => setCount(count+1)}>Click me</button>
    </div>
  );
}

Let's go through what has just happened above. We imported a function useState() from react package. useState is a function that lets you use state inside a functional component. Beware: useState() can be used only inside a functional component (if you're adventurous developer, you can try to import it and use it inside a class component, you will experience some errors being thrown in your console 😁).

Let's move on. What's this: [count, setCount] = useState(0); Yes, looks totally strange, but is totally simple, I'll explain.

Firstly, the function useState() receives only one argument and that is the default value of your state parameter and returns an array of the two elements. The first element in the array (0th index) is the current state value and the second element in the array (1st index) is a function to update the state in the value. So it can be written like:

const stateData = useState(0);
const count = stateData[0];
const setCount = stateData[1];

Pretty clear, right? And the strange declaration with square brackets is Array destruction assignment known introduced in ES6.

When using hooks or in other words when using the function useState() there is no need for "previous state" as with this.setState() from class component, you just simply modify the variable as you need:

const [count, setCount] = useState(0);
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);

console.log(count); // => will print 4

Hooks are very powerful react feature that allows you to write way less code than with classes. Totally worth checking out a bit more about the topic in the very comprehensive react docs: reactjs.org/docs/hooks-intro.html .

2.3 Advanced approaches to handling state

So far, we've wen through fundamental ways of handling the state in react. And even though, when developing an application of a larger scale, you are probably going to be using some kind of framework for handling your application state.

And in this part we're going to be talking about exactly that.

Currently, there are dozens of available frameworks for handling react state and it may seem very hard to actually choose the right one. But, I will be do some recommendations at the end of the article, now I will list just the most popular ones with a short description and links, where you can learn more, if you start to like them:

2.3.1 React Redux

This used to be one of the very first state management frameworks for react. It consists of:

  • a provider - that is a upper-most component that serves as a wrapper and stores the state of your app
  • connector - is a function that connects any component in your hierarchy with the store

What react redux allows you to do is to actually ditch any useState() or this.setState() calls from your code and replaces those with pre-defined actions or predefined functions that you prepare in a separate file - which in the end makes your code way more readable.

Let's go for a quick example:

//a simple component TodoItem.js
import React from 'react';

export default const TodoItem = ({ onClick, completed, text }) => (
  <li onClick={onClick} className={completed ? "incomplete" : "incomplete"}>{text}</li>
)

//a parent component, TodoList.js
import React from 'react';
import {toggleTodo} from '../actions';
import Todo from 'TodoItem.js';

const TodoList = ({todos}) => (
  <ul>
    {todos.map(todo => (
      <Todo key={todo.id} text={todo.text} onClick={() => toggleTodo(todo.id)} />
    ))}
  </ul>
)

//actions.js
export const toggleTodo = id => ({
  type: 'TOGGLE_TODO',
  id
})

//reducer.js
const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ]
    case 'TOGGLE_TODO':
      return state.map(todo =>
        todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
      )
    default:
      return state
  }
}

export default todos

For more information check out the official React Redux docs: react-redux.js.org.

2.3.2 useReducer() hook

Almost a year ago, React team introduced hooks, which I described above. Alongside with hooks, there was another very interesting update in react. And it was a functionality called "reducer".

What is does, truth to be told, is the very same as react-reduct library πŸ˜‚ .

If you check out the official documentation reactjs.org/docs/hooks-reference.html#usere.. you can see, that even the examples as quite similar to the redux's examples here: react-redux.js.org/introduction/basic-tutor...

2.3.3 MobX

MobX is I think rather popular state management library and it has pretty significant difference compared to Redux and that is that MobX can have multiple stores to store the state of the application, while Redux has only one.

2.3.4 Apollo client (graphql)

If you're keen on using GraphQL approach, you will certainly want to go for this library that handles basically everything you need in state management and graphql practises. Check out official docs apollographql.com/docs/react for more information.

2.3.5 Relay

Relay is a state management library used and developed by Facebook team. In my opinion, the library is currently competing for attention with Apollo client. Anyways, this is also very popular library worth checking it out on this link: relay.dev.

3. Frequent misconceptions about the react state

Is the state the same or similar to props? No, it's not.

Is the react context the same or similar to the state? No, it's not.

Can I assign a value to state directly, for example like this: this.state.current = 10? No, you can't. Well, technically it is possible, it is even going to re-render your component, but lifecycle methods (didUpdate, willReceiveProps) will not be working correctly.

Is a state management library required for creating a web application? No, it is not at all. You can build a complex application just with classes' this.setState() or just by using hooks.

4. Recommendations

And now, I will provide you with some recommendations on how to decide which method of managing state to choose and how to handle it.

Should I choose class components or hooks to develop my application?

When you are a single developer working on your own project use whatever suites you the most. For example, do you love classes? So go for classes then! (I personally used classes even for components without any state - it was because of my habit of creating those components very quickly.) And when you used classes for quite a time on your own project, I recommend taking a look into hooks as well - all while heavily using classes. You will start slowly using them and making your coding experience that much more effective.

If you are working with a team, learn and use whatever the team prefers - since in the hierarchy of this world a group is always superior to the individual. But, let's say that you are in a team of 4 developers and nobody seems to take a lead of what you should be using while working on your next project. In this case I recommend you decide what you like the most, find 3 good reasons why it should be used and then take the lead and present it for the group! 😎

Should I use a state management library or can I just develop my application with build-in react state functions? Note that you can really built a superior application with just useState and this.setState() functions. I did. But the important point is we were building the application 3 years and it grew very slowly, so it was very easy to gradually to extend it, manage code, refactor it and from that reason it was really easy to go without any state management library. If I should do it again, I would rather use some library with built in router.

Wrapping up

Guys, there you have it! πŸ₯³ A comprehensive guide on how to manage state in your application in 2020.

I am very curious on your takeout from this and also if I missed something, let me know here in the comments below so I can update the article and give you credits.

In my opinion, the fundamentals of state management has never changed and never will and I will always put priority into learning the very basics which you can very easily build upon your next venture.

From my experience, state management is way larger topic that can be put into a single article, so if you'd like to see a separate article or even youtube explanatory tutorial solely about react state - let me know in the comments πŸ™.

One more thing I'd like to ask you, if you liked this article, don’t forget to share it with people you like (or maybe also people you don’t like, maybe you’ll became friends! πŸ˜† ).

Anyways, thanks for reading and I’ll see you in the next article! πŸ€“

Yours in coding,

Ivan

Tapas Adhikary's photo

Ivan, this is just wow.. bookmarked for re-reading.. Thanks for sharing..

Bolaji Ayodeji's photo

Very helpful, thanks for sharing!

Juan F Gonzalez's photo

Hey Ivan.

Have you had any experiences with Recoil?

Ivan's photo

Hello Juan, truth to be told I've noticed this only recently and I am yet to fully grasp the idea behind the concept of recoil.

Also I haven't found any article here on hashnode about it.

May I ask, is it something you are after? Would you welcome such article about recoil?

Thanks! (and I hope I answered your question well! πŸ™‚)

Juan F Gonzalez's photo

Ivan Thanks for replying!

The reason I ask is that it started to appear more & more in my twitter feed and also on Github recommendations.

I think is natural that after the fall of Redux many others started coming up with ideas, what's interesting to me is that is coming from the folks at Facebook so it's not "just another state mngmt library"

Ali Dhuniya's photo

very helpful for anyone especially who is learning React like me

Sandeep Panda's photo

Thanks for authoring this Ivan and fulfilling my RFA! It's definitely going to help me. Bookmarked -- Going to read it tonight. :)

Rutik Wankhade's photo

Thanks for writing this amazing post. I Learned a lot.

Uzodike Oguejiofor's photo

Nice writeup. Really helpful.