Skip to content

Latest commit

 

History

History
388 lines (307 loc) · 11.4 KB

README-zh_CN.md

File metadata and controls

388 lines (307 loc) · 11.4 KB

react-query-kit

🕊️ 一个用于 ReactQuery 的工具包,它能使 ReactQuery 更易复用和类型安全

Latest build Latest published version Bundlephobia Dependency count 0 Types included License Number of downloads GitHub Stars


Motivation

  • 使 queryKeyqueryFn 强相关
  • 以类型安全的方式管理 queryKey
  • 快速生成自定义 ReactQuery 钩子
  • queryClient 的操作更清楚地关联到哪个自定义 ReactQuery 钩子
  • 为自定义 ReactQuery 钩子设置默认选项更容易和更清晰

react-query-kit.gif

English | 简体中文

Table of Contents

Installation

$ npm i react-query-kit
# or
$ yarn add react-query-kit

Examples

createQuery

Usage

import { QueryClient, dehydrate } from '@tanstack/react-query'
import { createQuery } from 'react-query-kit'

type Response = { title: string; content: string }
type Variables = { id: number }

const usePost = createQuery<Response, Variables, Error>({
  primaryKey: '/posts',
  queryFn: ({ queryKey: [primaryKey, variables] }) => {
    // primaryKey 相等于 '/posts'
    return fetch(`${primaryKey}/${variables.id}`).then(res => res.json())
  },
  // 如果你只想在有id时且没有数据时请求,可以这么设置
  enabled: (data, variables) => !data && variables.id,
  suspense: true
})

// 你也可以使用以下的语法来创建自定义hook
// const usePost = createQuery<Response, Variables, Error>(
//   '/posts',
//   ({ queryKey: [primaryKey, variables] }) => {
//     // primaryKey 相等于 '/posts'
//     return fetch(`${primaryKey}/${variables.id}`).then(res => res.json())
//   },
//   {
//     // if u only wanna fetch once
//     enabled: (data) => !data,
//     suspense: true
//   }
// )


const variables = { id: 1 }

// example
export default function Page() {
  // queryKey 相等于 ['/posts', { id: 1 }]
  const { data } = usePost({ variables, suspense: true })

  return (
    <div>
      <div>{data?.title}</div>
      <div>{data?.content}</div>
    </div>
  )
}

console.log(usePost.getKey()) //  ['/posts']
console.log(usePost.getKey(variables)) //  ['/posts', { id: 1 }]

// nextjs 例子
export async function getStaticProps() {
  const queryClient = new QueryClient()

  await queryClient.prefetchQuery(usePost.getKey(variables), usePost.queryFn)

  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  }
}

// 在 react 组件外使用
const data = await queryClient.fetchQuery(
  usePost.getKey(variables),
  usePost.queryFn
)

// useQueries 例子
const queries = useQueries({
  queries: [
    { queryKey: usePost.getKey(variables), queryFn: usePost.queryFn },
    { queryKey: useProjects.getKey(), queryFn: useProjects.queryFn },
  ],
})

// setQueryData
queryClient.setQueryData(usePost.getKey(variables), {...})

额外的API文档

Options

  • primaryKey: string
    • 必填
    • primaryKey 将是 queryKey 数组的第一个元素
  • enabled: boolean | ((data: TData, variables: TVariables) => boolean)
    • Optional
    • 将此设置为 false 以禁用此查询自动运行。
    • 如果设置为函数,该函数将使用最新数据执行以计算布尔值

Expose Methods

  • getPrimaryKey: () => primaryKey
  • getKey: (variables: TVariables) => [primaryKey, variables]
  • queryFn: QueryFunction<TFnData, [primaryKey, TVariables]>

Returns

  • setData: (updater: Updater<TData>, options?: SetDataOptions) => TData | undefined
    • 它的参数与 queryClient.setQueryData 类似,但不需要传入 queryKey

createInfiniteQuery

Usage

import { QueryClient, dehydrate } from '@tanstack/react-query'
import { createInfiniteQuery } from 'react-query-kit'

type Data = { projects: { id: string; name: string }[]; nextCursor: number }
type Variables = { active: boolean }

const useProjects = createInfiniteQuery<Data, Variables, Error>({
  primaryKey: 'projects',
  queryFn: ({ queryKey: [_primaryKey, variables], pageParam = 1 }) => {
    return fetch(`/projects?cursor=${pageParam}?active=${variables.active}`).then(res => res.json())
  },
  getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
})

