# Goodbye, useState (JavaScriptMN)

Canonical: https://davidk.dev/talks/goodbye-usestate-javascriptmn/
Video: https://www.youtube.com/watch?v=UYdKqv0W0n4
Speaker: David Khourshid (@DavidKPiano), creator of XState
Type: Conference talk · Event: JavaScriptMN meetup (June 2025) · Year: 2025 · Duration: 34:30 · Views: 136 (as of 2026-10-19)
Topics: React hooks, State management, React, XState

## Summary
We are so hyped to have David Khourshid (known as David K around the internet) stop by the JSMN stage on June 4, 2025.

## Key takeaways (quoted from the transcript)
- We have our string enums over here and instead of having just the status being set we could actually combine that into an object. (12:00)
- Instead of having to render your app and then be like, okay, let's click this button and then let's read from this text and see if the donut is actually added to these list of items. (16:00)
- If you read the React docs of course they will say that hey you should probably just use ustate and useReducer instead. (20:00)
- You could also consider them sync engines but the idea is that right now the way that we fetch data is very manual. (26:00)
- Overall start with useState refactor to better patterns and focus on maintenance because simple is better than easy. (28:00)

## Chapters
- 0:00 Introduction
- 2:00 By the way, this says Miami because this these are slides…
- 4:00 You can use suspense too if you want to handle loading…
- 14:00 You call useReducer on the donut reducer and then you could…
- 17:00 Take a look at live store
- 31:00 Lightning talks in the sense of just like short demos
- 33:00 We'll do a couple more shout outs and thank yous of…

## Transcript
[0:00] Awesome. Well, I am super excited to welcome our next presenter to the stage, David Cororsche. You may know him from the internet as @DavidKPiano. He is here to share a really special presentation with us. Founder of Stately and creator of XState. You're going to love this. We're saying goodbye to you state tonight. David, the floor is yours. All right. Hello everyone. Can you all hear me? Son. All right. Awesome. So I'm going to apologize to you in advance because this is going to be significantly less entertaining than the last talk.

[0:00] But we're going to be saying goodbye to you state, which hopefully is controversial enough for you to be like, hm, I wonder what he's going to go on about. That is hold on. Where are my slides? There we go. All right. Who am I? He just said doesn't matter anyway because we have a lot to talk about. I'm David Kiana pretty much everywhere online. I created XState, founder of Staley. But what we're going to be talking about today not exactly useEffects but I did give a lot of talks on the past called goodbye useEffect and when I first gave that talk I was just trashing useEffects saying how it gives a lot of anti-atterns and people got angry at me they were like how dare you know just curse our blessed hook from the React team or whatever but then the React team actually posted said like you might not need useEffects like an entire page in their documentation which really should be required reading.

[1:00] So I really encourage you to read that and pretty much all of the all of the React documentation if how many of you are actually React developers.

[2:00] I don't want to talk to a room full of you or Angular. Okay, cool. We're good here. All right. So we need to talk about Ustate and I know what some of you are going to say. It's like you know Ustate is really popular. I'm not really ready to give it up. It's in the most recent React survey, it is the most popular thing by far, but you know there's also Axios there.

[2:00] I mean fetch is right there. I'm not sure like you can't trust surveys. That's the bottom line there. But here's the main problem. We have you know we start React apps. We have good intentions. Maybe start with the use date. Then we have an effect. So we have to call that useEffect which triggers another use date which might trigger another useEffect which triggers the same useState updates props in a completely different component which triggers a useEffect there which you're saving something to useState and then you know that's triggering a useEffect back in that component.

[2:00] You see how things just get really messy. And you might be thinking like, hey, there's nothing wrong with my used dates. Like, it's good code. Maybe you vibe coded it. Maybe some powerful AI generated it. But you have to remember that LLMs weren't trained on good code. They were trained on your code. So, yeah. I mean, yeah, average code. I'm not saying your code's terrible, but there's a few things we need to talk about with Ustate.

