Understanding useSyncExternalStore Through Some Examples
Aneesh

Aneesh @varadan13

Joined:
Aug 17, 2021

Understanding useSyncExternalStore Through Some Examples

Publish Date: Jun 21
0 0

useSyncExternalStore is a React Hook that lets you subscribe to an external store.

First, let’s create a useOnlineStatus hook which automatically tracks whether the user’s device is online or offline.

useOnlineStatus
To use useSyncExternalStore and create useOnlineStatus, we need the following:

  • A store
  • A subscribe function that invokes a callback provided by React when the state inside the store changes
  • A function that returns a snapshot of the current state from the store

Let’s create the store:


let currentNetworkStatus = {
    online: navigator.onLine,
};

Enter fullscreen mode Exit fullscreen mode

Let’s create the subscribe function:

The requirement for this function is that it accepts a callback, listens for state changes within the store, and returns a function that, when invoked, unsubscribes from listening to state changes.

(callback) => {
        const update = () => {
            const newStatus = { online: navigator.onLine };
            if (newStatus.online !== currentNetworkStatus.online) {
                currentNetworkStatus = newStatus;
                callback();
            }
        };
        window.addEventListener("online", update);
        window.addEventListener("offline", update);
        return () => {
            window.removeEventListener("online", update);
            window.removeEventListener("offline", update);
        };
    }
Enter fullscreen mode Exit fullscreen mode

Let’s create the snapshot function:

This function will just return the latest state from the store.

() => currentNetworkStatus.online
Enter fullscreen mode Exit fullscreen mode

Let’s pack the subscribe fn and the snapshot fn into an object:

const network = {
    getSnapshot: () => currentNetworkStatus,
    subscribe: (callback) => {
        const update = () => {
            const newStatus = { online: navigator.onLine };
            if (newStatus.online !== currentNetworkStatus.online) {
                currentNetworkStatus = newStatus;
                callback();
            }
        };
        window.addEventListener("online", update);
        window.addEventListener("offline", update);
        return () => {
            window.removeEventListener("online", update);
            window.removeEventListener("offline", update);
        };
    },
};
Enter fullscreen mode Exit fullscreen mode

Now finally the useOnlineStatus:

const useOnlineStatus = () => {

    const globalState = useSyncExternalStore(
        (cb) => network.subscribe(cb),
        () => network.getSnapshot()
    );

    return globalState
}
Enter fullscreen mode Exit fullscreen mode

Next, let’s construct a usePageFocus hook that returns true if the page is focused and false if it is not.

usePageFocus
Following what we did for useOnlineStatus.

Let’s first create a store:


let focusState = {
  focused: document.hasFocus(),
};
Enter fullscreen mode Exit fullscreen mode

Let’s create the subscribe fn:

(callback) => {
    const update = () => {
      const newFocus = { focused: document.hasFocus() };
      if (newFocus.focused !== focusState.focused) {
        focusState = newFocus;
        callback();
      }
    };

    window.addEventListener("focus", update);
    window.addEventListener("blur", update);

    return () => {
      window.removeEventListener("focus", update);
      window.removeEventListener("blur", update);
    };
  }
Enter fullscreen mode Exit fullscreen mode

Let’s create the snapshot fn:

() => focusState.focused
Enter fullscreen mode Exit fullscreen mode

Now, bringing it all together, first by packing the snapshot fn and subscribe fn into an object and then creating the hook.

const pageFocus = {
  getSnapshot: () => focusState,
  subscribe: (callback) => {
    const update = () => {
      const newFocus = { focused: document.hasFocus() };
      if (newFocus.focused !== focusState.focused) {
        focusState = newFocus;
        callback();
      }
    };

    window.addEventListener("focus", update);
    window.addEventListener("blur", update);

    return () => {
      window.removeEventListener("focus", update);
      window.removeEventListener("blur", update);
    };
  },
};
Enter fullscreen mode Exit fullscreen mode

Finally, the usePageFocus hook:

const usePageFocus = () => {

    const globalState = useSyncExternalStore(
        (cb) => pageFocus.subscribe(cb),
        () => pageFocus.getSnapshot()
    );

    return globalState
}

Enter fullscreen mode Exit fullscreen mode

Now, moving onto the mother of all hooks in this blog, the useGlobalState hook

useGlobalState hook

This hook works similar to the zustand api. For example:

we can create a hook to work with the loggedInUser state in a single file and then import it into any component without using providers or having us prop drill the state and state setter function into components.

Example implementation:


const useLoggedInUser = () => {
    const [loggedInUser, setLoggedInUser] = useGlobalState("loggedInUser", {})

    return [loggedInUser, setLoggedInUser]
}

Enter fullscreen mode Exit fullscreen mode

So let’s now build the useGlobalState.

under the hood it is all useSyncExternalStore

Let’s continue the way we built usePageFocus and useOnlineStatus.

First the store:

We can implement the store like a simple Map.

const store = new Map();
Enter fullscreen mode Exit fullscreen mode

Let’s implement the subscribe function:

const subscribeToStore = (key, listener) => {
    const entry = store.get(key);
    if (!entry) return () => { };
    entry.listeners.add(listener);
    return () => entry.listeners.delete(listener);
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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)]
}
Enter fullscreen mode Exit fullscreen mode

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(),
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

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());
    }
}
Enter fullscreen mode Exit fullscreen mode

So there we have it the useGlobalState function.

So that’s it for today, guys. We’ve looked at some example usages of useSyncExternalStore in React.

Comments 0 total

    Add comment