Peeps Avatar

Hello, codestus.

Go back

Tìm hiểu useOptimistic trong React

Published at: 07/06/2024

7 mins read

Nếu bạn cũng đam mê các tính năng mới của React, có lẽ bạn đã theo dõi các phiên bản thử nghiệm. Trong bài viết này, chúng ta sẽ xem xét một tính năng mới hấp dẫn chưa phải là một phần của bản phát hành chính thức: hook useOptimistic. Hook này hứa hẹn sẽ đơn giản hóa cách chúng ta xử lý các useOptimistic trong ứng dụng React, giúp chúng trở nên nhanh chóng khi phản hồi trạng thái. Hãy cùng khám phá cách sử dụng tính năng thử nghiệm này và xem xét tiềm năng của nó trong việc nâng cao trải nghiệm người dùng.

Lưu ý về useOptimistic

Lưu ý rằng, React 18.2.0 là phiên bản ổn định, và hook useOptimistic mà chúng ta sắp khám phá chưa được phát hành chính thức tại phiên bản này. Để có thể sử dụng nó dễ dàng, bạn cần nâng phiên bản React hiện tại lên phiên bản 18.3.1.

// React 18.2.0
import { experimental_useOptimistic as useOptimistic } from 'react';

// React 18.3.1
import { useOptimistic } from 'react' 

Nếu bạn sử dụng TypeScript, hãy nhớ bao gồm "react/experimental" cho phiên bản thử nghiệm trong tệp tsconfig.json của bạn để đảm bảo nhận dạng kiểu chính xác:

{
  "compilerOptions": {
    "types": ["react/experimental"]
  }
}

Hiểu Về useOptimistic

Optimistic không phải là một khái niệm mới trong phát triển web, nhưng chúng đóng vai trò quan trọng trong việc nâng cao trải nghiệm người dùng. Kỹ thuật này bao gồm việc cập nhật UI ngay lập tức với những thay đổi đã dự kiến, giả định rằng yêu cầu tới máy chủ tương ứng sẽ thành công. Điều này tạo ra cảm giác về một ứng dụng nhanh hơn và phản hồi tốt hơn.

Cách Thức Hoạt Động

Hãy tưởng tượng bạn có một danh sách các mục công việc cần làm (todo list) được lấy từ máy chủ. Khi người dùng thêm một mục mới, nó sẽ được gửi tới máy chủ - điều này mất thời gian để máy chủ xử lý và phản hồi lại. Để làm cho UI cảm giác nhanh hơn, chúng ta có thể ứng dụng optimistic khi thêm một mục mới vào danh sách, làm như thể máy chủ đã thực hiện hành động đó.Điều này có thể sẽ rất tốt, nhưng chúng ta biết rằng các yêu cầu mạng không phải lúc nào cũng đáng tin cậy. Việc xử lý các lỗi tiềm năng và đồng bộ hóa trạng thái có thể trở nên phức tạp, đây là lúc useOptimistic xuất hiện, đơn giản hóa quá trình này cho các nhà phát triển React.

Ứng dụng useOptimistic

Ví dụ này có thể được sử dụng trong cả ứng dụng SSR của NextJS, React thông thường.

Giả sử chúng ta có một thành phần TodoList chứa một danh sách các mục công việc cần làm và một input với một button để tạo một mục công việc mới được thêm vào danh sách.

Giả sử thành phần TodoList có một thuộc tính todos được cung cấp bởi dữ liệu được lấy từ máy chủ. Chúng ta triển khai useOptimistic để cập nhật UI như sau:

import { experimental_useOptimistic as useOptimistic } from 'react';
import { v4 as uuid } from 'uuid';
import { createTodo } from './actions';

type TodoItem = {
  id: string;
  item: string;
};

