Học viên: Phạm Thanh Huấn
Lớp: Web Frontend - React Js 29
Emai: HuanPT99@gmail.com
Link tham khảo: https://blog.openreplay.com/top-five-lightweight-state-management-libraries-for-react

Năm Thư Viện Quản Lý Trạng Thái Hàng Đầu Nhẹ Nhất Cho React

Các thư viện quản lý trạng thái cung cấp một cách có tổ chức để xử lý và cập nhật dữ liệu hoặc trạng thái hiện tại của ứng dụng của bạn. Chúng cho phép bạn theo dõi trạng thái của ứng dụng của bạn theo cách có tổ chức và cấu trúc, giúp quản lý, cập nhật và duy trì trạng thái của ứng dụng trở nên đơn giản hơn. Có nhiều tùy chọn cho React, và bài viết này sẽ tập trung vào năm thư viện hàng đầu nhẹ nhất.

Bài viết này sẽ kiểm tra và so sánh năm thư viện nhẹ nhất riêng biệt cho React để xác định lựa chọn tối ưu cho ứng dụng React của bạn.

1. React Hook Form

React Hook Form là một framework nhẹ nhàng và hiệu suất cao để xử lý trạng thái của biểu mẫu trong React. Nó tận dụng React hooks để cung cấp một API dễ hiểu để xây dựng các biểu mẫu có thể tái sử dụng với ít mã nền tảng. Sử dụng React Hook Form, bạn có thể nhanh chóng quản lý việc xác thực biểu mẫu, thông báo lỗi và logic gửi dữ liệu. Đây là một giải pháp tuyệt vời để tạo ra các biểu mẫu phức tạp trong ứng dụng React.

image1

import React from "react";
import { useForm } from "react-hook-form";

function contactForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label htmlFor="name">Name:</label>
      <input type="text" id="name" {...register("name", { required: true })} />
      {errors.name && <span className="error">This field is required</span>}

      <label htmlFor="email">Email:</label>
      <input
        type="email"
        id="email"
        {...register("email", { required: true, pattern: /^\S+@\S+$/i })}
      />
      {errors.email?.type === "required" && (
        <span className="error">This field is required</span>
      )}
      {errors.email?.type === "pattern" && (
        <span className="error">Invalid email address</span>
      )}

      <label htmlFor="message">Message:</label>
      <textarea id="message" {...register("message", { required: true })} />
      {errors.message && <span className="error">This field is required</span>}

      <button type="submit">Submit</button>
    </form>
  );
}

export default contactForm;

2. Zustand

Thư viện Zustand là một giải pháp quản lý trạng thái cơ bản nhỏ gọn, nhanh chóng và có khả năng mở rộng sử dụng nguyên tắc đơn giản hoá, và nó đã trở nên phổ biến đối với các developers React như một cách nhẹ nhàng và hiệu quả để xử lý trạng thái trong ứng dụng của họ. “Zustand” là một từ tiếng Đức có nghĩa là “state”.

Nó chứa hook API, vì vậy nó rất dễ sử dụng trong các ứng dụng React. Nó hỗ trợ các phương thức Async và dễ dàng tích hợp phần mềm trung gian bổ sung như immer, dev tools, v.v. Dễ dàng tích hợp với các thư viện quản lý trạng thái khác như Redux & React Context API, các states có thể được truy cập bên ngoài các thành phần React.

image2

Trước tiên, hãy tạo một kho lưu trữ (store). Store của bạn là một hook! Bạn tạo một bản sao mới của dữ liệu để thay đổi thay vì thay đổi dữ liệu gốc. Hàm set giúp việc cập nhật dễ dàng hơn bằng cách kết hợp các thay đổi với dữ liệu hiện có.

import create from 'zustand'

// create a store to hold our application's state
const useAppStore = create((set) => ({
  loggedInUser: null,
  notifications: [],
  setLoggedInUser: (user) => set({ loggedInUser: user }),
  addNotification: (notification) =>
    set((state) => ({ notifications: [...state.notifications, notification] })),
  clearNotifications: () => set({ notifications: [] }),
}));

Sau đó liên kết các components của bạn. Sử dụng hook ở bất cứ đâu; không cần nhà cung cấp. Chọn state của bạn và component sẽ hiển thị lại khi thay đổi.

// a component that displays the logged in user's name
function userDisplay() {
  const loggedInUser = useAppStore((state) => state.loggedInUser);
  return <h1>Welcome, {loggedInUser.name}!</h1>;
}

// a component that allows the user to log out
function logOutButton() {
  const setLoggedInUser = useAppStore((state) => state.setLoggedInUser);
  return <button onClick={() => setLoggedInUser(null)}>Logout</button>;
}

3. Recoil

