Step-by-Step Refactor Using useReducer
Below, we'll refactor the code to use a reducer
function instead of useState
for managing the deposit and withdrawal logic. This approach centralizes the state logic, making it cleaner and easier to manage.
1. Define the Reducer Function
A reducer function will handle actions like deposit and withdraw. It takes the current state and an action as input and returns the updated state.
function userReducer(state, action) {
switch (action.type) {
case "DEPOSIT":
return { ...state, amount: state.amount + action.payload };
case "WITHDRAW":
if (state.amount - action.payload < 0) {
alert("Invalid Transaction");
return state; // Return unchanged state
}
return { ...state, amount: state.amount - action.payload };
default:
return state; // Return current state for unknown actions
}
}
2. Replace useState
with useReducer
Use useReducer
to manage the user state. It will take the userReducer
function and the initial state (from UserContext
).
import { useContext, useReducer } from "react";
import UserContext from "../contexts/UserContext";
import Deposit from "./Deposit";
import Withdraw from "./Withdraw";
export default function Main() {
const { user, setUser } = useContext(UserContext);
// Initialize `useReducer`
const [state, dispatch] = useReducer(userReducer, user);
// Update UserContext whenever state changes
React.useEffect(() => {
setUser(state);
}, [state, setUser]);
3. Handle Deposit and Withdrawal via Dispatch
Refactor the handleDepositMoney
function to dispatch actions instead of directly modifying the state.
function handleTransaction(e, type, amount) {
e.preventDefault();
const value = Number(amount);
if (type === "deposit") {
dispatch({ type: "DEPOSIT", payload: value });
} else if (type === "withdraw") {
dispatch({ type: "WITHDRAW", payload: value });
}
}
4. Pass Dispatch and States to Components
Update the Deposit
and Withdraw
components to work with dispatch
.
return (
<div>
<div className="dashboard-style">
<p>Name: {state.name}</p>
<p>
Money: <span style={{ color: "red" }}>{state.amount}</span>
</p>
</div>
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
gap: "8px",
}}
>
<Deposit handleTransaction={handleTransaction} type="deposit" />
<Withdraw handleTransaction={handleTransaction} type="withdraw" />
</div>
</div>
);
}
5. Update the Child Components
Modify Deposit
and Withdraw
to call handleTransaction
with the appropriate type.
Example: Deposit
Component
export default function Deposit({ handleTransaction }) {
const [depositAmount, setDepositAmount] = React.useState("");
return (
<form
onSubmit={(e) => {
handleTransaction(e, "deposit", depositAmount);
setDepositAmount(""); // Clear input field
}}
>
<input
type="number"
value={depositAmount}
onChange={(e) => setDepositAmount(e.target.value)}
placeholder="Deposit Amount"
/>
<button type="submit">Deposit</button>
</form>
);
}
Example: Withdraw
Component
export default function Withdraw({ handleTransaction }) {
const [withdrawAmount, setWithdrawAmount] = React.useState("");
return (
<form
onSubmit={(e) => {
handleTransaction(e, "withdraw", withdrawAmount);
setWithdrawAmount(""); // Clear input field
}}
>
<input
type="number"
value={withdrawAmount}
onChange={(e) => setWithdrawAmount(e.target.value)}
placeholder="Withdraw Amount"
/>
<button type="submit">Withdraw</button>
</form>
);
}
Final Explanation for Beginners
- Reducer Function: Centralizes all state-changing logic (like deposits and withdrawals). It's simple and reusable.
- useReducer: Manages state based on the actions dispatched. It's ideal for complex state logic.
- Dispatch: Triggers actions (like "DEPOSIT" or "WITHDRAW") to update the state.
- Clean Code: State logic is isolated in one place (the reducer), making the code easier to read and maintain.
Let me know if you need further clarification! 😊