Skip to content

React useReducer + Context API based store for smart components

License

Notifications You must be signed in to change notification settings

DimitryDushkin/react-localux

Repository files navigation

React Localux 🐬 — context and hooks based store for React, TypeScript-friendly

npm version

React Localux is comfortable solution for separation store-related logic from react components. Unlike Redux main goal for React Localux is being home for compact local stores of smart components. Based on useReducer and new Context API.

For example, you might have screen-like component of some item with vast logic related to this screen and it is required to support several different stacked screens of such items. Implementing such feature with global Redux store result complicated code, but it turns out that using single local store for each screen produces quite straightforward solution.

React 16.8+ only, because it uses hooks (useReducer mainly). For React < 16.8 see v1.

Main features

  • Compact creation of store: just declare state, methods (combination of reducers and actions) and effects
  • Thunk-like actions support — effects
  • Default state support (handy for unit-testing)
  • Redux dev tools logging support
  • TypeScript first! Typings for all methods and effects for free.
  • API based on hooks
  • Lightweight: 1.7 Kb minified, 819 b gzip

Example code (from example.tsx)

npm i react-localux constate
// example-store.ts
import { createUseStore } from "react-localux";

const pause = async (timeout: number): Promise<any> =>
  new Promise(resolve => setTimeout(resolve, timeout));

type State = {
  loading: boolean;
  data?: string;
  error?: boolean;
};

export const defaultState: State = {
  loading: false
};

export const useItemsStore = createUseStore(
  defaultState,
  {
    loading: () => () => ({
      loading: true
    }),
    loadSuccess: state => (data: string) => ({
      ...state,
      loading: false,
      data
    }),
    loadFailed: state => () => ({
      ...state,
      loading: false,
      error: true
    })
  },
  {
    loadItem: (_, methods) => async () => {
      methods.loading();
      // Pretend making API call which can fail
      await pause(1000);
      if (Math.random() > 0.5) {
        methods.loadSuccess("Hooray!😁");
      } else {
        methods.loadFailed();
      }
    }
  }
);

// exampleComponent.tsx
function ItemScreen() {
  const { Provider } = useItemsStore;
  return (
    <Provider initialState={defaultState}>
      <Item />
    </Provider>
  );
}

function Item() {
  const { state, effects } = useItemsStore();

  return (
    <div>
      <h1>Item Screen</h1>
      {state.loading && <p>Loading...</p>}
      {state.error && <p>Error loading 😕</p>}
      {state.data && <p>Data loaded 🎆: {state.data}</p>}
      <button onClick={effects.loadItem}>Load item</button>
    </div>
  );
}

/**
 * If you need just slice of state and do not want re-renders
 * on other state parts change, you can do such optimization with useMemo
 **/
function ItemOptimized() {
    const { state } = useItemsStore();
    return useMemo(() => (
        <div>
          <h1>Item</h1>
          {state.data
            ? <p>{`Data loaded 🎆: ${state.data}`</p>
            : <p>Loading...</p>
          }
        </div>
    ), [state.data]);
}

Also see tests.

Similar solutions

react-waterfall:

  • No TypeScript support and due to API design decision for actions it is not possible to make types
  • No async actions support
  • Not very performant code on store creation

Alveron is really good lib with good documentation:

  • No TypeScript support and due to API design decision for actions it is not possible to make types (looks like it is possible now with TS 3.7)
  • No redux dev tools logging

Use methods

  • No async actions support built-in
  • No redux dev tools logging
  • API based on ambiguous immer (immer adds 4.3 Kb gzip). Immer adds performance penality on every action call, up to 15x on browsers without Proxy and 2x-3x on others.

Constate

  • It is more "put any hook inside context" then complete store solution.
  • No default state support, which is usefull for unit-testing.

That's why this library has been born. 👭

Credits

Thanks to @viventus for helpfull discussions.

Thanks to Constate library for simple solution for placing hooks inside context.

About

React useReducer + Context API based store for smart components

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages