Global vs Local State in React
Tags: react, webdev, javascript, redux
Published: 2020-09-25 18:38:16

The **state** and **state management** is seemingly the most **common** and **interesting** topic when it comes to app development on the **front-end**. Thus everyone is chasing the most efficient and prominent way to manage their application state... **are we?**

I'm not a guru of the state management world, however; I want to familiarize you with some basic concepts with examples, which are:

- **State**
- **Global state**
- **Local state** _(Better put everything in the store 😎)_

And further, I'll say:

- **When to use global and local state?**
- **Popular misconceptions about state management**

## The State

Why we need the state at all? The state is the **current data** that our app **stores** to control its behavior. For example, the _checkbox_ stores data (boolean) if it's **on** or **off**.

## Global State

Global means our state is accessible by **every** element/component of the app. But the important fact is that it **pollutes** the whole app since it echoes in every component that accesses it

### Release the beast!

To illustrate the problem lets create a simple counter with _React_ and _Redux_:

```jsx
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { inc } from './actions'

export const Counter = () => {
  const dispatch = useDispatch()
  const count = useSelector(store => store.counter.count)

  return (
    <>
      <h1>The count is: {count}</h1>
      <button onClick={() => dispatch(inc())}>Increment</button>
    </>
  )
}
```

What if I'll do something like this somewhere in the app:

```jsx
<>
  <Counter />
  <Counter />
</>
```

You're right. Both counters are showing up the same count:

