ReactJS - Sử dụng useReducer

Xin chào các bạn đang học lập trình! Hôm nay, chúng ta sẽ bắt đầu một hành trình thú vị vào thế giới của React hooks, cụ thể là tập trung vào hook mạnh mẽ useReducer. Đừng lo lắng nếu bạn mới bắt đầu học lập trình - tôi sẽ hướng dẫn bạn từng bước, giống như tôi đã làm cho hàng trăm học sinh trong những năm dạy học của mình. Hãy cùng nhau vào sâu hơn!

ReactJS - Using useReducer

useReducer là gì?

Trước khi chúng ta nhảy vào chi tiết, hãy hiểu rõ useReducer là gì. Hãy tưởng tượng bạn đang chơi một trò chơi video mà nhân vật của bạn có nhiều trạng thái khác nhau - sức khỏe, sức mạnh và tốc độ. Khi bạn chơi, các trạng thái này thay đổi dựa trên các hành động của bạn. useReducer giống như engine của trò chơi quản lý các thay đổi trạng thái dựa trên các quy tắc cụ thể.

Trong thuật ngữ React, useReducer là một hook giúp chúng ta quản lý logic trạng thái phức tạp trong các ứng dụng của mình. Nó đặc biệt hữu ích khi bạn có nhiều giá trị con trong trạng thái của mình, hoặc khi trạng thái tiếp theo phụ thuộc vào trạng thái trước đó.

Định dạng của hook useReducer

Bây giờ, hãy xem cách chúng ta thực sự sử dụng useReducer trong mã của mình. Dưới đây là cấu trúc cơ bản:

const [state, dispatch] = useReducer(reducer, initialState);

Hãy phân tích này:

  • state: Đây là trạng thái hiện tại của chúng ta, giống như trạng thái sức khỏe hiện tại của nhân vật trong ví dụ trò chơi.
  • dispatch: Đây là một hàm chúng ta sử dụng để gửi các hành động để cập nhật trạng thái.
  • reducer: Đây là một hàm xác định trạng thái của chúng ta nên thay đổi như thế nào đối với các hành động.
  • initialState: Đây là trạng thái bắt đầu của ứng dụng của chúng ta.

Nó có thể trông hơi rối rắm bây giờ, nhưng đừng lo lắng! Chúng ta sẽ thấy điều này trong thực tế và nó sẽ trở nên rõ ràng.

Áp dụng hook reducer

Hãy tạo một ứng dụng đếm đơn giản để hiểu cách useReducer hoạt động. Chúng ta sẽ bắt đầu với một cấu hình cơ bản và sau đó phát triển nó.

import React, { useReducer } from 'react';

// Bước 1: Định nghĩa trạng thái ban đầu
const initialState = { count: 0 };

// Bước 2: Tạo hàm reducer
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

// Bước 3: Tạo thành phần Counter
function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      Đếm: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  );
}

export default Counter;

Hãy phân tích từng bước:

  1. Chúng ta import useReducer từ React.
  2. Chúng ta định nghĩa initialState với giá trị đếm là 0.
  3. Chúng ta tạo một hàm reducer rằng nhận trạng thái hiện tại và một hành động, và trả về trạng thái mới dựa trên loại hành động.
  4. Trong thành phần Counter, chúng ta sử dụng useReducer để lấy trạng thái hiện tại và hàm dispatch.
  5. Chúng ta hiển thị giá trị đếm hiện tại và hai nút để dispatch hành động 'increment' và 'decrement' khi được nhấn.

Khi bạn nhấn nút '+', nó dispatch hành động 'increment', mà reducer của chúng ta xử lý bằng cách tăng giá trị đếm. Nút '-' hoạt động tương tự để giảm giá trị đếm.

Sử dụng useReducer

Bây giờ chúng ta đã thấy một ví dụ cơ bản, hãy khám phá một tình huống phức tạp hơn. Hãy tưởng tượng chúng ta đang xây dựng một ứng dụng quản lý任务 đơn giản. Chúng ta sẽ sử dụng useReducer để xử lý việc thêm và xóa các task.

import React, { useReducer, useState } from 'react';

// Bước 1: Định nghĩa trạng thái ban đầu
const initialState = { tasks: [] };

// Bước 2: Tạo hàm reducer
function reducer(state, action) {
  switch (action.type) {
    case 'ADD_TASK':
      return { tasks: [...state.tasks, action.payload] };
    case 'REMOVE_TASK':
      return { tasks: state.tasks.filter((task, index) => index !== action.payload) };
    default:
      return state;
  }
}

// Bước 3: Tạo thành phần TaskManager
function TaskManager() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const [newTask, setNewTask] = useState('');

  const handleAddTask = () => {
    if (newTask.trim()) {
      dispatch({ type: 'ADD_TASK', payload: newTask });
      setNewTask('');
    }
  };

  const handleRemoveTask = (index) => {
    dispatch({ type: 'REMOVE_TASK', payload: index });
  };

  return (
    <div>
      <input 
        value={newTask} 
        onChange={(e) => setNewTask(e.target.value)} 
        placeholder="Nhập một task mới"
      />
      <button onClick={handleAddTask}>Thêm Task</button>
      <ul>
        {state.tasks.map((task, index) => (
          <li key={index}>
            {task}
            <button onClick={() => handleRemoveTask(index)}>Xóa</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default TaskManager;

Trong ví dụ phức tạp hơn này:

  1. Trạng thái ban đầu của chúng ta bây giờ có một mảng các task.
  2. Hàm reducer xử lý hai loại hành động: 'ADD_TASK' và 'REMOVE_TASK'.
  3. Trong thành phần TaskManager, chúng ta sử dụng cả useReducer để quản lý task và useState để xử lý trường nhập liệu.
  4. Chúng ta có các hàm để xử lý việc thêm và xóa task, dispatch các hành động tương ứng.
  5. Chúng ta hiển thị một trường nhập liệu cho task mới, một nút để thêm task, và một danh sách các task hiện tại với các nút xóa.

Ví dụ này cho thấy cách useReducer có thể giúp quản lý logic trạng thái phức tạp trong một cách rõ ràng và tổ chức.

Bảng tóm tắt phương pháp

Dưới đây là bảng tóm tắt các phương pháp và khái niệm chúng ta đã覆盖:

Phương pháp/Khái niệm Mô tả Ví dụ
useReducer Một hook React để quản lý logic trạng thái phức tạp const [state, dispatch] = useReducer(reducer, initialState);
reducer Một hàm xác định cách trạng thái nên thay đổi đối với các hành động function reducer(state, action) { ... }
dispatch Một hàm sử dụng để gửi các hành động để cập nhật trạng thái dispatch({ type: 'ADD_TASK', payload: newTask })
hành động Một đối tượng mô tả thay đổi nào cần được thực hiện trên trạng thái { type: 'INCREMENT' }
initialState Trạng thái bắt đầu của ứng dụng const initialState = { count: 0 };

Nhớ rằng việc học cách sử dụng useReducer hiệu quả đòi hỏi sự thực hành. Đừng nản lòng nếu nó không ngay lập tức trở nên rõ ràng - ngay cả các nhà phát triển có kinh nghiệm đôi khi cũng cần thời gian để nắm bắt các khái niệm mới. Tiếp tục lập mã, tiếp tục thử nghiệm, và quan trọng nhất, hãy vui vẻ! Trước khi bạn biết, bạn sẽ quản lý trạng thái phức tạp như một chuyên gia. Chúc các bạn lập mã vui vẻ!

Credits: Image by storyset