Learning React - useReducer #6 | 青训营笔记

103 阅读3分钟

📝 Notes for Learning React - useReducer #6

  • 😀 Contents are about useReducer hook from React

  • 🚨 It is written only for myself (It may have no reference value)

  • 🔗 Source code in my GitHub

🪝 useReducer - React Hook that lets you add a reducer to your component.

👇🏻 useReducer - Intro

Why do we use useReducer?

  • Sometimes there are many states to be updated spread across in many event handlers in our components that will get the code very overwhelming.
  • For solving these kind of cases, we will consolidate all the state update logic outside our component in a single function, called a reducer.

Here are some scenarios where we might use useReducer (from chatGPT):

  1. Managing state with complex or deeply nested data structures: useReducer can be useful when we need to update state based on multiple sub-values or when we need to perform more complex operations on the state data.
  2. Handling state transitions that depend on previous state: useReducer allows us to handle state transitions that depend on the current state in a more consistent and predictable way.
  3. Managing multiple actions with a single reducer: If we have multiple actions that can modify the same state, we can use useReducer to manage all of these actions with a single reducer function, which can simplify our code and make it easier to reason about.
  4. Improving performance by avoiding unnecessary re-renders: In some cases, using useReducer instead of useState can improve performance by avoiding unnecessary re-renders of components that are subscribed to the state updates.

We are using useState here for coming up with the useReducer:

// index.js

import React, { useState, useReducer } from 'react';
import Modal from './Modal';
import { data } from '../../../data';
// reducer function

const Index = () => {
  const [name, setName] = useState('');
  const [people, setPeople] = useState(data);
  const [showModal, setShowModal] = useState(false);
  const handleSubmit = e => {
    e.preventDefault();

    if (name) {
      setShowModal(true);
      setPeople([...people, { id: new Date().getTime().toString(), name }]);
      setName('');
    } else {
      setShowModal(true);
    }
  };

  return (
    <>
      {/* &&: For showing the component */}
      {showModal && <Modal />}
      <form className="form" onSubmit={handleSubmit}>
        <div>
          <input
            type="text"
            value={name}
            onChange={e => setName(e.target.value)}
          />
        </div>
        <button type="submit">add </button>
      </form>
      {people.map(person => {
        return (
          <div key={person.id}>
            <h4>{person.name}</h4>
          </div>
        );
      })}
    </>
  );
};

export default Index;

👇🏻 useReducer - Refractor

Steps 1 and 2 are referenced from React.dev

Reducers are a different way to handle state. We can migrate from useState to useReducer in three steps:

1. Move from setting state to dispatching actions.

Instead of telling React “what to do” by setting state, we specify "what the user just did" by dispatching "actions" from your event handlers.

The object in dispatch is called an "action"

function handleDeleteTask(taskId) {
  dispatch({
    // "action" object:
    type: 'deleted', // Choose a name that says what happened!
    id: taskId,
  });
 }

🌻 Note

This dispatch() function will tell the reducer() what is the type that we are going to operate with our state. And dispatch() will forward 2 parameters to reducer() which one is type and another one is payload(data)

2. Write a reducer function.

A reducer function is where we will put our state logic. It takes two arguments, the current state and the action object, and it returns next state.

React will set the state to what you return from the reducer.

function yourReducer(state, action) {
  // return next state for React to set
}
3. Use the reducer from our component.
import React, { useState, useReducer } from 'react';
import Modal from './Modal';
import { data } from '../../../data';

const defaultState = {
  people: [],
  isModalOpen: false,
  modalContent: '',
};

const reducer = (state, action) => {
  if (action.type === 'ADD_ITEM') {
    const newPeople = [...state.people, action.payload];
    return {
      ...state,
      people: newPeople,
      isModalOpen: true,
      modalContent: 'Item added',
    };
  }
  // We still wanna return our state here
  if (action.type === 'NO_VALUE') {
    return {
      ...state,
      isModalOpen: true,
      modalContent: 'Please enter value',
    };
  }
  throw new Error('no matching action type');
};

const Index = () => {
  const [state, dispatch] = useReducer(reducer, defaultState);
  const [name, setName] = useState('');

  const handleSubmit = e => {
    e.preventDefault();

    if (name) {
      // We have `setName()` to assign the e.target.value to name
      const newItem = { id: new Date().getTime().toString(), name };
      // Name convention
      dispatch({ type: 'ADD_ITEM', payload: newItem });
      setName('');
    } else {
      dispatch({ type: 'NO_VALUE' });
    }
  };

  return (
    <>
      {state.isModalOpen && <Modal modalContent={state.modalContent} />}
      <form className="form" onSubmit={handleSubmit}>
        <div>
          <input
            type="text"
            value={name}
            onChange={e => setName(e.target.value)}
          />
        </div>
        <button type="submit">add </button>
      </form>
      {state.people.map(person => {
        return (
          <div key={person.id}>
            <h4>{person.name}</h4>
          </div>
        );
      })}
    </>
  );
};

export default Index;

✨ Recap the steps for using useReducer

  1. Determine the initial state: Before using useReducer, we need to determine the initial state of our component. This should be an object that represents the starting values of all the state variables we need to manage.
  2. Create the reducer function: The reducer function takes in the current state and an action object and returns the new state. It's important to keep the reducer function pure, meaning it should not modify the original state object, but instead return a new object that represents the updated state.
  3. Dispatch actions to update state: Once we have the reducer function and initial state, we can use the useReducer hook to create a state object and a dispatch function. We can use the dispatch function to send action objects to the reducer function, which will update the state accordingly.
  4. Use the state object in our component: We can then use the state object to render our component based on the current state. We can also pass the dispatch function down to child components as needed.

✉️ Contact me anyway if I get somethings wrong here.