Full Stack Development

useReducer

by Tomas Trescak t.trescak@westernsydney.edu.au

useReducer

Introduction

Feature useState useReducer
Complexity Ideal for simple state logic Suitable for complex state logic
State Updates Directly set state via updater Use a reducer function to determine state changes
Structure Single piece of state State + Dispatch + Reducer Function
Scalability Can become messy with multiple states Organizes updates into a single function

useEffect

/

Example

      import React, { render, useState } from "react";

const CounterWithState = () => {
  const [count, setCount] = useState(0);
  const [word, setWord] = useState("");

  function changeHandler(e) {
    setWord(e.currentTarget.value);
    setCount(e.currentTarget.value.length);
  }
  
  function del() {
    if (word.length) {
      setWord(word.slice(0, -1));
      setCount(count - 1);
    }
  }
  
  function reset() {
    setWord("");
    setCount(0)
  }

  return (
    <div>
      <h1>Counter with useState</h1>
      <p>Letters: {count}</p>
      <input value={word} onChange={changeHandler} />
      <button onClick={del}>Delete</button>
      <button onClick={reset}>Reset</button>
    </div>
  );
};

render(<CounterWithState />)
    

useReducer

/

Example

      import React, { render, useReducer } from "react";

type State = {
  word: string,
  count: number
}

type Action = 
  | { type: 'WORD', value: string }
  | { type: 'DELETE' }
  | { type: 'RESET' }
  
const reducer = (state: State, action: Action) => {
  switch (action.type) {
    case "WORD":
      return { 
        count: action.value.length, 
        word: action.value 
      };
    case "DELETE":
      return state.word.length 
        ? { 
            count: state.count - 1, 
            word: state.word.slice(0, -1) 
          }
        : state;
    case "RESET":
      return { word: '', count: 0 };
    default:
      throw new Error("Action not handled");
  }
};

const CounterWithReducer = () => {
  const [state, dispatch] = useReducer(
    reducer, 
    { count: 0, word: "" }
  );

  return (
    <div>
      <h1>Counter with useState</h1>
      <p>Letters: {state.count}</p>
      <input 
        value={state.word} 
        onChange={(e) => {
          dispatch({ 
            type: 'WORD', 
            value: e.currentTarget.value
          })
        }} 
      />
      <button 
        onClick={() => dispatch({ type: 'DELETE' })}>
        Delete
      </button>
      <button 
        onClick={() => dispatch({ type: 'RESET' })}>
        Reset
      </button>
    </div>
  );
};

render(<CounterWithReducer />)
    

useReducer

vs useState

  • Cleaner state management for complex logic.
  • Centralized state transitions in a single reducer function.
  • Easier to debug and test state logic.
  • Scalability when adding more actions or state properties.