Mastering State Management with the useReducer Hook in React
Sudhanshu Gaikwad
Posted on October 20, 2024
When building React applications, managing state can become complex, especially when dealing with multiple state variables that depend on each other. If you find yourself in this situation, it's time to explore the power of the useReducer
Hook!
What is useReducer?
TheuseReducer
Hook is a powerful tool for managing state in a predictable way. It allows you to manage complex state logic in a clean and organized manner, making your code more maintainable.
Syntax
The useReducer Hook accepts two arguments:
useReducer(reducer, initialState)
reducer:
A function that contains your custom state logic.
initialState:
The initial state of your component, usually an object.
How it Works
- Current State: The current state value based on the latest dispatched action.
- Dispatch Method: A function that lets you send actions to the reducer to update the state
1.Example:
Here's a simple example of how to use the useReducer Hook to manage a counter:
import React, { useReducer } from "react";
// Define the initial state
const initialState = { count: 0 };
// Define the reducer function
const reducer = (state, action) => {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
return state;
}
};
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
// Inline styles
const containerStyle = {
maxWidth: "400px",
margin: "50px auto",
padding: "20px",
borderRadius: "10px",
boxShadow: "0 4px 20px rgba(0, 0, 0, 0.2)",
textAlign: "center",
backgroundColor: "#ffffff",
fontFamily: "'Roboto', sans-serif",
};
const headingStyle = {
fontSize: "2.5rem",
margin: "20px 0",
color: "#333333",
};
const buttonStyle = {
margin: "10px",
padding: "12px 24px",
fontSize: "1.1rem",
border: "none",
borderRadius: "5px",
cursor: "pointer",
transition: "background-color 0.3s, transform 0.3s",
outline: "none",
boxShadow: "0 2px 10px rgba(0, 0, 0, 0.1)",
};
const incrementButtonStyle = {
...buttonStyle,
backgroundColor: "#28a745",
color: "white",
};
const decrementButtonStyle = {
...buttonStyle,
backgroundColor: "#dc3545",
color: "white",
};
// Hover and active styles
const buttonHoverStyle = {
filter: "brightness(1.1)",
transform: "scale(1.05)",
};
return (
<div style={containerStyle}>
<h1 style={headingStyle}>Count: {state.count}</h1>
<button
style={incrementButtonStyle}
onMouseOver={(e) =>
(e.currentTarget.style = {
...incrementButtonStyle,
...buttonHoverStyle,
})
}
onMouseOut={(e) => (e.currentTarget.style = incrementButtonStyle)}
onClick={() => dispatch({ type: "increment" })}
>
Increment
</button>
<button
style={decrementButtonStyle}
onMouseOver={(e) =>
(e.currentTarget.style = {
...decrementButtonStyle,
...buttonHoverStyle,
})
}
onMouseOut={(e) => (e.currentTarget.style = decrementButtonStyle)}
onClick={() => dispatch({ type: "decrement" })}
>
Decrement
</button>
</div>
);
}
export default Counter;
Output:
2.Example:
import React, { useReducer, useState } from "react";
// Define the initial state
const initialState = {
todos: [],
};
// Define the reducer function
const reducer = (state, action) => {
switch (action.type) {
case "ADD_TODO":
return { ...state, todos: [...state.todos, action.payload] };
case "REMOVE_TODO":
return {
...state,
todos: state.todos.filter((todo) => todo.id !== action.payload),
};
default:
return state;
}
};
function TodoApp() {
const [state, dispatch] = useReducer(reducer, initialState);
const [inputValue, setInputValue] = useState("");
const handleAddTodo = () => {
if (inputValue.trim()) {
dispatch({
type: "ADD_TODO",
payload: { id: Date.now(), text: inputValue },
});
setInputValue(""); // Clear input field
}
};
// Internal CSS
const styles = {
container: {
maxWidth: "600px",
margin: "50px auto",
padding: "20px",
borderRadius: "10px",
boxShadow: "0 4px 20px rgba(0, 0, 0, 0.1)",
backgroundColor: "#f9f9f9",
fontFamily: "'Arial', sans-serif",
},
heading: {
textAlign: "center",
fontSize: "2.5rem",
marginBottom: "20px",
color: "#333",
},
input: {
width: "calc(100% - 50px)",
padding: "10px",
borderRadius: "5px",
border: "1px solid #ccc",
fontSize: "1rem",
marginRight: "10px",
},
button: {
padding: "10px 15px",
fontSize: "1rem",
border: "none",
borderRadius: "5px",
backgroundColor: "#28a745",
color: "white",
cursor: "pointer",
transition: "background-color 0.3s",
},
buttonRemove: {
padding: "5px 10px",
marginLeft: "10px",
fontSize: "0.9rem",
border: "none",
borderRadius: "5px",
backgroundColor: "#dc3545",
color: "white",
cursor: "pointer",
transition: "background-color 0.3s",
},
todoList: {
listStyleType: "none",
padding: 0,
marginTop: "20px",
},
todoItem: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
padding: "10px",
borderBottom: "1px solid #ccc",
backgroundColor: "#fff",
borderRadius: "5px",
marginBottom: "10px",
},
};
return (
<div style={styles.container}>
<h1 style={styles.heading}>Todo List</h1>
<div style={{ display: "flex", justifyContent: "center" }}>
<input
style={styles.input}
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="Add a new todo"
/>
<button style={styles.button} onClick={handleAddTodo}>
Add Todo
</button>
</div>
<ul style={styles.todoList}>
{state.todos.map((todo) => (
<li style={styles.todoItem} key={todo.id}>
{todo.text}
<button
style={styles.buttonRemove}
onClick={() =>
dispatch({ type: "REMOVE_TODO", payload: todo.id })
}
>
Remove
</button>
</li>
))}
</ul>
</div>
);
}
export default TodoApp;
Output:
The useReducer
Hook is an excellent addition to your React toolkit. It empowers you to handle complex state management efficiently, making your applications more robust and easier to maintain. Give it a try in your next project, and take your state management to the next level!
Posted on October 20, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.