Reducer (Extracting State Logic) Example 2.00

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

  1. Reducer Function: Centralizes all state-changing logic (like deposits and withdrawals). It's simple and reusable.
  2. useReducer: Manages state based on the actions dispatched. It's ideal for complex state logic.
  3. Dispatch: Triggers actions (like "DEPOSIT" or "WITHDRAW") to update the state.
  4. 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! 😊