You Need To Start Using This Underrated React Hook
Chapters4
The video introduces the useSync external store hook as a cleaner alternative to useEffect for syncing data stored outside React with React state. It teases a free course and promises practical examples to simplify state management.
If you’re still wrestling with useEffect, start using React's useSyncExternalStore to cleanly sync outside-the-React data with your components.
Summary
Kyle from Web Dev Simplified introduces useSyncExternalStore as a powerful alternative to overusing useEffect for syncing external state into React. He begins by showing a simple online/offline status example where navigator.online is read outside React and synchronized without manually juggling effects. The video then demonstrates how useSyncExternalStore accepts a subscribe function and a snapshot getter, letting React re-render when external data changes without exposing a setter. Kyle emphasizes the readability and reliability gains, especially when external data could be mutated from elsewhere in the app. He then walks through a second, more impactful example: a dialogue element modal whose open/close state is driven by the browser's native behavior, yet kept in sync with React using the external store hook. The explanation highlights server-side rendering considerations, noting the third parameter (server snapshot) helps with SSR defaults. Finally, Kyle demonstrates building a small global store for to-dos using useSyncExternalStore, showing how to implement subscribe, getSnapshot, and a simple listener system to enable truly global state without context. Throughout, he contrasts the simpler, more robust approach with traditional useEffect patterns that require cleanup and fragile dependency management. He also plugs his free React Hook Simplified course for deeper learning and 30 custom hooks.
Key Takeaways
- useSyncExternalStore replaces messy useEffect-based polling by subscribing to external state changes and providing a snapshot for React to render.
- the subscribe function receives a callback; calling that callback signals React to fetch the latest value via the snapshot function (e.g., navigator.online).
- the dialogue modal example shows how to reflect browser-driven state (open/close) in React without manual state syncing, including escape-key handling.
- useSyncExternalStore supports a server snapshot parameter, enabling sensible defaults during server rendering when DOM access isn't available.
- the three-parameter form (subscribe, getSnapshot, serverSnapshot) can be leveraged to create a global store (like a Zoo Stand/Redux-like store) without context.
- the to-do store example demonstrates building a minimal, global store with subscribable listeners, getSnapshot, and mutating data while keeping components in sync.
- replacing useEffect with useSyncExternalStore often reduces boilerplate, avoids stale state issues, and makes dataflow easier to reason about across a project.
Who Is This For?
Frontend developers who rely on browser- or external-state data in React apps and want a cleaner, more maintainable pattern than heavy useEffect boilerplate. Essential for those exploring global stores without context or wanting SSR-friendly state synchronization.
Notable Quotes
"One of the biggest mistakes you can make in React is overusing the use effect hook."
—Kyle motivates the shift from useEffect overuse to useSyncExternalStore.
"anytime you have data stored outside of React and you want to synchronize it with variables inside of React, that's exactly what this custom hook is for."
—Definition of useSyncExternalStore purpose.
"we can replace all of this with just a simple isOpen, and we're going to use that sync external hook."
—Applying the hook to the dialogue/modal example.
"the third parameter is for server rendering; this is like a default state that the server will get back instead."
—Explains serverSnapshot usage.
"this is going to be the function we call to get a snapshot of all of our different data."
—Describes the getSnapshot function in the to-do store.
Questions This Video Answers
- How does useSyncExternalStore differ from useEffect for syncing external data in React?
- Can I implement a global store with useSyncExternalStore without React Context?
- What are server-side rendering considerations when using useSyncExternalStore?
- How would I migrate an existing component that uses useEffect to listen to browser events to useSyncExternalStore?
- What are practical examples of useSyncExternalStore beyond simple online/offline status?
useSyncExternalStoreReactuseEffectdialogue elementserver-side renderingglobal storecustom hooksWeb Dev SimplifiedZoo StandRedux-like patterns
Full Transcript
One of the biggest mistakes you can make in React is overusing the use effect hook. Which is why in this video I'm going to show you the use sync external store hook, which you've probably never even heard of, but is an incredibly useful hook for managing state in your applications without having to rely on pesky and complicated use effect. I'm not only going to show you what this hook does and how it works, but I'm also going to show you examples of why you want to use this in every single project, cuz I guarantee you it's going to drastically clean up your code.
Also, if you want to learn how every single hook in React works, I have a completely free React Hook simplified course that goes through every single hook and it also has 30 different custom hooks that I'm going to show you how to create. It's entirely free and it's going to be linked down in the description below. Welcome back to WebDev Simplified. My name is Kyle and my job is to simplify the web for you. And here is a really simple example of a perfect use case for use sync external store. As you can see here, we're trying to track the online status of a user.
So if I actually change to be offline, for example, I lose connection, we'll toggle that to offline. You can see it changes my text over here to offline. And while I'm online, you can see my text says online. So it's properly just showing the toggle between the two. And in order to do this inside of React is quite complicated because this navigator.online status is handled entirely by the browser and we essentially just want to listen for when that changes. So we're hooking up some event listeners inside of a use effect. We're then using this navigator.online to be able to toggle whether online or offline.
Essentially, we have some piece of state, the online status that is being stored outside of React in our browser, and we want to store it inside of React so we can use it with the rest of our code, such as changing what our text or the other things in our code is doing. So, normally with use effect, you would essentially wrap a callback inside of a use effect that listens for the changes of this variable and then you update whatever your state is and you need to make sure that you run cleanups and then that's how you're going to be handling it.
The main problem with doing it this way is I could set my online status elsewhere. For example, I could just come in here and call set online status to true somewhere else in my code. That would break things because now I'm updating that state variable where I shouldn't be. And also, it's just a lot of code to write and use effects are kind of a pain to work with. So, instead, we're going to be swapping this over to use sync external store because anytime you have data stored outside of React and you want to synchronize it with variables inside of React, that's exactly what this custom hook is for.
So in order to use this hook, we're just going to replace this with use sync external store. This use sync external store hook takes in two or three parameters, but it's mostly going to be two. The first one is a subscribe function. All this does is it subscribes to some type of callback that's going to be called every single time we update this variable outside of React. So for example, whenever our status changes from online to offline or from offline to online, we want to subscribe to those callbacks and make sure we listen to the data that's coming in.
The next thing we do is actually get the data, the snapshot of the data in our case. So this is a function that is just going to return to us what the current online status is. So this is like I said a function. And to get the current online status, we can just call navigator.online. That's giving us the most up-to-date value for our online status. And the subscribe function, like I said, it's going to be a function we call to listen for when those changes happen. So let's create a brand new function. We'll just call it subscribe.
Just like that. And the subscribe function is actually going to take in a call back. Now this call back is just a function that doesn't return anything. And essentially every single time that our status changes, we want to call this call back. And behind the scenes, React is going to call this code right here, get whatever that new value is, and set it to our is online variable. You can see by using this hook as we are, we're not getting any setter or anything like that to worry about. This entire use effect is actually going to be going away because it's all going to be handled in this subscribe function right here.
So, the way that this subscribe function works is you want to listen for the events that you're going to be using. So, it's very similar to this entire use effect right here. We'll actually just copy it over because it's almost identical. We'll paste in that use effect. There we go. And we'll get rid of everything that we have up here. And inside this function, essentially all we need to do is we need to set up our event listeners and our callbacks and everything just like we would inside of a use effect. But instead of directly setting any state variable here, what we can do instead is we can just take our callback function, which is what we have right here, and we can put that in the place where we'd be updating our state.
So essentially, anytime that our state changes, for example, our online status changes from online to offline or vice versa. Anytime that changes and it's not being stored inside of React, I call this callback function. And all that callback does is it tells React to call the function we put up here to get the new online status and then return that right here. So, anything that triggers the state to change, just make sure you call that call back. And we can actually clean this code up quite a bit by removing all of those right there. And then we can just make it so that down here, we're going to be removing those event listeners.
So, we'll say remove event listener for both of those. Just like that. And now we've cleaned everything up in our code. We have this really simple subscribe. We don't need to worry about any dependency arrays or anything like that because it's all handled for us by this use sync external store. And if I go ahead and I actually test this out, it should be working exactly the same as what we had before. So, let's go over to that network tab and I'll change it to offline. You can see it changes offline, back to online, and it changes back to online.
So, everything is working exactly the same as before, but we able to clean up that entire use effect, which is really nice. Now, this example really doesn't save you a ton of code. It's just an alternate way of doing the same thing. So, instead, I want to show you another example where it actually saves you a ton of code, and that is with a dialogue element or anything else similarly related to this. Again, it's something interfacing directly with the built-in features of the browser. So, here we just have a simple modal, but I'm using the dialogue element from HTML because it's fully accessible from the get-go and it's very easy to work with and has all those great features built in.
If you want to learn more about the dialogue element, I have a full video covering it. I'll link in the cards and description for you. But essentially, when I click on this open modal button, it opens and you can see my state right here changes to opened. When I click close, it closes. And you can see my state right here changed to closed. You can see we're tracking the state inside of a state variable. We have a ref for our modal and that's because the way the dialogue element works to open it up all we do is we call show modal as a function that opens our modal up and in order to close it we call close as a function and it handles all the other accessibility stuff behind the scenes for us which is great.
Now to sync up our state I'm just setting the open state on true and false based on when I click my open and close button and that's how I'm syncing my state between these. And as you can see it looks like it's working fine. The problem though is that it is actually not working fine. By default, to make sure that this is accessible, this modal will close when I click the escape key on my keyboard that's built into the browser. When I click escape, you can see the modal closes, but this opened states opened because the only way I'm changing that state is manually trying to sync it between the two, which is quite difficult to do because you're going to run into places where you forget to sync it in one place.
So now it's out of sync, which is obviously not great. In order to fix this, we could add up some event listeners and so on, but it's much easier to use that use sync external store hook. So, we can replace all of this with just a simple is open. And we're going to use that sync external hook. Right here, we have a subscribe function. And then we have our actual callback to get the open/clo state. Now, we're going to need our modal reference inside of here. So, we can just say modal ref.curren. And we can just get the open status of that.
There we go. Just like that. Let's make sure that we return this. And now we have that entire section set up. Let's just put a little question mark on there. And if for some reason that this thing does not exist, we'll say that it's currently not open. So we'll set that to false. Next, we have our subscribe function that we need to set up. So let's go ahead and do that. We'll create that subscribe function takes in a call back. And inside the subscribe function, we essentially want to listen to when this is opened and when it is closed.
So we can really easily do that with our modal ref. We can add an event listener to this. That event listener is just called toggle like this. That's how we listen to that event. And we want to pass it in the callback. Then we want to make sure that we clean this up. So we'll just come in here and we'll just say that we want to essentially remove the event listener. Just like that. There we go. Now we can get rid of all this set is open. We no longer need that. And if we just give this a quick save and a refresh, we should hopefully see everything show up.
And we're actually getting an error because our modal ref needs to be defined before we try to access it. There we go. Now, if we give it a save, we can see we can open our modal, we can close our modal, everything's synced up. But the really nice thing is when I click escape, for example, it still modifies this state because we're syncing it with the subscriber using this use sync external store, which is making sure everything is properly synced up. Now, I did mention at the beginning of this video that this actually has three parameters that it takes.
The first is the subscribe function. The second is that snapshot. The third is a snapshot as well, but this is only for the server. So if you're server rendering your components, this is like a default state that the server will get back instead. So for example, I can say that by default, this is going to be set to false. So I can make sure it's a function like that. And when this gets called from the server, it'll be set to false because it doesn't have access to the DOM. Then when it renders on the client, it'll use this snapshot every single time.
So if we give that a save, you can see everything's still working exactly the same. I'm not even server rendering anything. And in that case, this is ignored if you're not server rendering. But if you are, this is called when you're on the server specifically, which is another reason this is nicer than use effect because use effect never gets run on the server at all. So it can be kind of difficult to sync things up. This just makes it dead simple to do. Now I've shown how easy it is to use this to sync up existing state to React.
And this can be used for many things like caching, websockets, and so much more. But now I want to show you how you can use it to create your own custom stores similar to something like Zoo Stand, Redux, or Jotai. So we're going to come in here with a very basic to-do based example. So, you can see here I just have some to-dos inside of here, they're just a string array. I then have an add to-do and remove to-do function. I have an onsubmit that's in my form. So, when I type something into this field, and I click enter, it'll add that as a to-do because it's calling add to-do and putting that in the list.
Then, anytime I click on one of my to-dos, it's going to remove it by calling remove to-do. So, you can see if I click a to-do, it removes it. And if I add a bunch of to-dos with different names, when I click on one of them, it'll be removed from the list. Very basic, simple stuff. But right now, all of my state is local to this component. I could move it out to context to make it global or at least more global, but then I have to deal with wrapping my app in context and that has tons of different performance considerations to worry about.
So instead, I just want to create a global store to store all my information, share it throughout my application, and not worry about any performance related concerns or any context at all. This is super simple to do with this use sync external store. Let's just create a brand new file. We're going to call it to-do store.ts. And inside this to-do store, we essentially need to take all the code that we have inside of here and we need to move it over into this to-do store minus this onsubmit. We essentially need to have a way to add a to-do.
We have have a way to remove a to-do. And then we need to essentially have a way to have all of our to-dos in a list as well. So the first thing is we can just store all of our to-dos. They're going to be a string array. And that string array by default is just going to be an empty array. That's kind of this first part taken care of. The next part is we need our add to-do and remove to-do function. I'm going to put those inside of a variable. So we'll say export const todo store and we'll give that all capitals actually to-do store.
And what we'll do is we'll make that an object. And in this object we'll have an add to-do which takes in all of these different properties. And this is going to add a to-do to our list. So we can say to-dos.push our name. Since we're not worrying about state or anything like that. We don't have to worry about immutability. We can just mutate this directly and it's going to work just fine. I also need to have a way to remove a to-do. So, we can come in here and do the exact same thing with remove to-do.
And to remove a to-do, again, I can just take my to-dos. Don't have to worry about mutability. I can just directly slice off that one element using splice. So, that's taking care of my remove and add to-dos. So, at least I have that synced up. The next thing, since we know that we're dealing with a use sync external store, I need a way to subscribe to this store and I need a way to get a snapshot. Snapshot is super easy. I can just say get to-dos. This can be a function and it can just return my to-dos as is.
And there we go. That is going to be the function we call to get a snapshot of all of our different data. The next thing I need to do is to have a way to subscribe to this function. So to subscribe to this, I can just create a function called subscribe. We know that this is going to take in a callback, which is essentially a function that returns nothing. And all I want to do is I want to set this up as a listener inside of here. So I can say I want to get a list of all my listeners.
This I'm going to have set to be a set. And this set is just going to be those objects. So we're going to come in here with those functions that don't return anything. And by default, we'll set this to a new set. So it's completely empty. And here I can just say listeners do add. I want to add in my callback. It is now listening for that. And to unsubscribe, we can make sure we do that inside of return. We can just do listeners.delete. And we'll put our call back directly inside of here. So now I'm listening and I'm removing that listener.
All I need to do is anytime I update my state, I just need to call that listener. So I can say listeners for each and I can just call those listeners. I can do that in both places. Anytime that I update my state, I just make sure that I call out to all the things that are listening to it. And now I have a fully functional store set up. You can see it's not very much code to be able to do this. And now inside of here, I can essentially replace my to-dos with that sync external store.
I know that I have my to-do store.subscribe subscribe and my to-do store. Totodos, that's my snapshot. Those are the two little listeners I need to set up. So, I'm essentially saying here's my listening code for my subscribe and here's my snapshot code right there. And now I just need to make sure I set everything up like I have before. So, I can get rid of all these functions. And here I can just say to-do store, whoops, store add to-do. And that's going to add that to-do to my global store. Same thing down here. I can do remove to-do.
And that's going to remove the to-do from my global store. And now everything should be stored globally in my application. So I can use this anywhere and not worry about context or anything like that. Let's test this to make sure it works though. And unfortunately it looks like when I try to add a to-do, it's not quite working. And that reason for that is because in my to-do store, you can see that my to-dos array is never actually being recreated because of how React works. Essentially, it needs to be a new reference. So to make this work, I do kind of actually need to worry about using the correct way of not making this mutable.
So, I can say inside of here, take my existing to-dos, and I can add the name onto the end of the list, just like that. And I can make sure that this is a let variable up here. And I can do the exact same thing here, but just change it to two spliced. And set my to-dos equal to that new variable. That should clean up all those errors that I had. So, now if we type something in, hit enter. You can see that gets added. I can enter a new one. Enter a new one. All this is going through this global store.
I can remove them. That's working just fine. And the really great thing is if I change this in one place, it automatically updates everywhere else in my code, but only in the places that it needs without having to do any cascading or other things that you would normally get by using context. Now, if you enjoyed this video, you're definitely going to love my full React hook simplified course. Again, it's entirely free, covers every single hook you need to know about, and it includes 30 full custom hook implementations so you can really fine-tune and master hooks inside of React.
If you want to check this out, it'll be linked down in the description below. And again, it's entirely free. With that said, thank you very much for watching and have a good
More from Web Dev Simplified
Get daily recaps from
Web Dev Simplified
AI-powered summaries delivered to your inbox. Save hours every week while staying fully informed.









