0
0
Lập trình
Sơn Tùng Lê
Sơn Tùng Lê103931498422911686980

Quản lý Trạng thái với React Relay và Caching Thông Minh

Đăng vào 3 ngày trước

• 9 phút đọc

Giới thiệu

Quản lý trạng thái trong các ứng dụng React lớn có thể trở thành một cơn ác mộng cho các nhà phát triển. Thay vì sử dụng Redux hay Zustand, hãy khám phá cách mà React Relay với hệ thống caching thông minh giúp đơn giản hóa quy trình này. Bài viết này sẽ hướng dẫn bạn cách sử dụng React Relay để tối ưu hóa quản lý trạng thái và caching trong ứng dụng của bạn.

Vấn đề

Trong các ứng dụng React phức tạp, việc quản lý trạng thái thường gặp phải nhiều vấn đề như:

  • Gọi API dư thừa cho cùng một dữ liệu ở nhiều component khác nhau.
  • Xóa bỏ cache thủ công khi dữ liệu thay đổi.
  • Cập nhật lạc quan phức tạp thường gặp lỗi khi mutation thất bại.
  • Vấn đề hiệu suất do render lại không cần thiết.
  • Mã boilerplate cho trạng thái loading và xử lý lỗi.

Các giải pháp truyền thống như Redux yêu cầu bạn phải quản lý tất cả những điều này bằng tay, dẫn đến hàng trăm dòng mã boilerplate và các lỗi tinh vi khó theo dõi.

Giải pháp Thanh lịch

React Relay cung cấp một cache chuẩn hóa tự động quản lý trạng thái ứng dụng của bạn. Nó coi dữ liệu GraphQL của bạn như một nguồn dữ liệu duy nhất, tự động caching và cập nhật dữ liệu trên toàn bộ ứng dụng mà không cần can thiệp thủ công.

Phép màu xảy ra thông qua tích hợp DataLoadercập nhật lạc quan hoạt động mượt mà với schema GraphQL của bạn. Khi bạn cập nhật dữ liệu trong một component, Relay tự động cập nhật tất cả các component khác phụ thuộc vào dữ liệu đó.

Ví dụ mã

Hãy xây dựng một catalog sản phẩm với cập nhật theo thời gian thực. Chúng ta sẽ tạo một hệ thống nơi người dùng có thể duyệt sản phẩm, thêm chúng vào giỏ hàng và thấy được sự thay đổi ngay lập tức trên tất cả các component.

Mô hình Dữ liệu:

  • Product: id, name, price, isActive, category
  • Cart: totalPrice, items (kết nối đến sản phẩm)
  • Người dùng có thể bật/tắt trạng thái sản phẩm và thấy tổng giỏ hàng tự động cập nhật.
javascript Copy
// 1. Thiết lập Môi trường với Caching Thông minh
import { Environment, Network, RecordSource, Store } from 'relay-runtime';

