Skip to main content
Two main paradigms exist when it comes to global state management, not just in React, but other frameworks too. Some prefer atomic states which are small, lightweight stores holding the store for just a subset of features, the other school prefers a giant store holding everything in your application. Hana uses the latter, as it enables a no-boilerplate, no-config setup:
import { useStore } from '@hanabira/store';

const Component = () => {
  	const [item, setItem] = useStore('item');

	return (
    	<div>
      		<p>{item}</p>
      		<button onClick={() => setItem('New value')}>Set item</button>
    	</div>
  	);
};
And all data is imeedietly synced to the single store on set. While this is great in terms of simplicity and ease-of-use, its easy to quickly make a mess of the createStore() function, especially when you start taking advantage of reducers and default states for lots of different features. Modules allow you mimick the atomic state model, where you can break your state into “modules” by feature

Creating a store module

A module contains the initial state & reducers of a feature-set, and is solely for organization reasons. It does not change the way Hana Store works in any way, but makes your code more organized and easier to create a mental model about
import {
    LOCALE_STORAGE_AUTH_KEY,
    LOCALE_STORAGE_REFRESH_TOKEN,
} from '@/utils/constants';

import type { Module } from '@hanabira/store';

const AuthState: Module = {
    namespace: 'auth',
    state: {
        user: null,
        isAuth: false,
        [LOCALE_STORAGE_AUTH_KEY]: null,
        [LOCALE_STORAGE_REFRESH_TOKEN]: null,
    },
    reducers: {
        logout: (state) => ({ ...state, user: null, isAuth: false }),
        login: (state, payload) => ({
            ...state,
            isAuth: true,
            user: payload.user,
            [LOCALE_STORAGE_AUTH_KEY]: payload.token,
            [LOCALE_STORAGE_REFRESH_TOKEN]: payload.refreshToken,
        }),
    },
};

export default AuthState;
This AuthState module contains:
  • the initial state for the auth state of the application
  • login and logout reducers which just update the state
  • A namespace auth for the state and reducers in this module

Registering a module

You can add all your created modules in the createStore() function like this:
import { createStore } from '@hanabira/store';

import AuthState from '@/store/auth';

const store = createStore({
  ..., // Other config
  modules: [AuthState],
});
This will automatically load the state & reducers to the global state so you can use them just as before

Module Namespaces

In bigger applications, it gets difficult keeping track of the state keys, and you may end up overwriting some of them by using the same names. To fix this, Hana allows you to namespace your modules, and this will automatically append the namespace to the state/reducer key
const AuthState: Module = {
    namespace: 'auth',
    state: {
        user: null,
    },
    reducers: {
        logout: (state) => ({ ...state, user: null }),
        login: (state, payload) => ({
            ...state,
            user: payload.user,
        }),
    },
};
Once this is registered, you can use the module values like this:
import { useStore } from '@hanabira/store';

const Component = () => {
  const [user, setUser] = useStore('auth.user');

  return (
    <div>
      <p>{user?.name || 'Unknown'}</p>
      <button onClick={() => setUser({...})}>Set user</button>
    </div>
  );
};
This is the same syntax for using state values, except we appended the module namespace to the state key. It works the same way for reducers:
import { useReducer } from '@hanabira/store';

const Component = () => {
  const logout = useReducer('auth.logout');

  return (
    <div>
      <button onClick={() => logout()}>Logout</button>
    </div>
  );
};