Hãy cùng mình tìm hiểu use là gì cũng như những vấn đề mà không phải ai cũng biết khi sử dụng nó, cách khắc phục và code mẫu những ví dụ cụ thể 😁
Nó là first-class support Promise ở trong ReactJS
Thay vì ta sử dụng await một promise nào đó trong async function thì ta wrap cái promise đó vào use là được
“use is a new React function that accepts a promise conceptually similar to await. use handles the promise returned by a function in a way that is compatible with components, hooks, and Suspense.”
// `use` inside of a React component or Hook...
const data = use(promise);
// ...roughly equates to `await` in an async function
const data = await promise;
Điểm đặt biệt của use so với các Hooks khác là chúng ta có thể sử dụng nó trong nested conditions, blocks hoặc loops. Nó cũng đuợc tính là một Hooks đặt biệt
Thay vì sử dụng async/await chỉ ở Server Component thì ta có thể dùng use như là từ khoá await. Thay vì await cái fetch thì ta bỏ fetch và wrap lại bằng use
Nhưng cách wrap lại bằng use(fetch). Không được khuyến nghị, thay vào đó ta nên xài fetch bằng React Query hoặc SWR
Ví dụ:
"use client"
import styles from './page.module.css'
import { use } from 'react'
export default function Home() {
const pokemon = use(
fetch("https://pokeapi.co/api/v2/pokemon/ditto")
.then(data => data.json())
)
console.log(pokemon)
return (
<main className={styles.main}>
{pokemon.name}
</main>
)
}
Run sever bằng yarn dev và mở Devtools lên
Ta thấy không có điều gì cả, hoạt động rất bình thường. Nhưng khi chọn tab “Mạng” thì vòng lặp request liên tục
Vậy làm sao để khắc phục được việc này ?
Nhưng nếu chỉ muốn dùng use thôi thì sao ?
Có thể bạn sẽ nghĩ ?
Nếu ta thử viết 1 hàm dùng để fetching data ở ngoài rồi trước rồi sau đó dùng use wrap lại thay vì viết fetch trực tiếp vào use. Code sẽ như thế này
"use client"
import styles from './page.module.css'
import { use } from 'react'
async function fetchPokemon() {
const pokemon_api = await fetch("https://pokeapi.co/api/v2/pokemon/ditto").then(data => data.json())
return pokemon_api
}
export default function Home() {
const pokemon = use(fetchPokemon())
return (
<main className={styles.main}>
{
pokemon.name
}
</main>
)
}
Lúc này fetchPokemon() trả về Promise và ta sẽ dùng use để bọc nó lại. Cùng check lại kết quả ở tab “Mạng” để check xem còn bị infinity bandwidth hay không ?
Và Bùm 💥 như ta thấy, nó vẫn bị lỗi đó
Vậy còn cách nào giải quyết không ?
Có, đó chính là thêm 1 cái biến tạm để lưu cái hàm fetchPokemon() lại, sau đó dùng use rồi wrap cái biến đó lại
"use client"
import styles from './page.module.css'
import { use } from 'react'
async function fetchPokemon() {
const pokemon_api = await fetch("https://pokeapi.co/api/v2/pokemon/ditto")
.then(data => data.json())
return pokemon_api
}
const pokemonApi = fetchPokemon()
export default function Home() {
const pokemon = use(pokemonApi)
return (
<main className={styles.main}>
{
pokemon.name
}
</main>
)
}
Và đây là kết quả, chúng ta không bị dính lỗi nữa
Problem sovled. Hãy cùng refactor, code lại một hàm để tái sử dụng nào
Ý tưởng, ta sẽ dùng 1 function tổng để dùng map để lưu các Promise sau đó trả về closure gồm key và hàm callback để fetching. sau đó trong Client Component ta sẽ bọc use vào cái closure đó. Ta sẽ Generics hoá nó lên. Ta không đặt là useFetching bởi vì ta sẽ gọi nó ở ngoài Component nên không thể đặt là useFetching
export function makeFetching<T>() {
const map = new Map<string, Promise<T>>()
return (name: string, queryFn: () => Promise<T>) => {
if (!(map.has(name))) {
map.set(name, queryFn())
}
return map.get(name)!
}
}
Và đó là hàm chung để fetch, hãy cùng implement nó vào Component thôi
const pokemonFetch = makeFetching<any>() // gọi ở ngoài phạm vi Component
export default function Home() {
const ditto = use(
pokemonFetch(
"ditto",
() => fetch("https://pokeapi.co/api/v2/pokemon/ditto")
.then(data => data.json()
)
)
)
return (
<main className={styles.main}>
{
ditto.name
}
</main>
)
}
Thế là ta đã implement thành công. Hãy tạo 1 file riêng để tái sử dụng hàm makeFetching và gọi ở bất kì nơi nào bạn muốn. Ta cũng có thể lưu nhiều pokemon khác nhau dựa theo key cũng như dynamic hoá key và url
Mong rằng sau này ReactJS cũng như NextJS cập nhật để fix lại việc infinity bandwidth khi xài use
Hi vọng bài chia sẻ này hữu ích với mọi người!
Cheers 🍺
Nguồn tham khảo:
React RFC - acdlite
NextJS 13 WARNING: Easy Mistake = Infinite Loops - Jack Herrington
use in Client Components - Vercel
-
10 months ago
hay quá anh ưi