You’re  One Import Away From Managing Global State In React
Tags: react, javascript, opensource, showdev
Published: 2022-03-08 13:22:53

## Intro
When it comes to using third party libraries in my projects, I’m a big fan of libraries which provides simple and intuitive API, the ones that just makes sense at first glance.

There’re many libraries for managing global state in ReactJS, so when it comes to choosing one, as said earlier I look for simplicity and intuitive API.

That being said I would like to present to you a simple example, in this example we’re going to use a state management library called `state-pool` for managing our global states.

Assuming you have a basic knowledge of ReactJS and hooks, try to take a little bit of your time to understand the code below for managing global state in a component 

```js
import React from 'react';
import { createStore } from 'state-pool';


const store = createStore();  // Create a store for storing our global state
store.setState("count", 0);  // Create and initialize "count" global state

function Counter(props){
    // Use "count" global state
    const [count, setCount] = store.useState("count");

    // Other stuff 
}
```

### Questions

1. How easy was it for you to understand this example?

2. How familiar was it, assuming you have a basic knowledge of ReactJS and hooks?

You can leave your answers to these questions to the comments section below.



## Now let’s go!…
State pool is a state management library based on global variables and react hooks, it comes with a very simple and intuitive API which follows built in state management patterns in React(Hooks).

With these patterns you’re likely to be familiar with state pool without even learning it, like in a previous example above most people with a basic knowledge of ReactJS and hooks could understand how it works.

Managing global state with `state-pool` is very simple, all you need to do is

1. Create a store(which is basically a container for your global state) by using `createStore`
2. Create and initialize a global state by using `store.setState`
3. Use your global state in your component through `store.useState` hook

These three steps summarises pretty much everything you need to use `state-pool`.

Below is an example showing how to use `state-pool` to manage global state 

```js
import React from 'react';
import { createStore } from 'state-pool';


const store = createStore();  // Create store for storing our global state
store.setState("count", 0);  // Create and initialize a global state

function ClicksCounter(props){
    // Use "count" global state
    const [count, setCount] = store.useState("count");

    const incrementCount = (e) => {
        setCount(count + 1)
    }

    return (
        <div>
            Count: {count} <br/>
            <button onClick={incrementCount}>Click</button>
        </div>
    );
}
```

At this point you might have noticed that all you need to import from `state-pool` to be able to manage your global state is `createStore`, this is because store implements and encapsulates everything you need to manage your global state, this makes sense because a store is a container for your global states, so it should be able to manage everything in it, you just need to create one and use it.

It’s easy to understand that `store.setState` is used to set state in a store

Also if you’re already familiar with the built in `useState` hook it’s easy to understand that `store.useState` works the same way but it’s using the state from a store.

## store.useReducer
At this point you might have guessed there’s probably something like `store.useReducer` that works like built in `useReducer`, well you’re right!…

Below is a simple example showing how to use `store.useReducer` hook

```js
store.setState("user", {
    name: "Yezy",
    age: 25,
    email: "yezy@me.com"
});

function myReducer(state, action){
    // This could be any reducer
    // Do whatever you want to do here
    return newState
}

function Component(props){
    const [name, dispatch] = store.useReducer(myReducer, "user");

    // Other stuff ...
}
```

## Selector & Patcher
With state pool you can subscribe to deeply nested global state or a derived state, here is an example

```js
store.setState("user", {
    name: "Yezy",
    age: 25,
    email: "yezy@me.com"
});


function UserName(props){
    const selector = (user) => user.name;  // Subscribe to user.name only
    const patcher = (user, name) => {user.name = name};  // Update user.name

    const [name, setName] = store.useState("user", {selector: selector, patcher: patcher});

    const handleNameChange = (e) => {
        setName(e.target.value);
    }

    return (
        <div>
            Name: {name} <br/>
            <input type="text" value={name} onChange={handleNameChange}/>
        </div>
    );
}
```
Here `selector` & `patcher` are used for specifying a way to select deeply nested state and update it.

- `selector` should be a function which takes one parameter which is the global state and returns a selected value. The purpose of this is to subscribe to a deeply nested state.

- `patcher` should be a function which takes two parameters, the first is the global state and the second is the selected value. The purpose of this is to merge back the selected value to the global state once it's updated.

