No history yet

Professional State Patterns

Transcript

Beau

Okay, Jo, I've been building out this user settings page, and I feel like I'm doing something wrong. I've got, like, five different useState hooks. One for the username, one for the email, one for the bio... it works, but it feels so... clunky. There has to be a better way to handle forms, right?

Jo

That's a super common growing pain. And what you're describing is actually a great starting point for a really important concept in React: controlled components. It sounds like that's what you're building, even if you didn't use the name.

Beau

Controlled components... okay. I assume that means there's an 'uncontrolled' version, too?

Jo

Exactly. The distinction is all about where the 'source of truth' for the input's value lives. In a controlled component, React state is the boss. The input's value is set by your state variable, and it can only be changed by your setter function.

Beau

Right, so when I type, it triggers an onChange, which calls setUsername, which updates the state, which then re-renders the component and feeds that new value back into the input.

Jo

You got it. The flow is circular: UI triggers state change, state change triggers UI update. An uncontrolled component is more like old-school HTML. The DOM manages its own internal state. You might just grab the value with a ref when the user clicks 'submit', but React isn't involved in every keystroke.

Beau

Okay, that makes sense. Controlled gives you more power, like real-time validation or disabling the submit button if the form isn't valid. So, back to my first problem... cleaning up all those useStates.

Jo

Right. Instead of five separate state variables, you can have one. You can use a single state object. So, you'd have something like `useState` with an object inside that has keys for `username`, `email`, and `bio`.

Beau

Ah, okay. But then how do you update just one field? If I call the setter with just the new username, won't it wipe out the email and bio?

Jo

That's the key question, and it goes back to what we learned about immutability. You have to use the spread operator. In your change handler, you'd call your setter with a new object. First, you spread the existing state object to copy all the old values, and then you specify the one key you want to change.

Beau

So... `setFormState`, and inside I pass it `...formState`, and then `username: event.target.value`. Got it. That is so much cleaner. It feels more like the data structures I'm used to from the back-end.

Jo

Exactly. And this idea of managing different 'states' of a process goes way beyond forms. Think about fetching data from an API. It's not just about the data you get back. There's also the time *before* you have the data, and the possibility that something goes wrong.

Beau

You mean like loading and error states.

Jo

Yep. A really robust pattern is to have a state object that holds three things: `data`, `loading`, and `error`. When you start the API call, you set `loading` to true. If it succeeds, you put the response into `data` and set `loading` to false. If it fails, you put the error message in `error` and set `loading` to false.

Beau

And then in my component's return statement, I can just check those flags. If `loading` is true, show a spinner. If `error` has something in it, show an error message. Otherwise, show the `data`. That's a great mental model.

Jo

It covers all the bases. But as these state objects and the logic to update them get more complex, you might feel that `useState` is starting to get clunky again. Imagine a form with ten fields, where changing one field affects another... that setter function could get really complicated.

Beau

So... what's the next step? When does `useState` stop being the right tool for the job?

Jo

That's when you start looking at another hook called `useReducer`. It's basically a more powerful version of `useState` for complex state logic. The rule of thumb I use is this: if your state transitions are complex, or if the next state depends on multiple sub-values of the previous state, `useReducer` might be a better choice.

Beau

Okay, so `useReducer`... what does that look like? Give me the mental movie.

Jo

Imagine instead of just a setter function, you have a... a central command center, called a 'reducer'. You don't tell it *what* the new state is. Instead, you send it an 'action', like 'USER_TYPED_IN_EMAIL_FIELD'. The command center then looks at that action and the current state, and it figures out what the new state should be. It separates the 'what happened' from the 'how to update'.

Beau

Ah, I see. So for a simple form, `useState` with a state object is probably fine. But if I was building, say, a multi-step wizard form where your answer on step one changes the questions on step three... that's when `useReducer` would really shine.

Jo

Exactly. You've hit the nail on the head. It's about choosing the right level of complexity for the problem. Starting with `useState` is almost always the right call. But knowing when and why to graduate to `useReducer` is what separates a junior developer from a senior architect. It's about building a state architecture that's not just functional, but also maintainable and easy for the next person to understand.

Beau

Okay, that's a perfect roadmap. So for now, I'm going to refactor my settings page to use a single state object. It feels like the right-sized solution.