Building global store with React Hooks
Tags: react, hooks, reacthooks, redux
Published: 2019-02-17 22:09:10

---
title: Building global store with React Hooks
published: true
description: I'm pretty sure [there are many implementations](https://nikgraf.github.io/react-hooks/) of global store using React Context and hooks, but I want to tell my story of developing a simple yet convenient solution, so that I can hear your feedback on it. 
tags: react, hooks, react-hooks, redux
cover_image: https://images.unsplash.com/photo-1432107294469-414527cb5c65?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1950&q=80
---

Tere, tere, tere! In this post I will go through my case of development a global state management solution. Nothing ground breaking, just the flow of developing a suitable API for my case.

First of all, if you're unfamiliar with [React Context](https://reactjs.org/docs/context.html) and [React Hooks](https://reactjs.org/docs/hooks-intro.html), it's time to become friends with them.  Also I can honestly say that even after some time of using Hooks, I still have a tab with Hooks documentation pinned (it gets updated!).

## Problem statement

Knowing that all other projects started with Redux straight away, I decided to postpone writing boilerplate, creating many files, adding reselect to "dependencies". I wanted something Stupid Simple.

## Before Hooks

I started without Hooks first, as I was afraid they won't be released before my product has to be released, so I had pretty straightforward setup:

```jsx
const context = createContext({field: null});

class DataProvider extends Component {
    state = {
        field: null,
        otherField: { default: 'I am default' }
    }

    getField = async () => {
        const field = await api.getField();
        this.setState({ field });
    }

    render() {
        const value = { data: this.state, getField: this.getField };

        return <context.Provider value={value}>{ this.props.children }</context.Provider>
    }
}
```

