Hi Guys,
SO here is what we are going to learn to code today:
const useLoggedInUser = () => {
const [loggedInUser, setLoggedInUser] = useGlobalState("loggedInUser", {})
return [loggedInUser, setLoggedInUser]
}
We can export this function and use the hook to access the loggedInUser state in any component, without needing to prop drill or use providers. While there's nothing wrong with prop drilling or using providers, this is another valid approach in React for managing global state without relying on third-party libraries like Zustand.
So let's go:
To create useGlobalState we are going to rely on the useSyncExternalStore hook in React.
Here is how React defines the usage of the useSyncExternalStore hook:
useSyncExternalStore is a React Hook that lets you subscribe to an external store.
And
useSyncExternalStore returns the snapshot of the data in the store. You need to pass two functions as arguments:
The subscribe function should subscribe to the store and return a function that unsubscribes.
The getSnapshot function should read a snapshot of the data from the store.
So basically to work with this hook we need a store, a subscribe function and a getSnapShot function.
Let's first create a simple store:
const store = new Map();
This store keeps track of the states and the listeners which we will attach to it via useSyncExternalStore.
Let's now create a subscribe function:
const subscribeToStore = (key, listener) => {
const entry = store.get(key);
if (!entry) return () => { };
entry.listeners.add(listener);
return () => entry.listeners.delete(listener);
}
This function allows components to subscribe to changes in specific pieces of global state and get notified when that state changes.
The second param in the above function will be directly passed by React when specific pieces of global state changes, all thanks to useSyncExternalStore magic.
Now let’s implement the getSnapShot function:
const getSnapshotFromStore = (key) => {
const entry = store.get(key)
return entry?.value
}
Now let’s put all these pieces together to create the magic hook useGlobalState:
const useGlobalState = (key, initialValue) => {
initializeStateInStore(key, initialValue);
const globalState = useSyncExternalStore(
(cb) => subscribeToStore(key, cb),
() => getSnapshotFromStore(key)
);
return [globalState, (newValue) => setStateInStore(key, newValue)]
}
Notice that there are two function in the useGlobalState that we have not yet seen in this blog: initializeStateInStore, setStateInStore
initializeStateInStore is used to add an entry into the map as and when we initialize a new state and it is implemented like so:
const initializeStateInStore = (key, initial) => {
if (!store.has(key)) {
store.set(key, {
value: initial,
listeners: new Set(),
})
}
}
setStateInStore is a function that changes values in the global store and notifies all subscribers and it is implemented like so:
const setStateInStore = (key, newValue) => {
const entry = store.get(key)
if (entry) {
entry.value = newValue;
entry.listeners.forEach((fn) => fn());
}
}
So there we have it the useGlobalState function.
So that’s it for today, guys.
Here is the link to my github repo where I have implemented this function:
useGlobalState