[3:00] First of all, renders like it's you know, there's a lot of rerendering that happens. By the way, this says Miami because this these are slides from Miami and I'm doing this because Mark missed my Miami talk even though he was at React Miami. So that's why yes, I see this. Yeah, it's okay. But yeah so we that there's a lot of patterns that we typically useState for where we say if we have state in a component then it should go and useState right but sometimes those things don't actually belong in state for example if we're trying to track the number of clicks in a component then you know we this is going to cause a rerender every single time you know we click the button but it doesn't really need to and so one rule of thumb is if it's, you know, not really going to cause a rerender, you don't actually need to have that in useState.

[4:00] So, useRef is really useful for that.

[4:00] A lot of these things I actually talked about in great depth in my front-end masters workshop yesterday. So, you're pretty much all getting a preview of that right now. Another thing that I see in apps is it's really frustrating. You might have a table, you're sorting things, you're filtering things, or you might have some sort of view where it's like, I don't want this to go away when I refresh the page, but that's exactly what's going to happen.

[4:00] So, thankfully, there's patterns in frameworks such as Nex.js where you could actually use the URL as a source of truth. It gets a little bit confusing in Nex.js. There's a lot of boiler plate you have to set up. And Remix sort of makes things a little bit simpler, but I discovered this library called Nux a few months ago. And this is really useful. It basically replaces useState with useQuery state. And so everything is updated in the URL as you're changing the state.

[5:00] And so you basically get this URL powered state management for free. And treating the URL as a source of truth for state is just extremely useful for obvious benefits going back to the 1990s early 2000s where it's like you can actually bookmark pages and everything's going to be exactly as you saw it which is really nice. So yeah, Nuts is one of those great libraries that I recently started using such as Shad CN where you know amazing libraries but weird names it's a pattern but yeah also fetching data.

[5:00] This is one of those things where we love to use useEffect and use states. It's one of the first things we do when we're learning React and we're thinking, "Oh, wait. We need to fetch data. How are we going to do this?" Well, we're going to set is loading and then we're going to fetch something and then put that in state, but oh, we also have to handle what happens if there's an error.

[6:00] And then this actually doesn't handle cancellation at all. So, this code is actually wrong. And I guarantee if you look in your codebase and you search for set loading or set is loading or is success, you're going to see a lot of this like boolean variable madness in your codebase. So how can we solve that? Well, with React 19, you have server components and you could start fetching directly in them with RSC.

[6:00] And yeah, it makes it extremely simple to do that. You can use suspense too if you want to handle loading states and things like that. You there's also React hooks that really help handle those pending states so that you don't have to. And of course there is React query which really this is one of those things that I wish was built into React because it's extremely useful and it does do all of that boilerplate for you.

[7:00] So handling loading states, error states, even things like caching, retries, etc. the or sorry tansacquery but the main point is that it handles fetching data better than you. So there's also forms this is another big one where we have so many use calls and we have basically the pattern that I see most often is we have one use per field. So you know let's say that we have our name over here favorite variable bio whatever.

[7:00] So each one of those gets a useState because we need to store that value somewhere, right? And then you know we have to handle submit. So we're going to grab all of that data, aggregate it here, and then do something with it. But the and that just shows that. But the DOM actually has its own state management. It's it doesn't even require any JavaScript. When you put a value inside of an input, it's already there.

[8:00] It's under input. value and so same thing with selects text areas etc and so the magical thing that happens is when you give it a name such as first name all of this is available inside what's called the form data API and so you can grab this from when you have handle submit the events you get is a form event and so the target is a form and you could grab the form data directly from there use object from entries grab that form data you could been validated using ZOD.

[8:00] So now you have a runtime checked type safe object representation of your data and you have no use dates. And so you might be thinking like okay that's a cool party trick but you know there's you know why would I use sorry why would I not use dates for that and the thing is with frameworks like remix and nex.js they actually natively work with form data. So, next actually makes this really nice.