export function TodoList({ todos }: { todos: TodoItem[] }) {
  const [optimisticTodos, addOptimisticTodoItem] = useOptimistic<TodoItem[]>(
    todos,
    (state: TodoItem[], newTodoItem: string) => [
      ...state,
      { id: uuid(), item: newTodoItem },
    ]
  );

  return (
    <div>
      {optimisticTodos.map((todo) => (
        <div key={todo.id}>{todo.item}</div>
      ))}
      <form
        action={async (formData: FormData) => {
          const item = formData.get('item');
          addOptimisticTodoItem(item);
          await createTodo(item);
        }}
      >
        <input type="text" name="item" />
        <button type="submit">Add Item</button>
      </form>
    </div>
  );
}

Phân Tích Hook useOptimistic

Hãy xem xét hook này với các phần tử placeholder:

const [A, B] = useOptimistic<T>(C, D);
  • A là trạng thái optimistic state, mặc định là giá trị của C.
  • B là hàm gửi để gọi và thực hiện những gì chúng ta định nghĩa trong D.
  • C là dữ liệu gốc, nếu C thay đổi tức là chúng ta nhận được một giá trị mới từ máy chủ, A cũng sẽ được đặt lại với giá trị đó vì nó luôn coi C là dữ liệu cuối cùng.
  • D là sự thay đổi sẽ xảy ra với A khi B được gọi.
  • T là một thuộc tính tùy chọn (Generic Typescript ) để định nghĩa kiểu dữ liệu cho C.

Thuộc Tính

Bạn có thể tận dụng thêm hook useOptimistic bằng cách bao gồm các thuộc tính bổ sung trong sự thay đổi.

Ví dụ, giả sử chúng ta muốn có cách để chỉ ra rằng một cập nhật là lạc quan đối với người dùng. Chúng ta có thể làm điều đó bằng cách thêm thuộc tính pending: true vào cập nhật và hiển thị mục công việc với màu gray cho đến khi cập nhật thực sự xảy ra trên máy chủ.

Chúng ta có thể làm điều đó bằng cách cập nhật ví dụ ban đầu của chúng ta thành như sau:

export function TodoList({ todos }: { todos: TodoItem[] }) {
  const [optimisticTodos, addOptimisticTodoItem] = useOptimistic<TodoItem[]>(
    todos,
    (state: TodoItem[], newTodoItem: string) => [
      ...state,
      { id: uuid(), item: newTodoItem, pending: true },
    ]
  );

  return (
    <div>
      {optimisticTodos.map((todo) => (
        <div
          key={todo.id}
          style={{ color: todo.pending ? 'gray' : 'inherit' }}
        >
          {todo.item}
        </div>
      ))}
      <form
        action={async (formData: FormData) => {
          const item = formData.get('item');
          addOptimisticTodoItem(item);
          await createTodo(item);
        }}
      >
        <input type="text" name="item" />
        <button type="submit">Add Item</button>
      </form>
    </div>
  );
}

Bây giờ khi chúng ta gửi một mục công việc mới, UI của chúng ta sẽ cập nhật danh sách công việc ban đầu cộng thêm một mục công việc mới với thuộc tính pending là true. Trong phương thức render của chúng ta, mục công việc pending sẽ được hiển thị với màu xám. Khi cập nhật máy chủ đã thực hiện, thuộc tính todos chứa dữ liệu từ máy chủ sẽ thay đổi, điều này sẽ làm cho giá trị optimisticTodos cập nhật mới với nguồn dữ liệu là todos. Dữ liệu lúc này sẽ không có thuộc tính pending, vì vậy mục todo mới sẽ không còn có màu gray nữa.

Kết Luận

Hook useOptimistic mang đến một cái nhìn thú vị về tương lai của quản lý trạng thái trong các ứng dụng React. Nó nhằm mục đích đơn giản hóa việc triển khai các cập nhật UI, góp phần vào trải nghiệm người dùng nhanh hơn và phản hồi tốt hơn. Tính năng này có vẻ đặc biệt hứa hẹn khi kết hợp với các khả năng SSR của NextJS.

Nguồn tham khảo: dev.to