![counters animation](https://dev-to-uploads.s3.amazonaws.com/i/mw3ch8l1luhy0zairjff.gif)

With `useSelector` hook we are accessing some data stored in the global store previously declared in our app. So the **store** probably looks like this:

```js
{
  counter: {
    count: 0
  }
}
```

It is clear that both counters display the same number cause they reflect the same state

### The wind of change

To store multiple counts in the global store. We'll need to do something like this:


Change the structure of the store:

```js
{
  counters: [{ count: 0 }, { count: 0 }]
}
```

Change the `Counter`:

```jsx
export const Counter = ({ part = 0 }) => {
  const dispatch = useDispatch()
  // Now it selects just one of counters
  const count = useSelector(store => store.counters[part].count)

  return (
    <>
      <h1>The count is: {count}</h1>
      {/*We'll also need to change our action factory and reducer */}
      <button onClick={() => dispatch(inc(part))}>Increment</button>
    </>
  )
}
```

And finally:

```jsx
<>
  <Counter />
  <Counter part={1} />
</>
```

**Nailed it!** Just change **store**, **reducer**, **component**, and manually pass the `part` property to `Counter`...
What can go wrong?

### Choose your weapon _wisely_

I am a big fan of _MobX_. The _MobX_ team did a great job **bending** JavaScript to allow you to **feel** _reactive_ in it:

```jsx
import React from 'react'
import { observable } from 'mobx'
import { observer } from 'mobx-react'

const counter = observable({ count: 0 })

const Counter = observer(() => (
  <>
    <h1>The count is: {counter.count}</h1>
    <button onClick={() => counter.count++}>increment</button>
  </>
))
```

Wow, it looks so neat!
And with multiple counters:

```jsx
const counter = observable({ count: 0 })
const counter2 = observable({ count: 0 })

// counter is now a prop:
const Counter = observer(({ counter }) => (
  <>
    <h1>The count is: {counter.count}</h1>
    <button onClick={() => counter.count++}>increment</button>
  </>
))
```

Next:

```jsx
<>
  <Counter counter={counter} />
  <Counter counter={counter2} />
</>
```
We end up with less code, but still, we have to pass state manually for each of component 🤦‍♀️
## The local state

Even if the above examples seem stupid, the problem is real and it shows why we need a **local state**. **Local state** is not the state we **define** locally. It has the goal to **encapsulate** the dataflow within the component:

```jsx
const Counter = () => {
  const [count, setCount] = useState(0)
  const incrememt = () => setCount(count => count + 1)

  return (
    <>
      <h1>The count is: {count}</h1>
      <button onClick={increment}>increment</button>
    </>
  )
}
```

And voila! counters do not share the state anymore!

```jsx
<>
  <Counter />
  <Counter />
</>
```

### The dark nature of the local state

Sadly; the local state seems to be much **less** **manageable** and **debuggable**. What's more, it can also hurt the **performance** of _React_ app if not managed well. When you pass state many levels down and change state somewhere on the top component, **all** of its children get rerendered (inside virtual DOM) with it. It also **tangles** components together and makes them **less** **scalable**. _Redux_ **isolates** state from components **lifecycle** and **I/O**. On the other hand, **stateful** components seem to be more modular - **statefulness paradox**? No. If your app gets more **complex** things start to be more connected and it's harder to separate them, whenever it comes to global or local state

## Local vs global state

The **question** you should ask yourself to keep state local or global is not to **share** or not, it's about to **encapsulate** or not

## Which solution to choose

Well established managers like _Redux_ and _MobX_ that supports tools like _time-travel_ (see [`mobx-state-tree`](https://mobx-state-tree.js.org/)) make debugging a pleasure. But it comes with a cost - _Redux_ is known for being **verbose** and you have to keep **discipline** when working with it. It's meant to be used in **huge** projects. If you insist to use _Redux_ in your tiny app. Take a glance at [`redux-toolkit`](https://redux-toolkit.js.org/) - an official tool to reduce _Redux_ boilerplate or search for the other _Redux_ wrapper. [_Immer_](https://immerjs.github.io/immer) is a wonderful library to write reducers. I like [_Hookstate_](https://hookstate.js.org/) - a straightforward way to lift the state up. [_Effector_](https://effector.now.sh/) is worth checking and there are plenty of libraries waiting for you to discover them

### Don't follow the example

What I'm trying to say is you shoudn't write your code to look **exactly** like examples in the web. If they want to show how things work they probably sacrifice some good things to be more specific. Reach for _Redux_ `Counter` from this article and write some custom hook:

```jsx
const useCounter = (part = 0) => {
  const dispatch = useDispatch()
  const count = useSelector(store => store.counters[part].count)
  const increment = () => dispatch({ type: 'increment' })
  return [count, increment]
}
```

And our `Counter` becomes:

```jsx
export const Counter = ({ part = 0 }) => {
  const [count, increment] = useCounter(part)
  return (
    <>
      <h1>The count is: {count}</h1>
      <button onClick={increment}>Increment</button>
    </>
  )
}
```

This way we moved most of the state logic outside the component. _Hooks_ are like **functions** for components. So **split** your component into **hooks** and **compose** them ass (I hope) you do with your functions

## Popular misconceptions

- _Redux_ is a bad tool because it's too verbose

Redux is crude - that is correct. It is not designed to seduce you with code examples, but to provide transparent data flow

- _Context API_ can replace Redux (or any other state manager)

_Context API_ is not a state manager itself. Actually, you have to do all the management yourself like a pagan if you'll use it for that purpose. As if that were not enough, unlike several state managers, it does not optimize re-rendering. Instead, it can easily lead to unnecessary re-renders. Reach for [this](https://blog.logrocket.com/pitfalls-of-overusing-react-context/) great article

- You can avoid re-renders caused by _Context API_ if you **destructure** the context value

**_No!_** Please, before even thinking of doing that. Read [this](https://blog.isquaredsoftware.com/2020/01/blogged-answers-react-redux-and-context-behavior) post written by the _Redux_ maintainer @markerikson

- _Context API is made for passing _state_ down (or lifting up)

The truth is: _Context API_ is just a **prop passing** solution. I think the source of this popular misconception is that a variety of libraries use _context_ for **similar** purposes, for example: passing theme state. But the theme is something that changes **occasionally**, and theme change typically **should** rerender the whole app

- _MobX_ users practice voodoo

🙊

## Conclusion

I have to confess that this section is troublesome. Should I address some advice? I've read a lot of articles touching this matter and I feel like it's so much to say - it's a complex problem to solve. So I'll just ask: what do you think about the current state of state management in _React_? and what is your current solution to deal with this problem?