[9:00] There's this use action state hook and then you pass in the form action that's returned directly inside the form and you are going to get all of that form data inside here. So, you could just do get for each one of your fields. And so now you have loading states, you have, you know, this async stuff and you have all of your fields just right out of the box. You don't need to useState or useEffect to manage any of that.

[9:00] And let's be honest, forms comprise a lot of what we do as front-end developers. So, and if you do have more complex requirements, there are libraries. Tanstack form is one of them. Also, React hook form is a popular one. The point is that they handle complex forms better than you. So, don't try to reinvent the wheel. And then of course there's one of the more obvious problems with you know, overusing state.

[10:00] You might start with one and then you use eight and then things just get out of control and all of a sudden you're vibe state management thinging. So yeah, now one thing that we could do to sort of condense this is just group related data together. So by that I mean you could use an object that just groups everything instead of having to have one use date per thing. And so this makes your code a lot easier to understand because it shows that this data is related.

[10:00] I've been in many code bases where it's like okay there's 12 different use states. Some of them are related, some are not. They're in different positions and it's hard to know which ones actually go together. And then yeah, so it does become problematic and then you know you might have to write some boilerplate code for updating that but there's libraries like Ammer that actually make this really easy even if you're doing deep updates.

[11:00] So I highly recommend looking at that. And another thing that I talked about at the workshop too is one of the most egregious uses of use dates are for doing stepwise forms. And this the problem might seem very obvious, but it is less obvious when you're working on big components that do still have those multi-step forms or things like that where you're handling the showing of each step as a boolean variable.

[11:00] So we have like is address step, payment step, confirmation step, complete step. And so then you have to do things like this where you're basically switching all of the states that you shouldn't be on to false and then the step that you should be on to true gets really confusing. So instead, you could actually condense that into one single use date where you have what I like to call a string enum that says this is the step that I'm in.

[12:00] And by doing this, you're guaranteed that you're not going to be in more than one of those states at the same time. And you're closer to making a state machine, you know, without impossible states. And if you want to take this further, this is another thing that we did in the workshop yesterday. You could use those string enums with full types to prevent you know impossible states like this because it is possible for error to be true and success to be true and then you have weird situations.

[12:00] So instead you could use a discriminated union. So we have our string enums over here and instead of having just the status being set we could actually combine that into an object. And so this object is called a discriminated union. And so what's cool about this is that you could check the status. And for example, we could set success and then we could set the data. And so if we accidentally omit the data or if we omit the error message when we're in the error state, then TypeScript will yell at us.

[13:00] It will say, "Hey, you said it was success, but I don't see any data." And so the really cool thing is that you don't have to check like hey does this exist or you don't have to put an exclamation mark that says I'm sure this exists because based on those discriminated union types TypeScript will know that those values already exist. So it's really useful and this code is very clear to see and you get the type safety on top of that.

[13:00] So it's pretty cool. A big thing I talked about at the workshop too was derived states. And so this is another one of those huge problems that I see in pretty much every React codebase where we have a bunch of sources of truth states.

[14:00] But we also have derived states such as the total number of items like let's say that we have a doughnut shop and the also the cost total and then in a useEffect whenever those selected donuts change we have to recalculate all of those values because they are dependent on those previous values. So instead of that we can actually just derive it directly in render. And I know some of you might be looking at this and saying okay but can we memoize that please?

[14:00] This is never going to cause an issue. So if you're worried about that then sure memoise but honestly it's not anything you really need to worry about. All right. So, another big topic which I know where you know you're thinking not this, but I hate you state. I know where you're going. Yeah, I I know what you're going to say, but sometimes the situation does call for useReducer, especially if your logic gets complex enough that it warrants that.

[15:00] So for example, let's say that we have a doughnut shop and we could do things like adding a donut, removing a donut. But it's not just, for example, pushing to an array or removing from an array. It's not that simple. We also have a doughut inventory and we have to make sure that the user can only add donuts if there is enough of that doughut to add. So what do we do? we make a reducer so that we can actually guarantee that logic is run instead of being able to directly update the state and so we might have actions such as adding and removing a donut and then we put them in a reducer and then you know the drill.

