Skip to content

tanstack

常用参数

  • staleTime 重新获取数据的时间间隔 默认0
  • cacheTime 数据缓存时间
  • retry 失败重试次数 默认3次
  • refetchOnWindowsFocus
  • refetchOnReconnect
  • refetchOnMount
  • enabled

useQuery

javascript
function Todos() {
  // Access the client
  const queryClient = useQueryClient()

  // Queries
  const query = useQuery({ queryKey: ['todos'], queryFn: getTodos })

  // Mutations
  const mutation = useMutation({
    mutationFn: postTodo,
    onSuccess: () => {
      // Invalidate and refetch
      queryClient.invalidateQueries({ queryKey: ['todos'] })
    },
  })

  return (
    <div>
      <ul>
        {query.data?.map((todo) => (
          <li key={todo.id}>{todo.title}</li>
        ))}
      </ul>

      <button
        onClick={() => {
          mutation.mutate({
            id: Date.now(),
            title: 'Do Laundry',
          })
        }}
      >
        Add Todo
      </button>
    </div>
  )
}

staleTime 重新获取数据的时间间隔 默认0

javascript
	return useQuery({
		queryKey: ['transactions-by-address', address],
		queryFn: async () => {
			// combine from and to transactions
			const [txnIds, fromTxnIds] = await Promise.all([
				rpc.queryTransactionBlocks({
					filter: {
						ToAddress: address!,
					},
					options: {
						showInput: true,
						showEffects: true,
						showEvents: true,
					},
				}),
				rpc.queryTransactionBlocks({
					filter: {
						FromAddress: address!,
					},
					options: {
						showInput: true,
						showEffects: true,
						showEvents: true,
					},
				}),
			]);

			const inserted = new Map();
			const uniqueList: SuiTransactionBlockResponse[] = [];

			[...txnIds.data, ...fromTxnIds.data]
				.sort((a, b) => Number(b.timestampMs ?? 0) - Number(a.timestampMs ?? 0))
				.forEach((txb) => {
					if (inserted.get(txb.digest)) return;
					uniqueList.push(txb);
					inserted.set(txb.digest, true);
				});

			return uniqueList;
		},
		enabled: !!address,
		staleTime: 10 * 1000,
		refetchInterval,

persist

javascript
	return useQuery({
		queryKey: ['qredo', 'info', filter],
		queryFn: async () => backgroundClient.getQredoConnectionInfo(filter!),
		enabled: !!filter,
		staleTime: 0,
		refetchInterval: 1000,
		meta: { skipPersistedCache: true },
	});

select

javascript
	return useQuery({
		queryKey: [
			'get',
			'qredo',
			'transacions',
			qredoAPI,
			qredoID,
			networkName,
			activeAddress,
			filterStatus,
		],
		queryFn: () =>
			qredoAPI!.getTransactions({
				network: networkName!,
				address: activeAddress!,
			}),
		select: ({ list }) =>
			list.filter(({ status }) => !filterStatus?.length || filterStatus.includes(status)),
		enabled: !!(qredoAPI && qredoID && networkName && activeAddress && !forceDisabled),
		staleTime: 5000,
		refetchInterval: 5000,
	});

QueryClient

javascript
export const queryClient = new QueryClient({
	defaultOptions: {
		queries: {
			// Only retry once by default:
			retry: 1,
			// Default stale time to 30 seconds, which seems like a sensible tradeoff between network requests and stale data.
			staleTime: 30 * 1000,
			// Default cache time to 24 hours, so that data will remain in the cache and improve wallet loading UX.
			cacheTime: 24 * 60 * 60 * 1000,
			// Disable automatic interval fetching
			refetchInterval: 0,
			refetchIntervalInBackground: false,
			refetchOnWindowFocus: false,

			refetchOnMount: true,
		},
	},
});

Devtools

ReactQueryDevtools

useMutation

突变通常用于创建/更新/删除数据或执行副作用

javascript
function App() {
  const mutation = useMutation({
    mutationFn: (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}

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

副作用

javascript
useMutation({
  mutationFn: addTodo,
  onMutate: (variables) => {
    // A mutation is about to happen!

    // Optionally return a context containing data to use when for example rolling back
    return { id: 1 }
  },  
  onSuccess: (data, variables, context) => {
    // I will fire first
  },
  onError: (error, variables, context) => {
    // I will fire first

    // An error happened!
    console.log(`rolling back optimistic update with id ${context.id}`)
  },
  onSettled: (data, error, variables, context) => {
    // I will fire first
  },
})

mutate(todo, {
  onSuccess: (data, variables, context) => {
    // I will fire second!
  },
  onError: (error, variables, context) => {
    // I will fire second!
  },
  onSettled: (data, error, variables, context) => {
    // I will fire second!
  },
})

optimistic updates 立即更新 乐观更新

在某些情况下,我们需要在发送请求之前立即更新 UI,以提高用户体验。例如,在添加一条待办事项时,我们可以立即将它展示在列表中,而不必等待服务器返回确认信息

javascript
const queryClient = useQueryClient()

useMutation({
  mutationFn: updateTodo,
  // When mutate is called:
  onMutate: async (newTodo) => {
    // Cancel any outgoing refetches
    // (so they don't overwrite our optimistic update)
    // 取消 refetch
    await queryClient.cancelQueries({ queryKey: ['todos'] })

    // Snapshot the previous value
    // 获取之前的数据
    const previousTodos = queryClient.getQueryData(['todos'])

    // Optimistically update to the new value
    // 更新数据
    queryClient.setQueryData(['todos'], (old) => [...old, newTodo])

    // Return a context object with the snapshotted value
    return { previousTodos }
  },
  // If the mutation fails,
  // use the context returned from onMutate to roll back
  onError: (err, newTodo, context) => {
    // 回滚
    queryClient.setQueryData(['todos'], context.previousTodos)
  },
  // Always refetch after error or success:
  onSettled: () => {
    // 重新获取
    queryClient.invalidateQueries({ queryKey: ['todos'] })
  },
})

Updates from Mutation Response

javascript
const queryClient = useQueryClient()

const mutation = useMutation({
  mutationFn: editTodo,
  onSuccess: data => {
    queryClient.setQueryData(['todo', { id: 5 }], data)
  }
})

mutation.mutate({
  id: 5,
  name: 'Do the laundry',
})

// The query below will be updated with the response from the
// successful mutation
const { status, data, error } = useQuery({
  queryKey: ['todo', { id: 5 }],
  queryFn: fetchTodoById,
})

hydration

hydrate 注水 侵泡

脱水

next.js CSR SSR SSG静态站点生成

getStaticProps 打包阶段执行

next.js

如何在服务器上预取数据并将其传递给 queryClient

  • 1 自己预取数据并将其作为 initialData
  • 2 在服务器上预取查询,冻结缓存并在客户端上解除冻结
javascript
export async function getStaticProps() {
  const posts = await getPosts()
  return { props: { posts } }
}

function Posts(props) {
  const { data } = useQuery({
    queryKey: ['posts'],
    queryFn: getPosts,
    initialData: props.posts,
  })

  // ...
}

若要支持服务器上的缓存查询并设置冻结

Prefetch the query on the server, dehydrate the cache and rehydrate it on the client

https://codesandbox.io/s/react-query-next-js-hydration-wurll?file=/pages/_app.js

javascript
// _app.jsx
import {
  Hydrate,
  QueryClient,
  QueryClientProvider,
} from '@tanstack/react-query'

export default function MyApp({ Component, pageProps }) {
  const [queryClient] = React.useState(() => new QueryClient())

  return (
    <QueryClientProvider client={queryClient}>
    	// 解冻
      <Hydrate state={pageProps.dehydratedState}>
        <Component {...pageProps} />
      </Hydrate>
    </QueryClientProvider>
  )
}
javascript
import Head from "next/head";
import { QueryClient, dehydrate, useQuery } from "react-query";

function fetchIssues({ queryKey }) {
  const [issues, org, repo] = queryKey;
  return fetch(
    `https://api.github.com/repos/${org}/${repo}/issues`
  ).then((response) => response.json());
}

export async function getServerSideProps({ req, res, query }) {
  const org = "facebook";
  const repo = "react";

  const queryClient = new QueryClient();

  await queryClient.prefetchQuery(["issues", org, repo], fetchIssues);

  return {
    props: {
      // 冻结
      reactQueryData: dehydrate(queryClient),
      org,
      repo
    }
  };
}

const Issue = ({ issue }) => {
  return (
    <li>
      <h3>{issue.title}</h3>
    </li>
  );
};

export default function IndexPage({ org, repo }) {
  const issuesQuery = useQuery(["issues", org, repo], fetchIssues);
  return (
    <div>
      <Head>
        <title>{`${issuesQuery.data?.length} Issues for ${org}/${repo}`}</title>
        <meta
          name="description"
          content={`${issuesQuery.data?.length} Issues for ${org}/${repo}`}
        />
      </Head>
      <h1>
        Issues for {org}/{repo}
      </h1>
      <ul>
        {issuesQuery.data?.map((issue) => (
          <Issue key={issue.id} issue={issue} />
        ))}
      </ul>
    </div>
  );
}

更新: 2023-07-28 17:03:47
原文: https://www.yuque.com/u3641/dxlfpu/xggem509cpkw8uh4