Managing State With useReducer | Lecture 185 | React.JS 🔥
Summary
TLDRThe video script discusses the useReducer hook in React, which centralizes state updating logic in a reducer function, ideal for complex state management. It contrasts useReducer with useState, highlighting that useReducer allows for related state pieces to be managed together, resulting in cleaner, more readable components. The script explains that a reducer function takes the current state and an action to return the next state, emphasizing immutability and the lack of side effects. It also describes the dispatch function's role in triggering state updates based on actions. An analogy of withdrawing money from a bank illustrates the concept, clarifying the roles of dispatcher, reducer, action, and state in the useReducer mechanism.
Takeaways
- 🧠 UseReducer Hook: Centralizes state updating logic in a reducer function.
- 📦 Complex State Management: useReducer is ideal for managing complex state and related pieces of state.
- 🔄 useState Limitations: In complex scenarios with multiple state variables and updates, useState can become overwhelming.
- 🎯 Synchronous State Updates: useReducer allows for multiple state updates as a reaction to the same event.
- ⚙️ Reducer Function: A pure function that takes current state and action to return the next state without mutation.
- 🚫 Immutable State: React's state must not be mutated directly; reducers must return new state objects.
- 📜 Action Object: Describes state updates with an action type and payload, which is input data.
- 🔧 Dispatch Function: Triggers state updates by sending actions to the reducer from event handlers.
- 🔄 State Update Cycle: Dispatching an action leads to state computation by the reducer and subsequent component re-render.
- 💡 Reducer Analogy: Like the array reduce method, React reducers accumulate actions into a single state over time.
- 📈 useState vs useReducer: useState is simpler but useReducer solves complex state management problems and keeps components clean.
Q & A
What is the primary purpose of the useReducer hook?
-The useReducer hook is used to centralize state updating logic in a reducer function, making it an ideal solution for managing complex state and related pieces of state in a React application.
Why might using useState be insufficient for managing state in certain situations?
-In situations where components have many state variables and updates spread across multiple event handlers, or when multiple state updates need to occur simultaneously, using useState can become overwhelming and difficult to manage.
How does useReducer help with the challenges of managing complex state?
-useReducer allows for the decoupling of state logic from the component itself by moving all state updating logic into a single reducer function, resulting in cleaner and more readable components.
What does a reducer function typically take as inputs?
-A reducer function typically takes the current state and an action as inputs, and based on these values, it returns the next state (the updated state).
What is the significance of immutability in the context of React state management?
-In React, state is immutable, meaning the reducer function is not allowed to mutate the state directly. Instead, it must always return a new state object based on the current state and the received action.
What is the role of the dispatch function in useReducer?
-The dispatch function, returned by useReducer, is used to trigger state updates by sending an action to the reducer, which then computes the next state based on the current state and the action.
How does the useReducer mechanism differ from useState in terms of updating state?
-With useState, you directly call setState with the new state value to update the state, whereas with useReducer, you use the dispatch function to send an action to the reducer, which then determines how to update the state.
What is the analogy used in the script to help understand the useReducer mechanism?
-The analogy used is that of withdrawing money from a bank. The customer (dispatcher) goes to the bank (reducer) to request money (state update), and the bank employee (reducer function) handles the withdrawal from the vault (state object) based on the customer's request (action).
What does the action object represent in the useReducer mechanism?
-The action object represents a request to update the state. It usually contains an action type and a payload, which provides the necessary information for the reducer to determine how to update the state.
How does the useReducer hook manage related pieces of state?
-useReducer manages related pieces of state by storing them in a state object that is returned from the hook. This allows for a more organized and efficient way of handling complex state updates.
What is the benefit of using useReducer over useState in complex scenarios?
-useReducer provides a more structured and scalable approach to state management, especially for complex scenarios with multiple related state variables and updates. It helps maintain a clear separation of concerns, making the codebase easier to understand and maintain.
Outlines
📚 Introduction to useReducer and its Benefits
This paragraph introduces the useReducer hook as a centralized solution for managing state updates, particularly for complex scenarios where multiple state variables and updates are involved. It contrasts useReducer with the useState hook, highlighting the challenges of using useState for managing state in larger, more complex components. The useReducer hook is presented as an alternative that allows for related state pieces to be managed together, promoting cleaner and more readable component code by moving state logic into a reducer function. The paragraph emphasizes the importance of the reducer function in updating the state object and the concept of immutability in React, where the reducer must return a new state object without mutating the existing one.
🔄 How useReducer Works: Actions, Dispatch, and Reducers
This paragraph delves into the mechanics of useReducer, explaining the roles of action objects, the dispatch function, reducers, and the state object. It describes how actions contain information for the reducer, typically in the form of an action type and a payload. The reducer's function is to take the current state and an action to compute the next state, which then triggers a re-render of the component. The analogy of a bank transaction is used to illustrate the process, where the customer (dispatcher) requests an action (withdrawal), and the bank employee (reducer) performs the update on behalf of the customer, keeping the process organized and abstracted from the user.
🏦 Analogy of useReducer with Bank Transactions
The paragraph uses a detailed analogy of banking to clarify the useReducer mechanism. It compares the state to a bank's vault, where data is stored and updated. The customer represents the dispatcher, who initiates the state update by requesting an action. The bank employee is likened to the reducer, responsible for executing the action based on the instructions (action object) provided by the customer. This analogy emphasizes the separation of concerns, where the reducer handles all the logic for updating the state, allowing developers to maintain clean and straightforward components. The analogy also highlights the importance of the action object, which contains the necessary information for the reducer to perform its task.
Mindmap
Keywords
💡useReducer
💡useState
💡reducer function
💡immutable state
💡action
💡dispatch function
💡state object
💡component
💡event handler
💡pure function
💡re-render
Highlights
The useReducer hook centralizes state updating logic in a reducer function.
useReducer is an alternative to useState for managing complex state and related state pieces.
As components and state updates become complex, useState may not be sufficient.
Multiple state updates often need to happen simultaneously as a reaction to the same event.
Updating one piece of state can depend on one or more other pieces of state, which can be challenging with many states.
useReducer returns a state and a dispatch function, which helps in managing state more effectively.
The reducer function is responsible for all state updates, moving the logic from event handlers into a central place.
Decoupling state logic from the component makes the components cleaner and more readable.
Reducers must be pure functions that always return a new state based on the current state and received action.
Actions are objects that describe how state should be updated, typically containing an action type and payload.
Dispatch function triggers state updates by sending actions to the reducer.
Updating state with useReducer triggers a re-render of the component instance.
The reducer function accumulates all actions into one single state over time, similar to the array reduce method.
Dispatch function coordinates the state update process and gives the reducer access to the current state.
useReducer is more complex to set up than useState but solves specific problems in managing state.
An analogy of withdrawing money from a bank helps clarify the useReducer mechanism.
The state is like a bank's vault where data is stored and updated.
The customer represents the dispatcher, requesting the state update through an action.
The bank employee is the reducer, performing the update based on the action's instructions.
The action is the request message containing the action type and payload for the withdrawal.
Transcripts
So we just learned how to use the useReducer hook
to centralize all the state updating logic
in one central place, which is the reducer function.
So let's now dive deeper into the concept of reducers
and how and why they can make our applications
a lot better in certain situations.
So up until this point,
we have been using the useState hook
to manage all our state, right?
However, as components and state updates
become more complex, using useState to manage all state
is, in certain situations, not enough.
For example, some components have a lot of state variables
and also a lot of state updates
that are spread across multiple event handlers
all over the component or maybe even multiple components.
And so this can quickly become overwhelming
and hard to manage.
It's also very common that multiple state updates
need to happen at the same time
so as a reaction to the same event.
For example, when we want to start a game,
we might have to set the score to zero,
set an is playing status and start a timer.
And finally, many times updating one piece of state
depends on one or more other pieces of state,
which can also become challenging
when there is a lot of state.
And so in all these cases, useReducer can really help.
So these are the problems that reducers try to solve
and so let's now see how.
So first of all, useReducer is an alternative way
of setting and managing state,
which is ideal for complex state
and for related pieces of state.
Now, we already used useReducer in the last two lectures
and this is what that looked like.
So we call useReducer
with a reducer function and its initial state
and it returns a state and a dispatch function.
So starting from the beginning, when we use useReducer,
we usually store related pieces of state
in a state object that is returned from the useReducer hook.
Now, it could also be a primitive value
but usually, we use objects.
Now, as we already know, useReducer needs something called
a reducer function in order to work.
So this function is where we place all the logic
that will be responsible for updating the state
and moving all state updating logic from event handlers
into this one central place allows us
to completely decouple state logic from the component itself
which makes our components so much cleaner
and so much more readable.
So when we manage state with useReducer,
it's ultimately this reducer function
that will be updating the state object.
So in a way.
it's a bit like the setState function in useState
but with superpowers.
Now in practice, the reducer is simply a function
that takes in the current state and an action,
and based on those values, returns the next state,
so the updated state.
Now, keep in mind that state is immutable in React.
This means that the reducer
is not allowed to mutate the state,
and in fact, no side effects are allowed
in the reducer at all.
So a reducer must be a pure function
that always returns a new state.
And again, based on the current state
and the received action.
And speaking of the action, the action is simply an object
that describes how state should be updated.
It usually contains an action type and a so-called payload
which is basically input data.
And it's based on this action type and payload
that the reducer will then determine
how exactly to create the next state.
And now the final piece of the puzzle is this.
How do we actually trigger a state update?
Well, that's where the dispatch function comes into play.
So useReducer will return a so-called dispatch function
which is a function that we can use
to trigger state updates.
So instead of using setState to update state,
we now use the dispatch function in order to send an action
from the event handler
where we're calling dispatch to the reducer.
And as we already know,
the reducer will then use this action
to compute the next state.
Okay, so these are all the pieces that need to fit together
in order to effectively use the useReducer hook.
So an action object, a dispatch function, a reducer,
and a state object.
But now let's also look at the diagram
to really see how all of these pieces actually fit together
in order to update state.
So let's say
that we're in an event handler in some component
and we now need to update some state.
So what do we do? Well, that's right.
We call the dispatch function
that we got back from useReducer
in order to dispatch an action to the reducer.
And this action, as we learned before,
is an object that contains information for the reducer.
So information about how the reducer
should update the state.
In this case, the action type is updateDay
and the payload is 23, which probably means that the reducer
will set the day state to 23.
Now, the object doesn't need to have this exact shape
with a type in the payload, but it's a standard
that has been adopted by most developers.
Now basically, the reducer takes in this action
together with the current state
and it will then return a brand new state object
which we usually call the next state
in the context of reducers.
And as always with state,
updating state will then trigger a re-render
of the component instance.
Now, if you're wondering why the reducer function
is actually called a reducer,
the answer is that it's because it follows
the exact same idea as the array reduce method.
So just like the reduce method
accumulates all array values into one single value,
the React reducer accumulates all actions
into one single state over time.
Okay now, behind the scenes,
the dispatch function has access to the reducer
because we passed it into the useReducer hook, right?
So dispatch is really coordinating this whole thing
and also giving the reducer access to the current state.
And now to understand this even better,
let's compare the mechanism of useReducer
with a much simpler useState mechanism.
So when we use useState, we get back a setter function
and let's just call it setState.
And then when we want to update state,
we just passed the new updated state value that we want
and React will simply update the state
which in turn will trigger the re-render.
So it's a lot simpler and more straightforward
than useReducer, but since useReducer solves the problems
that we saw earlier in this lecture,
it's a great choice in many situations,
even though it's a bit more complicated to set up.
But we will talk more about the big advantages of useReducer
and also when to use it later in the section.
Now, I understand that this whole idea
of dispatching actions and writing reducers
is super confusing in the beginning.
I know because I do remember
how confused I was back in the day.
And so let me show you now a really helpful analogy
that made all this really clear to me
when I first learned about this.
So imagine that you needed to take $5,000
out of your bank account for some reason.
Now, since this is a large amount,
you can't just do it from an ATM,
so you need to go physically to a bank.
Now, once you are at the bank,
how do you actually get those $5,000?
Do you walk straight into the bank's vault,
grab the cash, and then go home?
Well, I don't think so, right?
That's usually not how it works.
How it does work is that you go into the bank
and there you'll find a person sitting at a desk
ready to assist you.
Now, when you arrive at the bank,
you already know how much cash you want to withdraw
and from what account number.
And so you walk right to the person
and tell them that you would like to withdraw $5,000
from account 923577 for example.
What happens then is that usually the person
will type something into his computer,
check if you actually have the cash in your account,
and if so, he goes to the bank's vault,
and gets the money to finally hand it over to you.
It's your money after all, right?
But note the big difference between this real version
and the previous version of the story
where you just grabbed the cash yourself.
In this real version, you told the person what to do
and how to do it and he then got the money
for you on your behalf,
and so you didn't take the money directly yourself.
And that's a huge difference.
So does this maybe start to sound familiar?
Well, I hope it does.
And so let's now bring this analogy back to useReducer
and identify what each of these pieces represents
in the useReducer mechanism.
And let's start with the most important thing, the state.
So what do you think the state is in this analogy?
Well, the state is represented by the bank's vault
because this is where the relevant data, so the money,
is stored and also updated.
So the vault is what needs to be updated,
and so that's our state.
Nice, so with that out of the way,
let's think about how the money is taken from the vault.
So about how state is actually updated.
So starting from the beginning,
what do you think the customer going to the bank
represents in this analogy?
Well, the customer going to the bank
and requesting the money is clearly the dispatcher
because it is who is requesting the state update, right?
And they're doing so by going to the person
and requesting to withdraw the $5,000.
So what is the reducer here and what is the action?
Well, the reducer is going to be
the person working at the bank
because that's the one who actually makes the update.
In this case, it's the one who goes to the vault
to get your money.
But how does the person know how much money to take
and from what account?
They know because you told him so
exactly in your request message.
And so that request message is clearly the action.
In this example, the action can be modeled like this.
With the action type being withdraw
and the payload being the data about the withdrawal
that you want to make.
So summarizing, you went into the bank
with a clear action in mind,
you then dispatched that action to the reducer,
so to the person working there who took the action,
and followed the instructions
to take the right amount of money from your account.
So from state.
he then gave you your money finishing this cycle.
So you did not go directly into the vault
and took your money.
Instead, you had the person, as a middleman,
who knows a lot better than you
how to perform different actions on the vault.
So he knows how to deposit, how to withdraw,
how to open and close an account,
how to request a loan, and more.
And he does all this
without you having to worry about the details.
So exactly like a reducer function,
which also decouples and abstracts
all the state updating logic away from you, so that you
can have clean and easy to understand components.
Okay, so I hope that this now made the relationship
between dispatcher, reducer, action, and state crystal clear
and you will get plenty of opportunities
throughout this section to practice all this.
Browse More Related Video
Introduction to Redux | Lecture 257 | React.JS 🔥
useState Hook | Mastering React: An In-Depth Zero to Hero Video Series
Rules for Render Logic: Pure Components | Lecture 131 | React.JS 🔥
How NOT to Fetch Data in React | Lecture 139 | React.JS 🔥
The useEffect Cleanup Function | Lecture 151 | React.JS 🔥
State Update Batching in Practice | Lecture 133 | React.JS 🔥
5.0 / 5 (0 votes)