[15:00] So you call useReducer on the donut reducer and then you could even create helper functions so that you don't have to do this dispatch type etc. But the really cool thing about this is it makes it actually a lot easier to test your core application logic. Instead of having to render your app and then be like, okay, let's click this button and then let's read from this text and see if the donut is actually added to these list of items.

[16:00] Those tests are valuable. But when you have the actual reducer, the core logic, you could write many more tests without having to worry about the intricacies of your UI. meaning your UI can actually change and your tests over here will not break. So these are really valuable tests to have. And so this is why I talk a lot about direct versus indirect state management where direct is you can update state from anywhere.

[16:00] There's no rules. It doesn't matter. And indirect is you send some sort of intent where it's like the user clicked submit or they clicked add donut or something like that and then some core logic decides how are we going to update the state from there and there's a lot of benefits to doing things indirectly even though it's a little bit more boilerplate with events.

[17:00] You have what caused the change, the parameters that caused the change when the change happened if you add a time stamp and also traceability. you have a nice list of events that you can see why did this date end up the way that it is. So there's a couple of other cool things that you could do like cool techniques. useSyncExternalStore really cool hook. It's not really the most popular hook. But it React sort of lists this in other hooks, but I think it's really useful.

[17:00] This is mainly like one of those hooks that library authors would use internally, but there are really good use cases for you to use it as well. So it takes three arguments. the subscribe function for getting the value get snapshot for getting the current value and a server snapshot for when this is actually rendered or when this is yeah rendered on the server. So I'll give you an example over here. Let's say that we want to calculate the window width.

[18:00] You would typically have a use date paired with a useEffect in order to do that. And what you could do instead is create a subscribe function that actually does that subscribing and unsubscribing which is pretty simple. You have a call back and you whenever the window resizes you call that call back whenever and then you could clean that up. And so incync external store you would pass the subscribe function and then you would pass a function to read the snapshot.

[18:00] So, this actually solves a pretty annoying problem in Nex.js where let's say that you just want to show the new dates. You can't just do this because what's rendered on the server is going to be different than what's rendered on the client. So, you know, I've run into this many times and it is a pretty frustrating bug because it's just a date. Why is this problematic? And so you might do this where you have dates in use dates and then you have a useEffect where you actually set that dates but then you're going to get this weird flash of like you know nothing and then you see the dates.

[19:00] But you could actually useSyncExternalStore where don't worry about this. We have the client snapshot which is the date and then we have the server snapshot. This almost works, but you do get a bunch of, you know, errors from Nex.js, and that's because we need this to be serializable on both the client and server. So, we turn this into an ISO string, and then we could just show the date string directly in there.

[19:00] TK Dodo wrote a really good article about how you could avoid hydration mismatches using the USync external store hook. So, it's it's a pretty useful hook. But if you read the React docs of course they will say that hey you should probably just use ustate and useReducer instead. This is like a hook for library actor or yeah, library authors, but you know, I don't read the rules. So, but another, you know, sort of elephant in the room is third-party libraries where we're thinking, why do I need to use a third party library like Redux, sustainate and useReducer?

[20:00] There there are a few problems with this and a few limitations with React where let's say you want state and that state has some special logic. So you might put it in useReducer instead and then you need to share it between components. So then okay you have to somehow lift that into context. By the way in React it's just use now. You don't need the use context anymore. You could just pass your context directly into use.

[21:00] But then you realize, okay, my state's changing often and it's causing way too many rerenders. So, you do a pub sub pattern. If you're from Florida, you would get this, but here's and you might think, okay, well, I actually don't need the entire state. So, you have to create some sort of custom U selector hook. And at the end of the day, you just reinvented Redux. And, all of this is just abstract boilerplate code.