Recoil là một thư viện quản lý trạng thái cho các ứng dụng React giúp bạn quản lý và chia sẻ trạng thái theo cách hiệu quả và có thể dự đoán được hơn. Nó được phát hành cho công chúng bởi một nhóm tại Facebook vào tháng 5 năm 2020 như một giải pháp thay thế cho Redux và cung cấp một cách tiếp cận đơn giản hơn, linh hoạt hơn để quản lý nhà nước.

AtomsSelectors là hai khái niệm chính trong Recoil.

Atoms là các đơn vị trạng thái rời rạc, lưu trữ các phần trạng thái trong React.

Selector là một trạng thái tạo ra được tính toán từ trạng thái hiện tại thông qua một hàm thuần túy (pure function), từ đó tính toán một giá trị mới dựa trên trạng thái đó. Nói cách khác, selector là một biểu diễn trạng thái được tạo ra từ một phép tính cụ thể được thực hiện trên trạng thái gốc.

image3

const boxState = atom({
    key: "box",
    default: {color: #000000}
});

Bạn có thể tạo, xuất và sử dụng một atom ở bất kỳ đâu trong ứng dụng của bạn. Và bạn sử dụng nó theo cách tương tự như bạn sử dụng useState. Để sử dụng atoms, bạn cần sử dụng một hook Recoil, như useRecoilState.

export const Box = ({ id }: { id: number }) => {
  const [box, setBox] = useRecoilState(boxState(id));

  return (
    <div
      className="box"
      onClick={() => {
        setBox({ color: randomColor() });
      }}
      style={{
        backgroundColor: box.color,
      }}
    />
  );
};

Recoil có thể được sử dụng để tạo ra một state mới từ một atom bằng cách sử dụng các selector. Đơn giản là lấy giá trị của các atoms mà bạn cần trong selector của bạn, và mỗi khi giá trị của những atoms đó thay đổi, selector sẽ chạy lại và các thành phần sử dụng nó sẽ được vẽ lại và hiển thị giá trị mới.

const userInfoState = selector({
  key: "userInfo",
  get: async ({ get }) => {
    //asynchronous state
    const userId = get(UserIdState);

    const info = await API.userInfo(userId);
    return info;
  },
});

Để sử dụng các selector, bạn cần sử dụng một hook Recoil, ví dụ như useRecoilValue.

const userInfo = () => {
  const userInfo = useRecoilValue(userInfoState);
};

4. React Context API

React Context API là một thư viện React tích hợp sẵn, cho phép bạn chia sẻ dữ liệu giữa các thành phần mà không cần gửi nó xuống component tree bằng cách sử dụng props thủ công. Điều này giúp đoạn code của bạn trở nên dễ đọc hơn và sạch hơn. Bạn có thể tạo một ngữ cảnh (context) với một giá trị và bất kỳ thành phần nào cần giá trị đó có thể sử dụng hook useContext để lấy nó. Context API có thể được sử dụng để lưu trữ nhiều loại dữ liệu, bao gồm chủ đề (themes), xác thực người dùng (user authentication) và thậm chí giỏ hàng mua sắm.

image4

Giả sử chúng ta đang phát triển một trang web liệt kê những bộ phim hàng đầu của người dùng. Chúng ta muốn lưu trữ danh sách phim và cung cấp nó cho tất cả các thành phần sử dụng nó. Sử dụng Context API, chúng ta có thể xây dựng một movieContext để giữ danh sách các bộ phim.

Đầu tiên, chúng ta sẽ tạo một tệp mới có tên movieContext.js. Trong tệp này, chúng tôi sẽ nhập hàm createContext từ React và sử dụng nó để tạo ngữ cảnh của chúng ta:

import { createContext } from 'react';

export const movieContext = createContext();

Tiếp theo, Chúng ta sẽ sử dụng movieProvider component với React Context API để quản lý danh sách phim. Chúng ta sẽ lưu trữ danh sách bằng useState và tìm nạp nó bằng useEffect. Điều này cho phép chúng ta truy cập danh sách trên ứng dụng mà không cần chuyển nó xuống qua component tree:

import { useState, useEffect } from "react";

export const movieProvider = ({ children }) => {
  const [movies, setMovies] = useState([]);

  useEffect(() => {
    fetch("https://api.example.com/movies")
      .then((response) => response.json())
      .then((data) => setMovies(data));
  }, []);

  return (
    <movieContext.Provider value={movies}>{children}</movieContext.Provider>
  );
};

Cuối cùng, chúng ta sẽ bao bọc toàn bộ ứng dụng của mình trong movieProvider component để làm cho trạng thái phim có thể truy cập được cho tất cả các components:

import { movieProvider } from "./movieContext";

function App() {
  return (
    <movieProvider>
      <div className="App">
        <Header />
        <movieList />
      </div>
    </movieProvider>
  );
}

Bất kỳ component nào yêu cầu quyền truy cập vào danh sách phim hiện có thể sử dụng useContext hook để truy xuất thông tin từ movieContext:

import { useContext } from "react";
import { movieContext } from "./movieContext";

export const movieList = () => {
  const movies = useContext(movieContext);

  return (
    <ul>
      {movies.map((movie) => (
        <li key={movie.id}>{movie.title}</li>
      ))}
    </ul>
  );
};

5. Redux Toolkit

Hãy nghĩ về Redux như một bộ não tập trung cho ứng dụng của bạn, nơi tất cả các quyết định và hành động quan trọng được đưa ra. Nó theo dõi trạng thái hiện tại của ứng dụng của bạn và giúp bạn dễ dàng cập nhật và truy cập trạng thái đó từ mọi nơi trong mã của bạn. Nó chạy trong các môi trường khác nhau (máy khách, máy chủ và native).

Redux Toolkit giống như có một trợ lý cá nhân chăm sóc tất cả các chi tiết nhỏ cho bạn. Nó cung cấp cho bạn các chức năng và hướng dẫn được xây dựng sẵn giúp đơn giản hóa quá trình viết mã Redux, vì vậy bạn có thể tập trung vào việc xây dựng các tính năng của ứng dụng của mình thay vì lo lắng về các chi tiết thực tế của Redux.

iamge5

Giả sử bạn đang phát triển một ứng dụng shopping và cần xử lý trạng thái của các mặt hàng trong giỏ hàng. Bạn có thể sử dụng Redux Toolkit để xác định một slice của state xử lý các mặt hàng trong giỏ hàng. Đây là một số code để giúp bạn bắt đầu:

import { createSlice } from "@reduxjs/toolkit";

const cartSlice = createSlice({
  name: "cart",
  initialState: [],
  reducers: {
    addItem: (state, action) => {
      state.push(action.payload);
    },
    removeItem: (state, action) => {
      const index = state.findIndex((item) => item.id === action.payload.id);
      if (index !== -1) {
        state.splice(index, 1);
      }
    },
  },
});

export const { addItem, removeItem } = cartSlice.actions;

export default cartSlice.reducer;

Để sử dụng slice giỏ hàng này trong Redux store của bạn, bạn sẽ import nó và thêm vào hàm combineReducers:

import { configureStore } from "@reduxjs/toolkit";
import cartReducer from "./cartSlice";

const store = configureStore({
  reducer: {
    cart: cartReducer,
  },
});

export default store;

Bây giờ, để thêm một mục vào giỏ hàng, bạn có thể gửi hành động addItem thông qua việc sử dụng dispatch:

import { useDispatch } from "react-redux";
import { addItem } from "./cartSlice";

function AddToCartButton({ item }) {
  const dispatch = useDispatch();

  const handleClick = () => {
    dispatch(addItem(item));
  };

  return <button onClick={handleClick}>Add to Cart</button>;
}

Cuối cùng, để hiển thị các mục trong giỏ hàng, bạn có thể sử dụng useSelector hook để lấy trạng thái giỏ hàng từ Redux store:

import { useSelector } from "react-redux";

function Cart() {
  const cartItems = useSelector((state) => state.cart);

  return (
    <div>
      <h2>Cart</h2>
      <ul>
        {cartItems.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

Kết luận

Mỗi dự án là duy nhất và có một tập hợp các phụ thuộc và trường hợp sử dụng kinh doanh riêng, và phù hợp với một giải pháp hơn giải pháp khác. Tất cả các phương pháp được liệt kê ở trên đều cố gắng xử lý cùng một vấn đề theo các cách khác nhau.

React Hook Form: Phù hợp để xây dựng các biểu mẫu mạnh mẽ với các tính năng như xác thực biểu mẫu tự động và xử lý lỗi. Tài liệu rõ ràng và dễ tiếp cận cho người mới bắt đầu.

Zustand: Một giải pháp quản lý trạng thái nhẹ phù hợp cho các ứng dụng nhỏ đến trung bình không yêu cầu một giải pháp đầy đủ tính năng như Redux.

Recoil: Một lựa chọn quản lý trạng thái mạnh mẽ hơn, được thiết kế để quản lý các trạng thái phức tạp trong các ứng dụng lớn hơn.

React Context API: Cung cấp hiệu suất tốt cho các ứng dụng đơn giản nhưng có thể không phù hợp với các dự án lớn hơn hoặc phức tạp hơn. Việc kiểm tra và đo hiệu suất là rất quan trọng để xác định tính phù hợp của nó.

Redux Toolkit: Cung cấp cải tiến hiệu suất thông qua các tính năng như memoized selectors và cập nhật Redux store cải thiện. Hiệu quả để xử lý trạng thái trong các ứng dụng React.

Bất kể thư viện quản lý trạng thái mà bạn chọn, bạn sẽ cần phân tích kỹ năng và nhìn chung tổng thể của đội phát triển với React.

Cảm ơn mọi người đã dành thời gian đọc bài dịch của mình!