Welcome to react-hooks-global-state


How to create a global state?

import { createGlobalState } from 'react-global-state-hooks/createGlobalState';

// It works like useState, but it's global
const useCounter = createGlobalState(0);

How to share a global state?

const ComponentA = () => {
  const [count, setCount] = useCounter();

  return <button onClick={() => {
    setCount((prev) => prev + 1);
  }}>Increment</button>;
}; 
const ComponentB = () => {
  const [count, setCount] = useCounter();

  return <button onClick={() => {
    setCount((prev) => prev - 1);
  }}>Decrement</button>;
}; 

Select only what you need











How does it look like?

const ContactList = () => {
  const [filter, setFilter] = useState('');

  // You can use selectors to get only what you need
  const [filteredContacts] = useContacts(
    (contacts) => {
      return [...contacts.values()].filter((contact) => {
        const filter = filter.toLowerCase();
        const name = contact.name.toLowerCase();

        return name.includes(filter);
      });
    },
    // You can pass an array of dependencies
    [filter]
  );

  return filteredContacts.map(...
};
  • The selector will recompute if the state changes
  • The selector will also recompute if the dependencies change
const useContacts = createGlobalState(getContactsMock(), {
  ...
},
{
  isEqualRoot: (a, b) => a.size === b.size,
  dependencies: [filter],
});

Create reusable selected states

const useContactsArray = useContacts.createSelectorHook((contacts) => {
  return [...contacts.values()];
});

// You can create a selector from another selector
const useContactsWithJ = useContactsArray.createSelectorHook((contacts) => {
  return contacts.filter((contact) => {
    const name = contact.name.toLowerCase();
    return name.startsWith('j');
  });
});

Each selector listens only to the state of the hook it was created from.

Contacts whose names start with 'J'




Retrieve hook controls outside of components

const useProgress = createGlobalState(0);

// You can retrieve the state controls outside of the components or hooks
const [progressRetriever, progressMutator] = useProgress.stateControls();

const ComponentA = () => {
  const [isPaused, setIsPaused] = useState(true);

  useEffect(() => {
    if (isPaused) return;

    const interval = setInterval(() => {
      progressMutator((prev) => (prev + 1) % 101);
    }, 1000);

    return () => clearInterval(interval);
  }, [isPaused]);

...
};

You can listen to the state changes outside hooks

Subscribe to state changes without a hooks

const ref = useRef<HTMLProgressElement>(null);

useEffect(() => {
  const progressElement = ref.current!;

  // returns a function to unsubscribe
  return progressRetriever((progress) => {
    progressElement.value = progress;
  });
}, []);

return <progress ref={ref} value={0} max={100} />;
console.log(progressRetriever()); // 0
// synchronously returns the current value of the state

Hooks who depend other hooks

const useContacts = createGlobalState(getContactsMock());

// the retriever do both, get the state or subscribe to changes
const [contactsRetriever, setContacts] = useContacts.stateControls();

const useContactsArray = useContacts.createSelectorHook((contacts) => {
  return [...contacts.values()];
});
// The second argument is a configuration object, we'll analyze it from now on
const useSelectedContactId = createGlobalState(null as string | null, {

// Callbacks are the lifecycle events of the state
  callbacks: {
    onInit: ({ setState, getState }) => {
      // If the contacts map stops containing the selected one, reset
      contactsRetriever((contacts) => {
        if (contacts.has(getState()!)) return;

        setState(null);
      });
    },
  },
});

Persist state in localStorage

If you are using react-global-state-hooks, which is the version of the library focused on browser usage, you can persist the state in the localStorage.

const useCounter = createGlobalState(0, {
  localStorage: {
    key: '_counter_from_react_hooks_global_states_example',
  },
});

Custom actions

You can also restrict the manipulation of the state to and specific set of actions.

const useCounterWithActions = createGlobalState(0, {
  actions: {
    // the first function defines the action name and arguments
    increment: (value = 1) => {
      // the return of the second function is the action itself
      // and defines the return type of the action
      return ({ setState, getState }): number => {
        setState((prev) => prev + value || 1);

        return getState();
      };
    },
    decrement: (value = 1) => {
      return ({ setState }) => {
        setState((prev) => prev - value || 1);

        return getState();
      };
    },
  },
});
// Now instead of a setter, you have an object with the actions
// the actions object and keys are memoized and won't change
const [count, { increment, decrement }] = useCounterWithActions();

// Increase the count by himself each time: 1, 2, 4, 8, 16, ...
<Button onClick={() => increment(count)}>Increment</Button>
<Button onClick={() => decrement(count)}>Decrement</Button>