[21:00] And so that's why there's the concept of a store that a lot of state management libraries have where the store can either just live outside the components and you just import it where you need it or you could pass it in through context and then the store goes through a store provider and then every component that needs it can just read directly from the context. So I and that's why there's a lot of choices for this you know Redux and Sydney end and X8 those are all examples of storebased libraries and then you have the atom based libraries too like Jotai and recoil but I want to real quickly talk to you about not XState but XState store.

[22:00] This is one of the newer libraries that came out this year and it actually is supposed to be a much simpler version of XState for first state management if you don't need all of the finite state machine and statechart stuff. And so just to show you what that looks like, let's say that we have a counter. So you would put the count inside the context. So you know count is zero. And then you would have an object that has all of the transitions which just dictate how the data changes.

[22:00] So this is pretty simple. You would just add one to the context.ount and then you could subscribe directly to the store. So so far I didn't show you any React because it really works in any library. But yeah, so you could subscribe to whenever the data the data changes and you could trigger events to send. So you could either do send with the event object or you could do trigger dot whatever the event is that you want to send so that you could trigger those transitions.

[23:00] So let's say that you know we have our donut chop example from before. This is what it would look like in XState store. And this is all in the read me by the way. So I'm not going to go too deeply into the code but we have this convenient use store hook. So, if you don't want to useReducer, you could just use store and do all of the same stuff just in a much more concise syntax where you have all of your transitions here such as adding a donut, removing a donut, you're checking on the inventory, which is why we have that custom logic.

[23:00] And then you could select the pieces of state that you really care about. So, it's not going to cause excessive rerenders when some unrelated piece of states changes. And then you could just directly trigger from the doughnut store to make changes such as adding a donut or removing a donut. And hopefully you hate that less than having to do all that yourself or useReducer. Another thing that you could do with XState store which I think is sort of a missing feature in things like Redux and Zustand is the ability to emit events.

[24:00] And so this allows you to have declarative side effects which is pretty nice. So let's say that we want to emit some sort of out of stock so that you could show some sort of popup that says hey it's out of stock you should know about this. So when you add a donut, there's a third argument over here where you could pull emits from and then you could emit that out of stock message and say, "Hey, this donut is out of stock." And then you could subscribe to that and this is one of the good use cases of useEffect where you listen for that out of stock message and when you get it, you have the donut inside and you could say, "Hey, we're out of stock of that donut." So pretty simple.

[25:00] So, X8 store, another cool thing about this is that it acts both like a global store where you could use it just like substander, put it in and grab whatever you need, but it can also be a local store too. So, it works just like useReducer as well. And the third trick is it also works as an atomic library. So it does work like re recoil or jotai where you would create an atom and then you could set the data directly and you can also have combined atoms.

[25:00] So you could read from the flavor atom and the coffee atom and then you could subscribe just like XState store. In fact the stores that you create with XState store are also atoms too. So you could mix and match them. Another topic and this is a final topic that I want to talk about with regards to states is that really there's just so much beyond useState and useEffect and all the other things that I've talked about.

[26:00] So this is what's coming in the horizon local first state and this is one of the things I'm very excited about. You could also consider them sync engines but the idea is that right now the way that we fetch data is very manual. We're making a request. We're waiting for a response. This is hitting the database. It is very manual. But with data sync engines instead of that we are automatically syncing with the database.

[26:00] So we have some representation of data. We could make changes and see those changes immediately in the front end and that is synced with our database, our reactive database in the back end. And so any updates to that database get fed directly into our front end. Now there's a lot of libraries sort of in this area. Zero is actually no longer an oh well no zero might still be an alpha. Live store was actually just released.

[27:00] Did a public general release. So that's the one that I'm really excited about. But there's a lot of a lot of sync engines out there that I recommend you take a look at. But yeah. So that just gives you a preview that's a whole other talk. The point though that I want to make is that useState it's something that's really useful for truly component internal UI state. And so these are small pieces of state that don't really have too much complex logic attached to them.

[27:00] And you also have to realize that not all state is UI state. Some of it fits in useRef. Some of it might fit in some external store. So in general ask yourself is there a better pattern and especially with React 19 there usually is. So overall start with useState refactor to better patterns and focus on maintenance because simple is better than easy. So thank you so much any questions or yeah the idea of local first I would get it to work in any of my demos or examples that I've tried.

