I built the simplest react state management library (usecat)

PengPeng
2 min read

There are so many state management libs for react, but they are too complex / not easy to use for me. I only need these:

  • Create state for something;

  • Update the state out of component;

  • Get the state out of component;

  • A hook to read state within component;

So I created the lib myself, it's dead simple but very flexible, I have a cat, so I call the lib usecat, github link here. It's so simple that I can list the whole source code here:

import { useEffect, useReducer } from 'react';

const cats = [];

export function createCat(initialValue) {
  let value = initialValue;
  const listeners = new Set();

  const get = () => value;

  const set = (newValue) => {
    if (newValue !== value) {
      const oldValue = value;
      value = newValue;
      listeners.forEach((listener) => listener(oldValue, newValue));
    }
  };

  const reset = () => set(initialValue);

  const subscribe = (listener) => {
    listeners.add(listener);
    return () => listeners.delete(listener);
  };

  const cat = { get, set, reset, subscribe };
  cats.push(cat);

  return cat;
}

export function useCat(cat, selector = (value) => value) {
  const [, forceUpdate] = useReducer((x) => x + 1, 0);

  useEffect(() => {
    const handler = (oldValue, newValue) => {
      const oldSlice = selector(oldValue);
      const newSlice = selector(newValue);
      if (oldSlice !== newSlice) {
        forceUpdate();
      }
    };
    const unsubscribe = cat.subscribe(handler);
    return () => unsubscribe();
  }, [cat]);

  return selector(cat.get());
}

Usage is also simple

Create some cats:

import { createCat } from 'usecat';

const isLoadingTodosCat = createCat(false);
const todosCat = createCat([]);

Get and set values out of component:

import { isLoadingTodosCat, todosCat } from './cats';

async function fetchTodos() {
  const currentTodos = todosCat.get();
  if (currentTodos?.length) {
    return;
  }

  isLoadingTodosCat.set(true);

  try {
    const response = await fetch('your-fancy-api');
    const todos = await response.json();
    todosCat.set(todos);
  } catch (e) {
    // handle error yourself ;)
  }

  isLoadingTodosCat.set(false);
}

Use hook to get value updates:

import { useCat } from 'usecat';
import { isLoadingTodosCat, todosCat } from './cats'
import { fetchTodos } from './network';

function MyComponent() {
  const isLoading = useCat(isLoadingTodosCat);
  const todos = useCat(todosCat);

  useEffect(() => {
    fetchTodos();
  }, [])

  return (
     <>
        {isLoading && <div>loading ...</div>}
        {todos.map(todo => (
          <div key={todo.id}>{todo.title}</div>
        ))}
      </>
  )
}

I am using this simple thing to build all my projects, like notenote.cc, check its source code, it's open.

0
Subscribe to my newsletter

Read articles from Peng directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Peng
Peng