Understanding useReducer in React
React provides several hooks to manage state in function components. Among these, useReducer stands out as a powerful alternative to useState, especially for managing complex state transitions. In this guide, we will break down how to master the useReducer hook, share best practices, and walk through 10 practical examples to help you build better React applications.
Let’s dive deep into usereducer, understand its use cases, and learn when it’s better than other hooks.
How useReducer Works in React
The useReducer hook is based on the concept of a reducer function, familiar to those who have worked with Redux. It is a pure function that takes in the current state and an action, and returns a new updated state.
Unlike useState, which simply updates a value, useReducer is best suited when:
State logic is complex.
The next state depends on the previous state.
There are multiple related state values.
Basic useReducer Syntax Explained
Here’s the basic syntax of usereducer in react:
reducer: A function that determines how the state should change.
initialState: The initial value of the state.
state: The current state.
dispatch: A function to trigger state changes.
Example of a simple reducer:
state
: The current state object (example:{ count: 5 }
).action
: An object that usually has at least atype
property (example:{ type: 'increment' }
).switch (action.type)
: Checks what kind of action you want to perform.return
: Always returns a new state object (important for keeping things immutable — not directly changing the old state).
Why Choose useReducer Over useState?
Better state organization: Manage complex logic cleanly.
Predictable updates: Actions define explicit state transitions.
Easier debugging: Centralized reducer functions.
Scalable code: Especially useful when the app grows.
10 Practical Examples of useReducer in React
Let’s now see 10 detailed examples of usereducer hook usage.
1. Basic Counter Example
useReducer import:
→ Brings inuseReducer
from React to manage complex state.initialState:
→ Sets the starting state{ count: 0 }
.reducer function:
→ Takes currentstate
andaction
, and returns the new updated state.Counter component:
→ UsesuseReducer
to getstate
anddispatch
function.dispatch:
→ Sends actions (increment
ordecrement
) to the reducer.UI (JSX):
→ Shows the current count and provides buttons to update it.
2. Managing Form State
initialState:
→ Starts with emptyname
andemail
fields.reducer function:
→ Updates the specific field (name
oremail
) without changing the rest of the state.Form component:
→ UsesuseReducer
to manage form data.handleChange function:
→ Dispatches the input’sname
andvalue
to update the correct field.Inputs (UI):
→onChange
callshandleChange
, and inputs show the lateststate
values.Output:
→ Displays the fullstate
as a JSON string below the inputs.
3. Toggle Theme (Light/Dark Mode)
initialState:
→ Starts with the theme set to'light'
.reducer function:
→ Switches the theme between'light'
and'dark'
when thetoggle
action is dispatched.ThemeToggle component:
→ UsesuseReducer
to manage theme state and update it based on button clicks.dispatch with
toggle
action:
→ When the button is clicked, thedispatch
sends the action{ type: 'toggle' }
to the reducer.state.theme
in className:
→ Applies the current theme ('light'
or'dark'
) to thediv
‘s class name to dynamically change the theme style.
4. Managing Multiple Inputs
initialState:
→ Initializesusername
andpassword
as empty strings.reducer function:
→ Updates the corresponding field (username
orpassword
) in the state based on thename
andvalue
of the input field.LoginForm component:
→ UsesuseReducer
to manage form state and updates theusername
andpassword
based on user input.dispatch with
e.target
:
→ When a user types into either theusername
orpassword
input, thedispatch
function is called, passinge.target
(which containsname
andvalue
properties) to the reducer.Inputs (UI):
→ Thevalue
of each input is tied to the corresponding state field (state.username
orstate.password
), ensuring the inputs are controlled components.
5. Cart Management in E-commerce
initialState:
→ Starts with an empty array[]
representing the cart.reducer function:
→ Handles two actions:add
: Adds a product to the cart.remove
: Removes a product from the cart by filtering out the item with the givenid
.
Cart component:
→ UsesuseReducer
to manage thecart
state, allowing items to be added and removed.dispatch
withadd
action:
→ When the “Add Product” button is clicked, an item is added to the cart with theid
andname
properties.dispatch
withremove
action:
→ Each item in the cart has a “Remove” button that removes the item by dispatching theremove
action with the item’sid
.Rendering the Cart:
→ The cart is rendered as a list of products. Each product has its name displayed along with a “Remove” button.
6. Step Wizard Form
initialState:
→ Initializes withstep: 1
, indicating the starting step in the wizard.reducer function:
→ Handles two actions:next
: Increases the current step by 1.previous
: Decreases the current step by 1.
Wizard component:
→ UsesuseReducer
to manage the current step, and renders the current step and buttons to navigate between them.dispatch
withnext
action:
→ When the “Next” button is clicked, thestep
is incremented by 1.dispatch
withprevious
action:
→ When the “Back” button is clicked, thestep
is decremented by 1.Displaying the current step:
→ The current step is displayed in the<p>
element.
7. Show/Hide Password
initialState:
→ Initializes withshowPassword: false
, meaning the password is initially hidden.reducer function:
→ Handles the action to toggle the visibility of the password:toggle
: Flips theshowPassword
state betweentrue
(show) andfalse
(hide).
PasswordToggle component:
→ UsesuseReducer
to control the visibility of the password input field.dispatch
withtoggle
action:
→ Clicking the “Show/Hide” button dispatches thetoggle
action to change theshowPassword
state.Password input type change:
→ The input field type switches betweentext
(show password) andpassword
(hide password) based onstate.showPassword
.
8. Notification Handler
initialState:
→ Initializes withmessage: ''
andtype: ''
to store the message and its associated type.reducer function:
→ Updates the state with the newmessage
andtype
values based on the dispatched action.Notifications component:
→ UsesuseReducer
to handle the state and display notifications with different types.dispatch
withsuccess
action:
→ Clicking the “Success” button triggers thedispatch
to set the message to'Success!'
and type to'success'
.dispatch
witherror
action:
→ Clicking the “Error” button triggers thedispatch
to set the message to'Error occurred'
and type to'error'
.Notification display:
→ The message is displayed conditionally, and the notification type is applied dynamically to set the appropriate style (e.g.,alert-success
oralert-error
).
9. Loading State Management
initialState:
→ Initializes withloading: false
, meaning the process is not currently loading.reducer function:
→ Handles two actions:start
: Setsloading
totrue
, indicating the loading process has started.end
: Setsloading
tofalse
, indicating the loading process has finished.
Loader component:
→ UsesuseReducer
to track whether the system is loading or done and displays the appropriate message.dispatch
withstart
action:
→ Clicking the “Load” button triggers thedispatch
with{ type: 'start' }
to setloading
totrue
.dispatch
withend
action:
→ Clicking the “Finish” button triggers thedispatch
with{ type: 'end' }
to setloading
tofalse
.Conditional rendering:
→ The component displays either “Loading…” or “Done!” based on the value ofstate.loading
.
10. Complex Calculator
initialState:
→ Initializes withtotal: 0
, starting the calculator with a total value of zero.reducer function:
→ Handles three actions:add
: Increases thetotal
by the value provided in the action.subtract
: Decreases thetotal
by the value provided in the action.reset
: Resets thetotal
back to0
.
Calculator component:
→ UsesuseReducer
to manage and update thetotal
value.dispatch
withadd
action:
→ Clicking the “Add 5” button triggers thedispatch
with{ type: 'add', value: 5 }
, adding 5 to the current total.dispatch
withsubtract
action:
→ Clicking the “Subtract 3” button triggers thedispatch
with{ type: 'subtract', value: 3 }
, subtracting 3 from the current total.dispatch
withreset
action:
→ Clicking the “Reset” button triggers thedispatch
with{ type: 'reset' }
, setting the total back to 0.Display the total:
→ The total is displayed dynamically, updating each time the state changes based on user input.
Best Practices for useReducer in React
Always keep reducer functions pure.
Use constants for action types to avoid typos.
Modularize reducer logic in large applications.
Handle default cases in reducers.
Avoid unnecessary re-renders by using React.memo if needed.
Frequently Asked Questions (FAQs)
What is useReducer in React?
- useReducer is a React Hook that lets you manage complex state logic in a function component through a reducer function, providing an alternative to useState.
When should I use useReducer?
- Use useReducer when state logic involves multiple sub-values, complex conditions, or depends on previous state transitions.
What’s the difference between useState and useReducer?
useState is simpler for single values.
useReducer is better for complex or related state changes that require multiple actions.
Can I replace Redux with useReducer?
- In small applications, yes. useReducer manages local component state similarly to Redux but without the full global state management overhead.
How do I structure actions in useReducer?
- Use type and optional payload inside action objects to clearly define what change should happen in your reducer.
Final Thoughts
Mastering the usereducer hook is a crucial skill for modern React developers. With a good grasp of useReducer in React, you can manage complex component states much more effectively, making your code scalable, maintainable, and professional.
Keep practicing these examples, and soon using usereducer will feel natural in your React development journey!
This is a simple example of a reducer function in JavaScript. It demonstrates how to handle state changes in a predictable way. The reducer takes the current state and an action as arguments, then returns a new state based on the action type. It’s a fundamental concept in state management libraries like Redux. Why is using a reducer considered beneficial for managing application state?