[28:00] Do you have a specific library like if I'm trying to get it to work out of the box for the first time? I would yeah, it is tricky because they're not exactly like just easily plugandplay and that's actually why I really like Nucks because that is plugandplay. It's really nice to work with. But yeah, I would take a look at what was that first definitely I don't know why I'm blanking on the name, but this one live just because Johannes Strickling worked really hard on it and he's always looking for feedback. like he would even call me multiple times being like, "what do you think about this?" etc.

[29:00] And so he talks to everyone about it. So, yeah, take a look at live store. He's done a lot of really good things with it. Thank yeah. Any other questions? Yes. So, is that mobile first idea? because that's similar to like what mobile development decided to deal with for a long time where like you know the signal might not be there all the time. So I know I've used like AWS amplified a few years ago but I'm not vouching for it but that was just one of the nicities that came with that is it would just handle that server sync in the background.

[30:00] I I think so. So yeah, I was going to say, you know, notion as an example, but notion's pretty bad at this. But yeah, the idea is that you can be doing things locally and even if there's no internet connection. That's one of the premises of local first is that it's going to sync eventually. It is eventually consistent so that you know things that are updated externally will sync back when you get back online.

[30:00] So it is pretty nice. And I hate it when apps that should be local first just do not work when there's no internet. It's one of my biggest frustrations. Any other questions on use date, useEffects? Are you mad at me?

[31:00] Awesome. Thank you so much, David. That was amazing. Yeah. I was just I was just thinking as we were sitting here the first time I saw I think David's Twitter thread about using the browser built-in form state rather than holding like variables in each value in in in use states in a component. And I remember sharing that with my core organizer Brian and some of some of our other friends and how just how mind-blowing that was.

[31:00] It's really cool and a cool fanboy moment for me to share the stage with you tonight in some small way. So, well, cool. I have a couple more slides to throw out there to close us out. First, I want to shout out our folks who are watching at home, Andy and AFA. And I think Kevin was in there too. I think Kevin's no longer joining us, but just want to say that I appreciate and then for those of you as well if you're here tonight and our next IRL event you can't make it, we do a good job or try to do a good job at least of getting this online so you can follow along at home.

[32:00] Gosh, there are some other things, weren't there? Let me see. Can I Oh, yeah. You all already know about the Discord. We do have a couple more minutes remaining if anybody we have a long tradition here of having what we call lightning talks. They're maybe not traditional lightning talks in the sense that not it's not going to be like a bunch of people answering questions really quickly. Lightning talks in the sense of just like short demos.

[32:00] Somebody has something they might like to share with the group. We've had people launch companies at JavaScript before. So it's not unheard of. If anybody has a short demo, we're talking like under five minutes or something they like to share tonight. We like to leave the floor open for them now. Raise your hand. I'm comfortable making the silence awkward. All right. Is it awkward enough yet, Brian? He's he hasn't said so yet.

[32:00] Okay. He says no. All right. Y'all are off the hook.

[33:00] Well, just know that for subsequent events, if you have something you'd like to share, whether that's a project you're really proud of or a project you're just starting, a blog post you wrote, a demo, a tech demo, doesn't have to be super polished. Like I said, we try to be one of the nicest kindest rooms you can get in the Twin Cities. Just know that we're always here for you. All right, that's enough of me yapping.

[33:00] We'll do a couple more shout outs and thank yous of course to Frontend Masters Mark, Dustin, Christa, and the whole crew. Of course to David for presenting and for Caleb for his awesome can we get another round of applause for our speakers today? to our host Frontend Masters and data site for the pizza and the and the food and the letting us in the door and letting us have this awesome space.

[33:00] Another round of applause, please. All right. I guess without further ado, I'll see you all next time. We can hang out for a little while longer. We'll start to clean up. Whether this is your first or millionth event, thanks for being here. I'll see you next time.