This is a [common approach](https://reactjs.org/docs/context.html#dynamic-context), nothing special here.
> I will often point out that I don't do premature performance optimisations. Like here we create new value every time `field` updates, so even if some consumer doesn't use `data.field` it still gets updated. But is it a problem?

Later, I had some contexts, which operated with more data and had more actions, so I separated context value to `{ data, api }` (by the way it was good challenge to separate stores to a simple modules with small responsibility area!).

So my consumers looked like this:

```jsx

const Consumer = ({ title }) => {
	return (
		<BooksConsumer>
			{({ data: { books }, api: { getBooks } }) => (
				<AuthorsConsumer>
					{({ data: authors }) => <OurComponent title={title} books={books} getBooks={getBooks} authors={authors} />}
				</AuthorsConsumer>
			)}
		</BooksConsumer>
	);
};

```

Okay, looks ugly. And also for each context I had to repeat all the boilerplate of creation. Then I heard about [hooks released soon](https://github.com/facebook/react/commit/200817599a9a4a5ca4ede377e39387105f0b3318) and decided to go all in. At least to get rid of ugly (I don't think so) render functions nesting.

```jsx
const Consumer = ({ title }) => {
	const {
		data: { books },
		api: { getBooks },
	} = useContext(BooksContext);
	const {
		data: { authors },
	} = useContext(AuthorsContext);

	return <OurComponent title={title} books={books} getBooks={getBooks} authors={authors} />;
};
```

> I have `Consumer`s wrappers around components for easier testing.

So the next problem was the boilerplate I had to write for each context. And as [one smart guy said](https://twitter.com/lexswed/status/1073600675312009216), we want to move our dynamic pattern to a function. The first idea was to simply have something like this:

```jsx
function createStore(data, api) {
	const context = createContext(state);

	const Provider = ({ children }) => {
		const [state, updateState] = useState(data);
		const value = {
			data: state,
			api: wrapApi(api, updateState, state),
		};
		return <context.Provider value={value}>{children}</context.Provider>;
	};

	return [context, Provider];
}

function wrapApi(api, updateState, state) {
	return Object.keys(api).reduce((res, key) => {
		res[key] = api[key](updateState, state);
		return res;
	}, {});
}
```

So that the usage of it can be:

```jsx

const api = {
	getBooks: (updateState, state) => async () => {
		const books = await fetchBooks();

		updateState({ ...state, books });
	},
};


const state = {
    books: [],
    notBooks: []
}

export const [BooksContext, BooksProvider] = createStore(state, api);

```

Now, every time we need a new context - we use one function with a simple API. Notice, that I put `updateState, state` into the closure of our api, so that our components can easily just call functions from `api`.
> Fun fact: `api` name was chosen because it short, and not because it makes API requests 🤷🏻‍♂️.

Again, you might be tempting to make "optimisations":
```jsx
const Provider = ({ children }) => {
		const [state, updateState] = useState(data);
		const value = useMemo(
			() => ({
				data: state,
				api: wrapApi(api, updateState, state),
			}),
			[state, updateState],
		); // this
		return <context.Provider value={value}>{children}</context.Provider>;
	};
```
But of course it won't make things better, state will change (as we want it to), so we will need to create new value again and bind API again.

Okay, it actually seems enough already. But I wanted some debugging experience. Not that I miss redux-dev-tools or anything... I decided to go with reducer now and add some powerful debugging capabilities (`console.log`):

```jsx
function createStore(reducer, actions, initialState) {
	const context = createContext(initialState);

	const Provider = ({ children }) => {
		const [data, dispatch] = useReducer(reducer, initialState);

		const actionCreators = bindActionCreators(actions, dispatch, data); // bind same way as before but add console.log 😅

		return <context.Provider value={[data, actionCreators]}>{children}</context.Provider>;
	};

	return [context, Provider];
}
```

So I could use it as:
```jsx

function reducer(state, { action, payload }) {
	switch (action) {
		case SET_BOOKS: {
			return { ...state, books: payload };
		}
		default: {
			return state;
		}
	}
}

const api = {
	getBooks: (dispatch, state) => async () => {
		const books = await fetchBooks();

		dispatch({ action: SET_BOOKS, payload: books });
	},
};

const defaultState = {
	books: [],
	notBooks: [],
};
const [BooksContext, BooksProvider] = createStore(reducer, api, defaultState);
```

> Oh mama... Looks like Redux :c

Let's simplify a bit:

```jsx
function createStore(reducer, actions, initialState) {
	const context = createContext(initialState);

	const Provider = ({ children }) => {
		const reducerFn = (state, { type, payload }) => reducer[type](state, payload); // we create reducer here instead

		const [data, dispatch] = useReducer(reducerFn, initialState);

		const actionCreators = bindActionCreators(actions, dispatch, data);

		return <context.Provider value={[data, actionCreators]}>{children}</context.Provider>;
	};

	return [context, Provider];
}
```

And the usage would be:

```jsx

const reducer = {
	[SET_BOOKS]: (state, payload) => ({ ...state, books: payload }), // or just `books` as param
};

const actions = {
	getBooks: (dispatch, state) => async () => {
		const books = await fetchBooks();

		dispatch({ action: SET_BOOKS, payload: books });
	},
};
const [BooksContext, BooksProvider] = createStore(reducer, actions);

// component

const Consumer = () => {
    const [{ books  }, { getBooks }] = useContext(BooksContext);

    // ...usage
}
```

Looks better. Especially with TypeScript: I have secure `actions`, so that you can dispatch only `enum Actions` which then used in reducer. But why is it better than using redux? It's not, but.

Usually, you would import some helper functions from state library, like `connect` form `react-redux` and yours action creators (maybe even with `redux-thunk`). Then you map your state and dispatchers to props. With current API I can just select what I need already from context, knowing what it is and where it comes from (single source), no need for file-traveling (if you don't use redux ducks). I only needed to do something... Write own Hook!

## `useStore`

```tsx
function createStore<D, A extends StoreActions>(
	reducer: StoreReducer<D, any>,
	actions: A,
	initialState?: D,
): Store<D, A> {
	const context = createContext<ContextValue<D, A>>([initialState, actions ? ({} as BoundStoreActions<A>) : null]);

	const StoreProvider: FunctionComponent = ({ children }) => {
                // create Reducer, use Reducer, bind actions
		const store = useStoreProvider(reducer, actions, initialState);

		return <context.Provider value={store}>{children}</context.Provider>;
	};

	const useStore: useStore<D, A> = selector => {
		const [data, actions] = useContext(context);
                 // map state and actions to anything you need (object, array, just one field)
		const selected = selector(data, actions);

		return selected;
	};

	return [context, StoreProvider, useStore];
}
```

And then in my component:

```tsx
const BooksList = ({ authorId ) => {
	const { books, setFavorite } = useBooks((store, actions) => ({
		books: store.books.filter(book => book.authorId === authorId), // feels like selector, isn't it? 🙃
		setFavorite: actions.setFavorite,
	}));
        const authorInfo = useAuthors(store => store.authors[authorId]);

	return (
		<List>
			{books.map(book => (
				<Book key={book.id} title={book.title} author={authorInfo} />
			))}
		</List>
	);
};
```

Pretty neat, isn't it?

Why I put actions to the store? It means that all my actions have to be bound every time store changes, why not just use them as normal functions, passing them to component? Like this:

```jsx
// modules/books
const [BooksContext, BooksProvider, useBooks] = createStore(reducer, initialState);
// components/Books
import {getBooks, getAuthors, useBooks } from 'modules/books'

const Books = () => {
    const {books, getBooks, getAuthors } = useBooks(store => ({ books: store.data.books, getBooks, getAuthors});
     // I wrote getBooks, getAuthors 3 times :/

    // use it
```

So what could be done there is passing actions that I want to bind to a store and bind them in `useBooks`. It means that it can be any action, even unrelated to a store and hence can dispatch action type that doesn't exist in store. But at the same time it can allow to have reusable actions fro different stores, which is rare case. Also, store creation is easier now, no need to bind anything during creation. But the main thing here is that you have to import actions which have names and them take them from `useBooks` with either same name (which can be confusing) or make up a new name for them (which is soo tough).

## What's next?

The thing is I'm already quite happy with the current solution (not types, btw, seems like I have to learn how to write them properly). It works, there is **no performance issues** with 9+ stores with different kind of data and purposes. But I'd like to hear your opinions and solutions, take a look at the [source code](https://github.com/LexSwed/use-pipe), it has [second proposal](https://github.com/LexSwed/use-pipe/blob/master/src/createStore2.tsx) with [example](https://github.com/LexSwed/use-pipe/blob/master/example/createStore2.js). If it's something you'd like to use and improve - you're welcome! Then I can continue working on it, add tests, examples of usage, maybe performance tweaks, like selectors, etc (not that I won't working on it, but anyway...)!

So how would you created your global stores?