The useSyncExternalStore Hook

Here we'll learn how to use the useSyncExternalStore hook

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the React Data Fetching: Beyond the Basics course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to React Data Fetching: Beyond the Basics, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course React Data Fetching: Beyond the Basics
  • [00:00 - 00:11] We've added a lot of feature to our application, but there's one issue we've been ignoring. We currently have our own custom event listener to update the state whenever data is changed.

    [00:12 - 00:27] When it's listing for an event, it uses memory and processing resources from our machine. But if it's not needed, so if we switch to a component that's not using our use data hook, then this will still be listening for changes and taking up resources.

    [00:28 - 00:38] This of course is not good for performance. To fix this, we'd need to add a window.removeEventListener method to our use data hook when a component is unmounted.

    [00:39 - 00:48] And a common way to detect if a component is unmounted is to use the use effect hook. But there is another way.

    [00:49 - 00:57] And that is to use the use sync external store hook. This is a hook that exists in React version 18 and above.

    [00:58 - 01:22] And it allows us to subscribe to an external nonreact based store such as custom event listeners and it will automatically subscribe and unsubscribe when a component is unmounted. It also prevents unnecessary re-renders by keeping a snapshot of the store data and only update the component when the data in the snapshot changes.

    [01:23 - 01:30] As a free factor, our use data hook to make use of the use external sync hook. We can test this using our first Pokemon app.

    [01:31 - 01:38] Let's take a look at it in the browser. Before we go ahead and add the use sync external store hook, let's just fix one thing.

    [01:39 - 01:54] If we right click anywhere on this page, click on Inspect and change the tab to Network, then refresh the page, you'll see the call that's been made contains something called a abort signal. This is because we're running the get Pokemon's function directly in the main.

    [01:55 - 02:05] jsx file which has an argument of signal, but we actually have an argument of a mount that is conflicting with it. Let's take a look at the Pokemon.js file.

    [02:06 - 02:23] If we scroll down to line 16, you'll see here there's an argument called a mount with a default value of 150. But since we've changed the first argument to be a signal inside the code that runs this fetch call, then that value is being passed in here instead of the default 150.

    [02:24 - 02:31] So we need to go into our main.jsx file and run get Pokemon's as a nested function. Let's go ahead and do that.

    [02:32 - 02:47] On line 8, let's add parentheses before get Pokemon's add an equal and greater down sign and then at the end of get Pokemon's we're going to add parentheses. This means that when the function runs, it's no longer going to pass in the signal argument into the get Pokemon's function.

    [02:48 - 02:55] Let's test this in the browser. Okay, now you can see the Pokemon call is being made with a default value of 150.

    [02:56 - 03:00] Cool. It's important to do this for future data fetching function calls.

    [03:01 - 03:13] Okay, now let's go into our data loader file. The first thing we're going to do here is go to the first line in this file and we're going to import our use sync external store hook the same way we import you state.

    [03:14 - 03:25] So after you state on line one, I'm going to add a comma and write use sync and then we've got it coming as the first drop down. So I'm going to hit enter and that's important correctly.

    [03:26 - 03:41] Now in our use data function on line five, let's go all the way down to line nine and we're going to comment the slide out. Then beneath it, I'm going to create a new variable called data and this is going to equal our use sync external store hook and we're going to add parentheses.

    [03:42 - 03:50] This hook takes in two arguments. The first is the function which we'll call subscribe and this function subscrib es to the external store.

    [03:51 - 04:14] So this rerenders the component whenever there's a change and returned a cleanup function to unsubscribe and it takes a second argument which you're going to call get snapshot and this is the function that reads data from the external store. There's also an optional third argument called get server snapshot but we're not going to be using that for this course.

    [04:15 - 04:28] So we don't need to add it. In our case, the subscribe function would be the add event listener listening for the data fetched event and the key luck function would be the remove event listener which we haven't yet written.

    [04:29 - 04:40] Let's go ahead and create a subscribe function and we'll put this beneath our use data function. Next at the end of line 10, let's hit enter to make some space between the data variable and the add event listener.

    [04:41 - 04:58] Then we're going to go down to the end of line 15, hit enter a few times and write function, subscribe then add parentheses and this actually takes a mandatory argument of call back. The call back argument updates the external store and triggers actions like re- rendering the component.

    [04:59 - 05:15] It's a bit similar to the set data function that's being used on line 12. Let's add curly braces and hit enter and inside our subscribe function, we're going to write window dot add event listener, then add parentheses and in the first argument, we're going to put the name of the event.

    [05:16 - 05:25] So data fetch and in the second argument, we're going to put our call back. Now we're going to return what we want to run when the component amounts.

    [05:26 - 05:51] So at the end of line 18, we'll hit enter and write the turn and since we have to return a function, let's add parentheses and we're going to add an arrow function and here we'll add curly braces and we'll write window dot remove event and we'll select the random listener from the job down. Then add parentheses and here we're going to add a string of data fetched, which is the event we want removed and then we'll also add our call back.

    [05:52 - 05:55] Cool. And that is it for our subscribe function.

    [05:56 - 06:03] Next we'll work on the second argument for the hook, which is get a snapshot. Then this is a function that will read the data from the external store.

    [06:04 - 06:20] In our case, that's pretty much what this line of code is doing. Since we're just getting data from the cache base on the key, so we can put that into a function and we're going to press command T to copy and then here, which has get a snapshot, I'll turn that into a function and paste in the code we copied.

    [06:21 - 06:30] Let's get rid of the extra parentheses and that is it. Now that we have this, we don't need the code online nine so we can delete that and we also don't need the code online 11.

    [06:31 - 06:36] So we can also delete that and remove some white space in our code. Cool.

    [06:37 - 06:49] Now it's really difficult to demonstrate the differences between you state and you think external store since the app should work fine with our new changes. But there's one other benefit of making this change that we could test.

    [06:50 - 07:04] The use to external store hook will only rerender the component if data from the snapshot has changed and not if the data from the external store has changed. So our app will be working differently from the way it was before.

    [07:05 - 07:10] Let's demonstrate. If we press command Z or command Z a few times until we get to the previous state.

    [07:11 - 07:21] So that was when there was just the use state hook. So that's about here.

    [07:22 - 07:35] Then save this file and let's go to the home component. Now in this file after our use data hook online nine, let's add a console log and here we're going to add a string of re render.

    [07:36 - 07:43] So technically whenever the component renders or re renders, it should run this console log. Let's take a look at that in the browser.

    [07:44 - 07:58] So if we refresh the page, we'll see our re render log hits four times once for when the component first renders without any data and a second time for when the data is fetched. If we were in a strict mode, then the console log will only appear twice.

    [07:59 - 08:11] Now if you hover over any of the buttons, we'll see the re render log happens again. This is because the data in our cache has been updated, but the data on the home page hasn't been updated.

    [08:12 - 08:30] So there's really no need for the home page to re render. Now let's go back to our data loader file and press shift command Z or shift command Z a few times until we get back to using the use SIG external store hook.

    [08:31 - 08:36] So that should be about here. Now let's save the file and go back to the browser.

    [08:37 - 08:47] Now you'll notice we have four console logs again for the same reason. But if we hover over the view details button, you'll see the component doesn't re render.

    [08:48 - 08:58] Then this is because the new hook we've added only re renders the component if the data from the get snapshot function. So the second argument inside the hook is updated.

    [08:59 - 09:11] And in this case, since we're getting data with the key of Pokemon, that doesn 't update if we hover over the details button. But we can look at the network and we can see those calls are still being made.

    [09:12 - 09:23] This of course is much better for performance. What's also good for performance is that if we click on one of these Pokemon, the component here, so the details component only renders once.

    [09:24 - 09:31] This is because it's fetching data from the cache. And if we were using the use effect hook, it would render the component twice.

    [09:32 - 09:36] Once without data and again after the data has been fetched. Cool.

    [09:37 - 09:44] In the next video, we're going to fix another issue that we've been ignoring with our data load a file.