import { createGlobalState } from 'react-global-state-hooks/createGlobalState';
// It works like useState, but it's global
const useCounter = createGlobalState(0);
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>;
};
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(...
};
const useContacts = createGlobalState(getContactsMock(), {
...
},
{
isEqualRoot: (a, b) => a.size === b.size,
dependencies: [filter],
});
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.
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
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
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);
});
},
},
});
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',
},
});
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>