React-Query 入门

657 阅读2分钟

React-Query是什么

简单的来说这是一个适用于React Hooks 的请求库,这个库将帮助你获取、同步、更新和缓存你的远程数据, 提供两个简单的hooks,就能完成增删改查等操作,React-Query使用声明式管理服务端状态,可以使用零配置开箱即用地处理缓存,后台更新和陈旧数据。
说着可能没什么概念,直接看例子

Example 1


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

const queryClient = new QueryClient()

export const App = () => {
    return (
        <QueryClientProvider client={queryClient}>
            <Example />
        </QueryClientProvider>
    )
}

const Example = () => {
    const { isLoading, error, data } = useQuery('repoData', () =>
        fetch(
            'https://api.github.com/repos/tannerlinsley/react-query',
        ).then((res) => res.json()),
    )

    if (isLoading) return 'Loading...'

    if (error) return 'An error has occurred: ' + error.message

    return (
        <div>
            <h1>{data.name}</h1>
            <p>{data.description}</p>
            <strong>👀 {data.subscribers_count}</strong>{' '}
            <strong>✨ {data.stargazers_count}</strong>{' '}
            <strong>🍴 {data.forks_count}</strong>
        </div>
    )
}

React-Query中的Query指一个异步请求的数据源。

例子中repoData字符串就是这个query独一无二的key

可以看到,React-Query封装了完整的请求中间状态(isLoadingisError...)。

不仅如此,React-Query还为我们做了如下工作:

  • 多个组件请求同一个query时只发出一个请求

  • 缓存数据失效/更新策略(判断缓存合适失效,失效后自动请求数据)

  • 对失效数据垃圾清理

数据的CRUD由2个hook处理:

  • useQuery处理数据的查

  • useMutation处理数据的增/删/改

Example 2

github 链接
demo主要的例子放在src/App.tsx
服务端的响应主要放在server.js中
运行yarn start 会开启一个前端服务器和一个后端服务器(用于模拟请求)

import { ReactQueryDevtools} from 'react-query/devtools'
import {
    useQuery,
    useMutation,
    useQueryClient,
    QueryClient,
    QueryClientProvider,
} from 'react-query'

type User = {
    id: number;
    name: string;
}

const queryClient = new QueryClient()

function App() {
    return (
        <QueryClientProvider client={queryClient}>
            <UserPage />
            <ReactQueryDevtools initialIsOpen={false} />
        </QueryClientProvider>
    )
}

const getAllUsers = async ():Promise<{responseId:number,result:User[]}> => {
    const res =  await fetch('http://localhost:8080/getUserList')
    return res.json()
}

const findUser = async ({queryKey}:{queryKey: (string | number)[]}):Promise<{responseId:number,result:User[]}> =>{
    const [,id] = queryKey
    const res =  await fetch(`http://localhost:8080/getUserById?userId=${id}`)
    return res.json()
}

function UserPage() {

    const queryClient = useQueryClient()

    //userList,是唯一的key,用于缓存结果,避免重复请求,当mutation向后端提交数据后,queryClient.invalidateQueries('userList')会触发自动更新
    const {data,isError,isLoading,} = useQuery('userList', getAllUsers)

    //不重要:只是为了实现user Id自增,这里只是为了方便演示
    const users = data?.result || []
    const lastUserId = users[users.length-1]?.id ?? 0

    //查询第五个用户:  唯一key,也可以是数组,用于传递一些依赖,有时候我们通过id 或pageNumber查询,这样可以更好的缓存结果
    const {data:currentUser} = useQuery(['userList',5], findUser)

    // update
    const mutation = useMutation((newUser:User)=>fetch('http://localhost:8080/updateUserList',{
        method:'POST',
        body:JSON.stringify(newUser)
    }).then(res=>res.json()), {
        onSuccess: () => queryClient.invalidateQueries('userList'),
    })
 if(isLoading){
     return <div>loading....</div>
 }
    return (
        <div>
            <h1>当前用户</h1>
            <ul>

                {
                    currentUser?.result.map(item=> (
                        <li key={item.id}>{item.name}</li>
                    ))
                }
            </ul>
            <h1>用户列表</h1>
            <ul>
                {data?.result.map(todo => (
                    <li key={todo.id}>{todo.name}</li>
                ))}
            </ul>


            <button
                onClick={() => {
                    mutation.mutate({
                        id: lastUserId+1,
                        name: `Do Laundry ${lastUserId+1}`,
                    })
                }}
            >
                添加新用户
            </button>

        </div>
    )
}

使用 useQueries 的动态并行查询

function App({ users }) {
    const userQueries = useQueries(
        users.map(user => {
            return {
                queryKey: ['user', user.id],
                queryFn: () => fetchUserById(user.id),
            }
        })
    )
}

依赖查询 (enabled 选项)

// Get the user
const { data: user } = useQuery(['user', email], getUserByEmail)

const userId = user?.id

// Then get the user's projects
const { isIdle, data: projects } = useQuery(
    ['projects', userId],
    getProjectsByUser,
    {
        // The query will not execute until the userId exists
        enabled: !!userId,
    }
)

// isIdle will be `true` until `enabled` is true and the query begins to fetch.
// It will then go to the `isLoading` stage and hopefully the `isSuccess` stage :)

Query Retries

重新发起请求

import { useQuery } from 'react-query'

// Make a specific query retry a certain number of times
const result = useQuery(['todos', 1], fetchTodoListPage, {
    retry: 10, // 请求失败后会重新发起请求,最大重试次数为10
    retryDelay : 1000 ,//间隔时间
    keepPreviousData:true,//用于分页查询
})

Demo