## State persistence 
State pool has a built in support for state persistence, it makes saving your global states in your preferred permanent storage very easy, all you need to do is tell state pool how to save, load, clear and remove your global state from your preferred storage by using `store.persist`

The way to implement these is by calling `store.persist` and pass them as shown below 

```js
store.persist({
    saveState: function(key, value, isInitialSet){/*your code to save state */},
    loadState: function(key){/*your code to load state */},
    removeState: function(key){/*your code to remove state */},
    clear: function(){/*your code to clear storage */}
})
```

After implementing these four functions you're good to go, you won’t need to worry about calling them, **state-pool** will be doing that for you automatically so that you can focus on using your states.

Both `store.setState`, `store.useState` and `store.useReducer` accepts an optional configuration parameter, `persist`, this is the one which is used to tell **state-pool** whether to save your global state to a permanent storage or not. i.e

```js
store.setState(
    key: String,
    initialState: Any,
    config?: {persist: Boolean}
)
```

```js
store.useState(
    key: String,
    config?: {default: Any, persist: Boolean, ...otherConfigs}
)
```

```js
store.useReducer(
    reducer: Function,
    key: String,
    config?: {default: Any, persist: Boolean, ...otherConfigs}
)
```

By default the value of `persist` in all cases is `false`(which means it doesn't save global states to a permanent storage), so if you want to activate it, you have to set it to be true.

What's even better about **state-pool** is that you get the freedom to choose what to save in your permanent storage, so you don't need to save the whole store in your permanent storage, but if you want to save the whole store you can use `PERSIST_ENTIRE_STORE` configuration.

Below is an example showing how you could implement state persistance in local storage.

```js
import { createStore } from 'state-pool';

const store = createStore();

let timerId: any = null
const DEBOUNCE_TIME = 1000  // In milliseconds

store.persist({
    PERSIST_ENTIRE_STORE: true,  // Use this only if you want to persist the entire store
    saveState: function(key, value, isInitialSet){
        const doStateSaving = () => {
            try {
                const serializedState = JSON.stringify(value);
                window.localStorage.setItem(key, serializedState);
            } catch {
                // Ignore write errors
            }
        }

        if(isInitialSet){
            // We don't debounce saving state since it's the initial set
            // so it's called only once and we need our storage to be updated
            // right away
            doStateSaving();
        }
        else {
            // Here we debounce saving state because it's the update and this function
            // is called every time the store state changes. However, it should not
            // be called too often because it triggers the expensive `JSON.stringify` operation.
            clearTimeout(timerId);
            timerId = setTimeout(doStateSaving, DEBOUNCE_TIME);
        }
    },
    loadState: function(key){
        try {
            const serializedState = window.localStorage.getItem(key);
            if (serializedState === null) {
                // No state saved
                return undefined
            }
            return JSON.parse(serializedState);
        } catch (err) {
            // Failed to load state
            return undefined
        }
    },
    removeState: function(key){
        window.localStorage.removeItem(key);
    },
    clear: function(){
        window.localStorage.clear();
    }
})
```

**Note:** When you set `PERSIST_ENTIRE_STORE = true`, **state-pool** will be persisting all your global states to the permanent storage by default unless you explicitly specify `persist = false` when initializing your global state.

You can do a lot with state pool apart from few mentioned, all at the cost of importing only one thing `createStore`.

All you need is `createStore` the rest can be handled by a store itself.

## Feature & Advantages 
Here are some of the features and advantages of using state pool

- Simple, familiar, flexible and very minimal core API but powerful
- Built-in support for state persistence
- Very easy to learn because its API is very similar to react state hook's API
- Support selecting deeply nested state
- Support creating global state dynamically
- Can be used outside react components
- Support both key based and non-key based global state
- States are stored as global variables(Can be used anywhere)
- Doesn't wrap your app in context providers
- Very organized API, You can do almost everything with a single import

## Installing state pol
You can install state pool with
`yarn add state-pool`
Or
`npm install state-pool`

## Conclusion
Congratulations for making to this point 🎉🎉,
if you want to learn more about this state management library you can check its full documentation [HERE](https://github.com/yezyilomo/state-pool).

Live examples [HERE](https://yezyilomo.github.io/state-pool-examples).

Giving it a star on [GitHub](https://github.com/yezyilomo/state-pool) will be appreciated.

Lastly I would like to hear your opinions, what do you think of this library?.