+10

[ReactJS] React Query là gì ? Tại sao nên dùng React Query ?

rq-logo.png

Tại sao phải dùng react-query?

Các cách quản lí và phân chia local state và global state trên đây vẫn đang hoạt động tốt và tương đối phổ biến cho các dự án React hiện tại.

Nhưng có một cách phân chia tốt hơn là client state và server state. Đây là ý tưởng từ react-query, một thư viện khá hot gần đây mà ai cũng nói đến.

Trước đây khi chúng ta muốn fetch data từ server, chúng ta có thể sử dụng window.fetch hay axios, nhưng cả hai cách này đều không xử lí caching data. Bởi vì thường có một nhu cầu là chúng ta muốn cache data của server trên client (để lấy nhanh hơn khi cần, và không còn gọi API lại). Và chúng ta thường tự xử lí (phần nào khá khoai và phát sinh nhiều vấn đề) hay dùng những thư viện khác. Với react-query, nó không những fetch data và còn xử lí phần caching, đồng bộ và cập nhật data giữa client và server. Và còn nhiều nhiều lợi ích khác ở đây.

Nhờ có react query mà chúng ta mới nhìn nhận lại rằng đáng lẽ ra có sự khác nhau giữ server state và client state.

  • Server state là những dữ liệu lưu ở phía server và chúng ta dùng API để lấy về trên client (React web). Ví dụ userInfo, listItems, propertyDetail, ….
  • Client state (UI state) là những dữ liệu chỉ tồn tại ở React và không được lưu xuống server như các biến isModalOpen, isLoading, status, … Những dữ liệu này sẽ mất đi khi chúng ta reload page.

Chúng ta có thể đã sai khi kết hợp hai state này chung với nhau trên Redux hay Context. Việc quản lí, cập nhật và đồng bộ hay cache những dữ liệu từ server nên phải được tách riêng ra và xử lí tốt hơn.

Một số đặc điểm nổi bật của server state là:

  • Nó nằm ở server và bạn ko thể điều khiển trực tiếp được nó.
  • Cần một async request để lấy và cập nhật
  • Nó có thể bị thay đổi bởi một ai đó mà bạn không biết
  • Nó bị cũ (stale) hay lỗi thời (outdated) trong quá trình sử dụng app

Với react-query chúng ta sẽ quản lí server state riêng biệt và hiệu quả hơn. Caching giúp cho app chạy nhanh và mượt mà hơn. Ví dụ bạn gọi API /posts để lấy tất cả bài posts khi bắt đầu load trang lên, sau đó click vào để xem chi tiết từng bài post (detail page). Khi bạn nhấn nút Back để trở lại trang posts thì mặc định bạn sẽ thấy data đã có sẵn từ cache và người dùng sẽ thấy data ngay lập tức (không cần chờ gọi API như sử dụng axios hay window.fetch). Điều đặc biệt là ngay lúc này react-query cũng gọi API ngầm bên dưới (background refetch) để update lại các bài posts tự động cho bạn

Tóm lại một số lợi ích của react-query

  • Caching data cho API
  • Hạn chế gọi nhiều request trùng nhau
  • Tự động cập nhật data của API bên dưới, giúp data luôn mới và đồng bộ với server
  • Phân trang và lazy loading
  • Điều khiển được data khi nó bị cũ, có thể gọi lại dễ dàng
  • Giúp tăng trải nghiệm UX cho web app với “instant” data

Hãy thử bắt đầu với React-Query

Các lợi ích chính mà React-Query mang lại cho chúng ta:

  • Window focus refetching: khi người dùng rời khỏi tab ứng dụng của bạn, React Query sẽ đánh dấu dữ liệu là "cũ" và tìm hiểu lại dữ liệu đó khi người đó quay lại.
  • Request retry: bạn có thể đặt số lần thử lại cho bất kỳ yêu cầu nào để chống lại các lỗi ngẫu nhiên.
  • Prefetching: nếu ứng dụng của bạn cần dữ liệu mới sau khi có yêu cầu cập nhật, bạn có thể tìm nạp trước truy vấn bằng một khóa cụ thể và React Query sẽ cập nhật nó trong nền.
  • Optimize Updates: khi bạn chỉnh sửa hoặc xóa một mục trong danh sách, bạn có thể đưa ra bản cập nhật lạc quan cho danh sách.

Dưới đây sẽ là cấu hình cơ bản để bắt đầu sử dụng React-Query

// App.js

import { QueryClient, QueryClientProvider } from 'react-query'

const queryClient = new QueryClient()

<QueryClientProvider client={queryClient}>
  <App />
</QueryClientProvider>

Tiếp tục, giả sử chúng ta có một hàm fetching sử dụng Axios để lấy dữ liệu các bài viết

// core/services/fetchingPosts.js

async function fetchingPosts() {
	const res = await axios.get("/api/posts");
	
	return res.data;
}

Việc kết hợp hàm tìm nạp dữ liệu của chúng ta với React-Query sẽ vơ cùng đơn giản

import React from 'react'
import { useQuery } from 'react-query'
import fetchingPosts from 'src/core/services/fetchingPosts.js'

export const Articles = () => {
  const { data, error, isError, isLoading } = useQuery(['articles'], fetchingPosts)

  if (isLoading) {
    return <span>Đang tải...</span>
  }

  if (isError) {
    return <span>Have an errors: {error.message}</span>
  }

  return (
    <ul>
      {data.map(article => (
        <li key={article.id}>{article.title}</li>
      ))}
    </ul>
  )
}

Bằng cách gọi hook React-query và lấy ra những biến ràng buộc dữ liệu mà React-Query đã quy định trước đó, nó hoàn toàn dễ dàng sử dụng và dễ tiếp cận đối với chúng ta.

Tìm nạp dữ liệu hiện đã tốt hơn

Tại sao nó lại tốt hơn so với cách tìm nạp dữ liệu thông thường với useEffect . Khi chúng ta truy vấn với cùng một key đã định nghĩ ở useQuery, React-Query ngay lập tức trả về dữ liệu đã được fetch trước đó và tìm nạp dữ liệu mới.

Khi tập dữ liệu thứ hai giống với tập đầu tiên, Truy vấn React sẽ giữ cả hai làm tham chiếu mà không buộc tải lại. Đó là một cải tiến to lớn đối với trải nghiệm người dùng.

Làm thế nào để Hooks hoạt động

Bạn đã biết cách tìm nạp dữ liệu dễ dàng với React-Query rồi. Bây giờ hãy tìm hiểu làm sao để cập nhật nó.

async function fetchingPosts(id) {
  await axios.post(`/api/posts/${id}`)
}

export const Articles = () => {
  const [title, setTitle] = React.useState('')
  const {isLoading, isError, error, mutate} = useMutation(fetchingPosts)

  return (
    <div>
      <input
        value={title}
        onChange={(e) => setTitle(e.target.value)}
        disabled={isLoading}
      />
      <button
        onClick={() => {
          mutate({ title })
        }}
        disabled={isLoading || !title}
      >
        Add Article
      </button>
      <div>
        {isLoading
          ? "Saving..."
          : isError
          ? error.message
          : "Saved!"}
      </div>
    </div>
  )
}

React Query có hook useMutation mà bạn có thể sử dụng để cập nhật / tạo / xóa dữ liệu. useMutation cung cấp cho bạn quyền truy cập vào hàm mutate mà chúng ta có thể chuyển các đối số cần thiết. Sau đó nó trả về thông tin về trạng thái của lệnh gọi API của chúng ta. Trạng thái có thể là:

  • idle: cho trạng thái nhàn rỗi hoặc mới / đặt lại
  • Loading: cho một đột biến hiện đang chạy,
  • error: Khi xuất hiện lỗi
  • success: Khi mọi thứ đã hoàn thành và dữ liệu đã sẵn sàng.

Bạn có thể truy cập thông tin trạng thái từ biến trạng thái hoặc đối với những người thích trạng thái boolean, họ có thể truy cập thông qua các biến sau.

  • isIdle
  • isLoading
  • isError
  • isSuccess

Như bạn thấy, nó rất dễ sử dụng. Và có nhiều sự lựa chọn cho bạn khi sử dụng useMutation React Query có thể trở thành một trong những công cụ dành cho nhà phát triển mạnh mẽ nhất của bạn.

Sức mạnh thực sự còn nằm ở phía sau?

Điều gì xảy ra khi một thiết bị ngoại tuyến trong giây lát khi đang gửi dữ liệu? React Query có một giải pháp cho điều đó!

Use Request Retry

Bạn có thể chuyển tùy chọn try với số lần query sẽ thử lại đột biến sau khi kết nối lại.

const mutation = useMutation(addArticle, { retry: 3 })

Query Client helps a lot with caching

Hàm được đóng gói kèm theo nhiều phương thức giúp bạn xử lý bộ nhớ đệm cache.

  • invalidateQueries phương thức đánh dấu một truy vấn có khóa đã cho là không hợp lệ để làm cho Truy vấn React tìm nạp lại dữ liệu đó. Bạn có thể sử dụng phương pháp đó trong hook useMutation sau khi cập nhật thành công (ví dụ bên dưới).
  • setQueryData được sử dụng cho việc cải thiện cập nhật dữ liệu đã lưu vào bộ nhớ đệm
  • prefetchQuery phương thức giúp bạn tìm nạp lại một số dữ liệu trước khi cần và rendered với useQuery. Nếu bạn biết khi nào người dùng cần dữ liệu đó, sử dụng phương thức này là một cách tìm nạp dữ liệu cải thiện nhiều về trải nghiệm người dùng.
  • clear đơn giản là xoá tất cả các cache đã lưu

Để sử dụng những phương thức này, bạn sẽ cần import useQueryClient hook từ React-Query. Sau đó, assign nó vào một biến dưới dạng const queryClient = useQueryClient và gọi các phương thức bên trong nó bằng cách gọi một object thông thường.

useMutation hook options

Trong hầu hết các trường hợp, bạn sẽ sử dụng các phương thức Query Client bên trong hooks options. Hãy cùng điểm qua các nó nhé

  • onMutate Chức năng này hoạt động trước useMutation. Nó khá hữu ích khi bạn muốn chạy các bạn cập nhật trên bộ nhớ cache cục bộ và cập nhật dữ liệu cho giao diện người dùng trước khi mutation xảy ra trên máy chủ

  • onSuccess Hàm chạy khi gọi mutation thành công. Truy vấn với key đã định trước và sẽ được tìm nạp lại dưới nền ứng dụng của bạn. Có thể xem ví dụ dưới

const mutation = useMutation(createPostArticle, {
  onSuccess: data => {
    queryClient.invalidateQueries('articles')
  },
})
  • onError Sẽ xảy ra khi quá trình gọi mutation có lỗi. Việc đặt bộ nhớ cache thành dữ liệu trước đó rất phổ biến khi có sự cố xảy ra

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.