const variables = { active: true }

// example
export default function Page() {
  // queryKey equals to ['projects', { active: true }]
  const { data, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage } =
    useProjects({ variables, suspense: true })

  return (
    <div>
      {data.pages.map((group, i) => (
        <React.Fragment key={i}>
          {group.projects.map(project => (
            <p key={project.id}>{project.name}</p>
          ))}
        </React.Fragment>
      ))}
      <div>
        <button
          onClick={() => fetchNextPage()}
          disabled={!hasNextPage || isFetchingNextPage}
        >
          {isFetchingNextPage
            ? 'Loading more...'
            : hasNextPage
            ? 'Load More'
            : 'Nothing more to load'}
        </button>
      </div>
      <div>{isFetching && !isFetchingNextPage ? 'Fetching...' : null}</div>
    </div>
  )
}

// nextjs example
export async function getStaticProps() {
  const queryClient = new QueryClient()

  await queryClient.prefetchInfiniteQuery(
    useProjects.getKey(variables),
    useProjects.queryFn
  )

  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  }
}

// 在 react 组件外使用
const data = await queryClient.fetchInfiniteQuery(
  useProjects.getKey(variables),
  useProjects.queryFn
)

额外的API文档

Options

  • primaryKey: string
    • 必填
    • primaryKey 将是 queryKey 数组的第一个元素
  • enabled: boolean | ((data: TData, variables: TVariables) => boolean)
    • Optional
    • 将此设置为 false 以禁用此查询自动运行。
    • 如果设置为函数,该函数将使用最新数据执行以计算布尔值

Expose Methods

  • getPrimaryKey: () => primaryKey
  • getKey: (variables: TVariables) => [primaryKey, variables]
  • queryFn: QueryFunction<TFnData, [primaryKey, TVariables]>

Returns

  • setData: (updater: Updater<TData>, options?: SetDataOptions) => TData | undefined
    • 它的参数与 queryClient.setQueryData 类似,但不需要传入 queryKey

createMutation

Usage

import { createMutation } from 'react-query-kit'

const useAddTodo = createMutation(
  async (variables: { title: string; content: string }) =>
    fetch('/post', {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(variables),
    }).then(res => res.json()),
  {
    onSuccess(data, variables, context) {
      // do somethings
    },
  }
)

// 你也可以使用以下的语法来创建自定义hook
// const useAddTodo = createMutation<TData, { title: string; content: string }>(
//   async (variables) =>
//     fetch('/post', {
//       method: 'POST',
//       headers: {
//         Accept: 'application/json',
//         'Content-Type': 'application/json',
//       },
//       body: JSON.stringify(variables),
//     }).then(res => res.json()),
// )

function App() {
  const mutation = useAddTodo({  
    onSettled: (data, error, variables, context) => {
        // Error or success... doesn't matter!
    }
  })

  return (
    <div>
      {mutation.isLoading ? (
        'Adding todo...'
      ) : (
        <>
          {mutation.isError ? (
            <div>An error occurred: {mutation.error.message}</div>
          ) : null}

          {mutation.isSuccess ? <div>Todo added!</div> : null}

          <button
            onClick={() => {
              mutation.mutate({  title: 'Do Laundry', content: "content..." })
            }}
          >
            Create Todo
          </button>
        </>
      )}
    </div>
  )
}

// usage outside of react component
useAddTodo.mutationFn({  title: 'Do Laundry', content: "content..." })

额外的API文档

Returns

  • getKey: () => MutationKey
  • mutationFn: MutationFunction<TData, TVariables>

类型推倒

您可以使用 inferVariablesinferData 提取任何自定义hook的 TypeScript 类型

import { inferVariables, inferData } from 'react-query-kit'

type Variables = inferVariables<typeof usePost>
type Data = inferData<typeof usePost>

Issues

Looking to contribute? Look for the Good First Issue label.

🐛 Bugs

请针对错误、缺少文档或意外行为提出问题。

See Bugs

💡 Feature Requests

请提交问题以建议新功能。 通过添加对功能请求进行投票 一个👍。 这有助于维护人员优先处理要处理的内容。

See Feature Requests

LICENSE

MIT