react-query là gì? Tại sao nên sử dụng react-query?

Có thể các bạn đã nghe hay thấy người khác nhắc đến thư viện react-query nhiều rồi. Tại sao phải dùng react-query khi ta có thể sử dụng useEffect để fetch data từ api? Bài viết này sẽ giải đáp thắc mắc đó. react-query là gì? Theo README của react-query Hooks for fetching, caching and

Có thể các bạn đã nghe hay thấy người khác nhắc đến thư viện react-query nhiều rồi. Tại sao phải dùng react-query khi ta có thể sử dụng useEffect để fetch data từ api? Bài viết này sẽ giải đáp thắc mắc đó.

react-query là gì?

Theo README của react-query

Hooks for fetching, caching and updating asynchronous data in React

Đơn giản thì react-query là thư viện để fetching, caching và updating dữ liệu bất đồng bộ của React.

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

Trước đây, để fetch dữ liệu từ server thì có nhiều cách. Một số cách phổ biến là sử dụng useEffectuseState hay sử dụng thư viện quản lí state (Redux).

constComponent=()=>{const[data, setData]=useState(null);useEffect(()=>{constgetData=async()=>{const response =awaitfetch("https://example.com/api").then(response=> response.json);setData(response)}getData()})return(// Some JSX here)}

Cách này hoạt động ổn, tuy nhiên lại khá là rườm rà. Phải viết 1 đống code chỉ để lấy dữ liệu từ server và hiện ra cho người dùng.

Sử dụng react-query

react-query cung cấp nhiều hooks với API cực kì đơn giản để fetch cũng như update dữ liệu từ server. Hỗ trợ sẵn caching, refetching và nhiều thứ khác.

Đầu tiên ta sẽ tìm hiểu cách để fetch dữ liệu sử dụnguseQuery

useQuery

Để sử dụng hook useQuery, ta phải truyền ít nhất 2 tham số:

  • Tham số đầu tiên là queryKey
  • Tham số thứ 2 là hàm trả về 1 promise:
    • Resolve data, hoặc
    • Throw error
  • Tham số thứ 3 là các options (ta sẽ tìm hiểu ở bên dưới).

queryKey được sử dụng để refetching, caching và chia sẻ dữ liệu giữa các component với nhau. (Ta sẽ tìm hiểu thêm với 1 số ví dụ ở bên dưới)

Sau đây là ví dụ sử dụng useQuery cơ bản.

constComponent=()=>{const{ data, isLoading, isError }=awaituseQuery('todo',()=>fetch('https://example/api/todo').then(response=> response.json()))if(isLoading){return(// Loading JSX)}if(isError){return(// Error JSX)}return(// Some JSX here)}

useQuery trả về những thông tin cần thiết như data, isLoading hay isError. (Bạn có thể xem thêm một số thông tin khác ở đây)

useMutation

useMutation khác với useQuery, được sử dụng để tạo/thay đổi/xóa dữ liệu ở. (ví dụ như đăng ký, đăng nhập).

Để sử dụng hook useMutation, ta phải truyền ít nhất 1 tham số:

  • Tham số thứ nhất là hàm trả về 1 promise:
    • Resolve data, hoặc
    • Throw error
  • Tham số thứ 2 là các options (ta sẽ tìm hiểu sau).
functionApp(){const mutation =useMutation(newTodo=>{return axios.post('/todos', newTodo)})return(<div>{mutation.isLoading ?('Adding todo...'):(<>{mutation.isError ?(<div>An error occurred: {mutation.error.message}</div>):null}{mutation.isSuccess ?<div>Todo added!</div>:null}<buttononClick={()=>{
               mutation.mutate({ id:newDate(), title:'Do Laundry'})}}>
             Create Todo
           </button></>)}</div>)}

Cũng như useQuery, useMutation trả về những thông tin cần thiết như isLoading hay isError. (Bạn có thể xem thêm một số thông tin khác ở đây).

Tuy nhiên, useMutation lại trả về mutate thay vì data (như ví dụ trên). Khi gọi hàm mutate thì hàm số đầu tiên của useMutation sẽ được gọi.

Một số ví dụ thực tế

Prefetch dữ liệu.

Mình gặp trường hợp này ở một dự án web phim, mình muốn khi user fetch dữ liệu của tập này, thì sẽ đồng thời fetch dữ liệu của tập tiếp theo, để khi nếu user bấm sang tập tiếp theo thì không phải đợi server trả về kết quả, từ đó tăng trải nghiệm người dùng lên đáng kể.

Đây là cách mình làm điều đó với react-query.

Đầu tiên, mình có một custom hook là useVideoSources, hook này sẽ trả về stream của video dựa vào ID của tập phim, tham số nhận vào sẽ là currentEpisode (tập hiện tại) và nextEpisode (tập tiếp theo).

constuseVideoSources=(currentEpisode, nextEpisode)=>{const queryClient =useQueryClient();constgetQueryKey=(episode)=>`episode-${episode.id}`;returnuseQuery(getQueryKey(currentEpisode),()=>fetchVideoSources(currentEpisode),{onSuccess:()=>{
        queryClient.prefetchQuery(getQueryKey(nextEpisode),()=>fetchSource(nextEpisode));},onError:(error)=>{
        toast.error(error.message);},});};

Ở đây bạn có thể thấy mình có truyền một object có option là onSuccessonError vào tham số thứ 3 (các options khác)

  • Option onSuccess sẽ chạy khi query thực thi thành công, mình sẽ dùng method queryClient.prefetchQuery (useQueryClient) để prefetch trước dữ liệu của tập tiếp theo. Nếu người dùng chuyển sang tập tiếp theo thì sẽ hiện dữ liệu ngay lập tức mà không cần đợi, vì đã được fetch từ trước.
  • Option onError sẽ chạy nếu query xảy ra lỗi, mình sẽ hiện toast cho người dùng biết.
Optimistic UI updates

Một trường hợp khác cũng là của web phim ở phần bình luận, khi người dùng bấm like vào một bình luận nào đó, thay vì mình đợi kết quả rồi mới hiện đã like, thì mình sẽ hiện đã like trước khi kết quả từ server được trả về.

Tìm hiểu thêm về optimistic UI updates

constuseCreateReaction=()=>{const queryClient =useQueryClient();returnuseMutation(async({ commentId })=>{if(!user)thrownewError("Please login to react");const{ error, data }=awaitlikeComment(commentId)if(error)throw error;return data;},{onMutate:({ commentId })=>{// Set dữ liệu của comment.
        queryClient.setQueryData(["comment", commentId],(comment)=>{// Gán isLiked của comment thành true
				comment.isLiked =true;return comment;});},onSettled:(_, __, params)=>{
        queryClient.invalidateQueries(["comment", params.commentId]);},onError:(error)=>{
        toast.error(error.message);},});};

Ở hook trên, ta sẽ sử dụng hook useMutation để xử lí cho trường hợp này.

  • Option useMutate sẽ được chạy trước khi mutation success hoặc error. Lúc này, ta sẽ sử dụng method queryClient.setQueryData để thay đổi dữ liệu của bình luận, đổi property isLiked của bình luận thành true. Những component đang sử dụng dữ liệu của bình luận này (useQuery) sẽ được re-render vì dữ liệu của query này đã thay đổi.
  • Option useSettled sẽ được chạy sau khi mutation thực thi xong (kể cả success hay error). Lúc này ta sẽ thực hiện invalidate lại dữ liệu của bình luận, vì khi ta thực hiện mutation sẽ có khả năng gặp lỗi nên việc invalidate sẽ nhằm chắc chắn dữ liệu đang được hiển thị là chính xác.
  • Option useError sẽ chạy khi mutation gặp lỗi. Ta sẽ hiện 1 toast để cho người dùng biết.

Kết

react-query là một thư viện rất đáng để thử, còn rất nhiều tính năng hay của react-query mà mình chưa đề cập tới, bạn có thể xem docs của react-query ở đây:

Qua đó là 1 số chia sẻ về react-query của mình, vì mình chỉ tự học thôi nên có thể bài viết này có thể có nhiều chỗ chưa chính xác, mong mọi người góp ý ở dưới phần bình luận.

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