const createFetchFunction = (graphqlUrl: string, getAuthToken: () => string) => {
  return async (request, variables) => {
    const response = await fetch(graphqlUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${getAuthToken()}`,
      },
      body: JSON.stringify({
        query: request.text,
        variables,
      }),
    });

    if (!response.ok) {
      throw new Error(`Yêu cầu GraphQL thất bại: ${response.statusText}`);
    }

    return response.json();
  };
};

const createEnvironment = (graphqlUrl: string, getAuthToken: () => string) => {
  const fetchFunction = createFetchFunction(graphqlUrl, getAuthToken);
  const network = Network.create(fetchFunction);
  const store = new Store(new RecordSource(), {
    gcReleaseBufferSize: 1000,
    queryCacheExpirationTime: 300000,
  });

  return new Environment({ network, store });
};

// 2. Truy vấn Thông minh với Caching Tự động
import { usePreloadedQuery, usePaginationFragment } from 'react-relay';
import { graphql } from 'relay-runtime';

const ProductList: FC<{ queryRef: any }> = ({ queryRef }) => {
  const data = usePreloadedQuery<ProductListQuery>(
    graphql`
      query ProductListQuery($first: Int!, $filters: ProductFilters) {
        products(first: $first, filters: $filters) {
          edges {
            node {
              id
              name
              price
              isActive
              ...ProductCard_product
            }
          }
          totalCount
        }
        cart {
          id
          totalPrice
        }
      }
    `,
    queryRef
  );

  const pagination = usePaginationFragment(
    graphql`
      fragment ProductList_products on Query
      @refetchable(queryName: "ProductListRefetchQuery") {
        products(first: $first, filters: $filters)
          @connection(key: "ProductList_products") {
          edges {
            node {
              id
              ...ProductCard_product
            }
          }
        }
      }
    `,
    data
  );

  return (
    <div>
      <h2>Sản phẩm ({data.products.totalCount})</h2>
      <p>Tổng Giỏ hàng: ${data.cart.totalPrice}</p>
      {data.products.edges.map(edge => (
        <ProductCard key={edge.node.id} productRef={edge.node} />
      ))}
      <button onClick={() => pagination.loadNext(10)}>
        Tải thêm
      </button>
    </div>
  );
};

// 3. Cập nhật Lạc quan với Xóa Cache Tự động
const ProductCard: FC<{ productRef: any }> = ({ productRef }) => {
  const product = useFragment(
    graphql`
      fragment ProductCard_product on Product {
        id
        name
        price
        isActive
      }
    `,
    productRef
  );

  const [toggleActive, isPending] = useMutationWithFeedback<ProductToggleMutation>(
    graphql`
      mutation ProductToggleMutation($input: ProductToggleInput!) {
        productToggle(input: $input) {
          product {
            id
            isActive
          }
          cart {
            id
            totalPrice
          }
          errors {
            message
          }
        }
      }
    `,
    {
      optimisticResponse: {
        productToggle: {
          product: {
            id: product.id,
            isActive: !product.isActive,
          },
          cart: {
            id: 'cart-1',
            totalPrice: product.isActive ? 0 : product.price,
          },
          errors: [],
        },
      },
      updater: (store, response) => {
        if (response.productToggle?.product) {
          const productRecord = store.get(product.id);
          productRecord?.setValue(response.productToggle.product.isActive, 'isActive');
        }
        if (response.productToggle?.cart) {
          const cartRecord = store.get('cart-1');
          cartRecord?.setValue(response.productToggle.cart.totalPrice, 'totalPrice');
        }
      },
    }
  );

  const handleToggle = () => {
    toggleActive({
      variables: { input: { productId: product.id } },
      onSuccess: () => console.log('Sản phẩm đã được thay đổi trạng thái thành công'),
      onError: (error) => console.error('Thay đổi trạng thái sản phẩm thất bại:', error),
    });
  };

  return (
    <div style={{ opacity: isPending ? 0.5 : 1 }}>
      <h3>{product.name} - ${product.price}</h3>
      <button onClick={handleToggle} disabled={isPending}>
        {product.isActive ? 'Vô hiệu hóa' : 'Kích hoạt'}
      </button>
    </div>
  );
};

// 4. Quản lý Kết nối cho Cuộn Vô Tận
import { ConnectionHandler, RecordProxy } from 'relay-runtime';

export function connectionUpdater({
  store,
  parentId,
  connectionName,
  edge,
  before = false,
}: {
  store: any;
  parentId: string;
  connectionName: string;
  edge: RecordProxy;
  before?: boolean;
}) {
  const parentProxy = store.get(parentId);
  const connection = ConnectionHandler.getConnection(parentProxy, connectionName);

  if (!connection) return;

  const currentOffset = connection.getValue('endCursorOffset') || 0;
  connection.setValue(Number(currentOffset) + 1, 'endCursorOffset');

  const currentCount = connection.getValue('count') || 0;
  connection.setValue(Number(currentCount) + 1, 'count');

  if (before) {
    ConnectionHandler.insertEdgeBefore(connection, edge);
  } else {
    ConnectionHandler.insertEdgeAfter(connection, edge);
  }
}

Kết luận

React Relay loại bỏ sự phức tạp trong quản lý trạng thái bằng cách coi dữ liệu GraphQL của bạn là nguồn dữ liệu duy nhất. Với caching tự động, cập nhật lạc quan và xóa cache thông minh, bạn có được quản lý trạng thái cấp doanh nghiệp với mã nguồn tối thiểu.

Điều quan trọng là dữ liệu của bạn chính là trạng thái của bạn. Thay vì phải đồng bộ hóa thủ công Redux với API của bạn, Relay tự động giữ mọi thứ được đồng bộ. Cách tiếp cận này mở rộng một cách hoàn hảo khi ứng dụng của bạn phát triển, và các tối ưu hiệu suất được tích hợp sẵn.

Bạn đã sẵn sàng từ bỏ Redux? Bắt đầu với cache chuẩn hóa của Relay và xem các vấn đề quản lý trạng thái của bạn biến mất. Tương lai của bạn sẽ cảm ơn bạn vì sự giảm thiểu phức tạp và cải thiện hiệu suất.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào