npm install react-query axios --save
npm install express cors morgan --save
React Query
会在内存中缓存状态QueryClientProvider
把QueryClient
实例传递给下层组件src\index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient();
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>);
src\App.js
import { useQuery } from 'react-query';
import request from './request';
function App() {
const { data } = useQuery('users', () => request.get('/users'))
return (
(<ul>
{
data?.map((user) => <li key={user.id}>{user.name}</li>)
}
</ul>)
)
}
export default App;
src\request.js
import axios from 'axios';
axios.interceptors.response.use(response => response.data);
axios.defaults.baseURL = 'http://localhost:8080';
export default axios;
const express = require('express');
const cors = require('cors');
const logger = require('morgan');
const app = express();
app.use(express.json());
app.use(logger('dev'));
app.use(cors({
allowedHeaders: ["Content-Type"],
allowMethods: ["GET", 'POST', "PUT", "DELETE", "OPTIONS"]
}));
const users=new Array(10).fill(true).map((item, index) => ({ id: String(index + 1), name: `name${index + 1}` }))
app.use((req, res, next) => {
setTimeout(next, 1000);
});
app.get('/users', (req, res) => {
res.json(users);
});
app.listen(8080, () => console.log('started on port 8080'));
字段 | 含义 | 取值 |
---|---|---|
status | 状态 | loading、error、success |
isLoading | 是否首次加载中 | true、false |
isError | 是否获取失败 | |
isSuccess | 是否获取成功 | true、false |
src\App.js
import { useQuery } from 'react-query';
import request from './request';
function App() {
+ const { data, isLoading, isError } = useQuery('users', () => {
+ throw new Error('用户列表加载失败!');
+ return request.get('/users');
})
+ if (isLoading) return <div>加载中.......</div>
+ if (isError) return <div>加载失败</div>
return (
(<ul>
{
data?.map((user) => <li key={user.id}>{user.name}</li>)
}
</ul>)
)
}
export default App;
React Query
带有专用的开发工具,它们有助于可视化React Query
的所有内部工作src\App.js
import { useQuery } from 'react-query';
+import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
+function Users() {
+ const { data, isLoading, isError } = useQuery('users', () => request.get('/users'))
+ if (isLoading) return <div>加载中.......</div>
+ if (isError) return <div>加载失败</div>
+ return (
+ (<ul>
+ {
+ data?.map((user) => <li key={user.id}>{user.name}</li>)
+ }
+ </ul>)
+ )
+}
function App() {
return (
+ <>
+ <Users />
+ <ReactQueryDevtools initialIsOpen={true} />
+ </>
)
}
export default App;
字段 | 含义 | 取值 |
---|---|---|
isFetching | 是否正在请求 | true、false |
src\App.js
import { useQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function Users() {
+ const { data, isLoading, isError, isFetching } = useQuery('users', () => request.get('/users'), {
+ refetchOnWindowFocus: true
})
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>加载失败</div>
return (
(
<>
<ul>
{
data?.map((user) => <li key={user.id}>{user.name}</li>)
}
</ul>
+ {isFetching && <div>更在更新数据...</div>}
</>
)
)
}
function App() {
return (
<>
<Users />
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
staleTime
可以理解为数据保质期,在保质期内遇到同 key
的请求,不会去再次获取数据,也就是从缓存中取,瞬间切换展示,isFetching
也一直为 false
字段 | 含义 | 取值 |
---|---|---|
isStale | 是否已经过期 | 0 立刻过期,Infinity 永不过期 |
src\App.js
import { useQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function Users() {
const { data, isLoading, isError, isFetching } = useQuery('users', () => request.get('/users'), {
refetchOnWindowFocus: true,
+ staleTime: 3000
})
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>加载失败</div>
return (
(
<>
<ul>
{
data?.map((user) => <li key={user.id}>{user.name}</li>)
}
</ul>
{isFetching && <div>更在更新数据...</div>}
</>
)
)
}
function App() {
return (
<>
<Users />
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
cacheTime
是指未使用/非活动缓存数据保留在内存中的时间(以毫秒为单位)src\App.js
import { useState } from 'react';
import { useQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function Users() {
const { data, isLoading, isError, isFetching } = useQuery('users', () => request.get('/users'), {
refetchOnWindowFocus: true,
+ staleTime: Infinity,
+ cacheTime: 5000
})
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>加载失败</div>
return (
(
<>
<ul>
{
data?.map((user) => <li key={user.id}>{user.name}</li>)
}
</ul>
{isFetching && <div>更在更新数据...</div>}
</>
)
)
}
function App() {
+ const [show, setShow] = useState(true);
return (
<>
+ <button onClick={() => setShow(!show)}>{show ? '隐藏' : '显示'}</button>
+ {show && <Users />}
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
src\App.js
import { useState } from 'react';
import { useQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function Users() {
const { data, isLoading, isError, isFetching } = useQuery('users', () => request.get('/users'), {
refetchOnWindowFocus: true,
staleTime: Infinity,
cacheTime: 5000,
+ refetchInterval: 1000
})
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>加载失败</div>
return (
(
<>
<ul>
{
data?.map((user) => <li key={user.id}>{user.name}</li>)
}
</ul>
{isFetching && <div>更在更新数据...</div>}
</>
)
)
}
function App() {
const [show, setShow] = useState(true);
return (
<>
<button onClick={() => setShow(!show)}>{show ? '隐藏' : '显示'}</button>
{show && <Users />}
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
api.js
const express = require('express');
const cors = require('cors');
const logger = require('morgan');
const app = express();
app.use(express.json());
app.use(cors({
allowedHeaders: ["Content-Type"],
allowMethods: ["GET", 'POST', "PUT", "DELETE", "OPTIONS"]
}));
app.use(logger('dev'));
const users = new Array(10).fill(true).map((item, index) => ({ id: String(index + 1), name: `name${index + 1}` }))
app.use((req, res, next) => {
setTimeout(next, 1000);
});
app.get('/users', (req, res) => {
res.json(users.map(user => ({ ...user, name: user.name + '#' + new Date().toLocaleString() })));
});
app.listen(8080, () => console.log('started on port 8080'));
src\App.js
import { useState } from 'react';
import { useQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
+function Users({ queryKey }) {
+ const { data, isLoading, isError, isFetching } = useQuery(queryKey, () => request.get('/users'))
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>加载失败</div>
return (
(
<>
<ul>
{
data?.map((user) => <li key={user.id}>{user.name}</li>)
}
</ul>
{isFetching && <div>更在更新数据...</div>}
</>
)
)
}
function App() {
return (
<>
+ <Users queryKey="users" />
+ <Users queryKey="users" />
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
src\App.js
import { useQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
+function useUsers() {
+ return useQuery('users', () => request.get('/users'));
+}
+function Stats() {
+ const { data } = useUsers();
+ return data && <h1>共计{data.length}用户</h1>
+}
function Users() {
+ const { data, isLoading, isError, isFetching } = useUsers();
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>加载失败</div>
return (
(
<>
<ul>
{
data?.map((user) => <li key={user.id}>{user.name}</li>)
}
</ul>
{isFetching && <div>更在更新数据...</div>}
</>
)
)
}
function App() {
return (
<>
<Users />
+ <Stats />
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
src\App.js
import { useEffect, useState } from 'react';
+import { useQuery, QueryObserver, useQueryClient } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function Stats() {
+ const [data, setData] = useState();
+ const queryClient = useQueryClient();
+ useEffect(() => {
+ const observer = new QueryObserver(queryClient, { queryKey: 'users' })
+ const unsubscribe = observer.subscribe(result => setData(result.data))
+ return unsubscribe;
+ }, []);
return data && <h1>共计{data.length}用户</h1>
}
function Users() {
+ const { data, isLoading, isError, isFetching } = useQuery('users', () => request.get('/users'))
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>加载失败</div>
return (
(
<>
<ul>
{
data?.map((user) => <li key={user.id}>{user.name}</li>)
}
</ul>
{isFetching && <div>更在更新数据...</div>}
</>
)
)
}
function App() {
return (
<>
<Users />
<Stats />
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
src\App.js
import React from 'react';
import { useQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
+function User({ userId }) {
+ const { data, isLoading, isError, error, isFetching } = useQuery(['user', userId], () => request.get('/user', {
+ params: {
+ userId
+ }
+ }))
+ if (isLoading) return <div>加载中.......</div>
+ if (isError) return <div>{error.message}</div>
+ return (
+ (
+ <>
+ {data.id ? <p>{data.id}:{data.name}</p> : <p>{userId}对应的用户不存在</p>}
+ {isFetching && <div>更在更新数据...</div>}
+ </>
+ )
+ )
+}
function App() {
+ const [userId, setUserId] = React.useState('');
return (
<>
+ <input value={userId} onChange={(event) => setUserId(event.target.value)} />
+ <User userId={userId} />
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
api.js
const express = require('express');
const cors = require('cors');
const logger = require('morgan');
const app = express();
app.use(express.json());
app.use(cors({
allowedHeaders: ["Content-Type"],
allowMethods: ["GET", 'POST', "PUT", "DELETE", "OPTIONS"]
}));
app.use(logger('dev'));
const users = new Array(10).fill(true).map((item, index) => ({ id: String(index + 1), name: `name${index + 1}` }))
app.use((req, res, next) => {
setTimeout(next, 1000);
});
app.get('/users', (req, res) => {
res.json(users.map(user => ({ ...user, name: user.name + '#' + new Date().toLocaleString() })));
});
+app.get('/user', (req, res) => {
+ const userId = req.query.userId;
+ const user = users.find(user => user.id === userId);
+ if (user)
+ res.json(user);
+ else
+ res.json({})
+});
app.listen(8080, () => console.log('started on port 8080'));
字段 | 含义 | 取值 |
---|---|---|
isIdle | 是否空闲 | true、false |
src\App.js
import React from 'react';
import { useQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function User({ userId }) {
const { data, isLoading, isError, error, isFetching, isIdle } = useQuery(['user', userId], () => request.get('/user', {
params: {
userId
}
+ }), { enabled: !!userId })
if (isIdle) return null;
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>{error.message}</div>
return (
(
<>
{data.id ? <p>{data.id}:{data.name}</p> : <p>{userId}对应的用户不存在</p>}
{isFetching && <div>更在更新数据...</div>}
</>
)
)
}
function App() {
const [userId, setUserId] = React.useState('');
return (
<>
<input value={userId} onChange={(event) => setUserId(event.target.value)} />
<User userId={userId} />
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
src\App.js
import React from 'react';
import { useQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function User({ userId }) {
const { data, isLoading, isError, error, isFetching, isIdle } = useQuery(['user', userId], () => request.get('/user', {
params: {
userId
}
}), {
enabled: !!userId,
+ retry: 1,
+ retryDelay: 1000,
+ retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
})
if (isIdle) return null;
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>{error.message}</div>
return (
(
<>
{data.id ? <p>{data.id}:{data.name}</p> : <p>{userId}对应的用户不存在</p>}
{isFetching && <div>更在更新数据...</div>}
</>
)
)
}
function App() {
const [userId, setUserId] = React.useState('');
return (
<>
<input value={userId} onChange={(event) => setUserId(event.target.value)} />
<User userId={userId} />
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
`
api.js
const express = require('express');
const cors = require('cors');
const logger = require('morgan');
const app = express();
app.use(express.json());
app.use(cors({
allowedHeaders: ["Content-Type"],
allowMethods: ["GET", 'POST', "PUT", "DELETE", "OPTIONS"]
}));
app.use(logger('dev'));
const users = new Array(10).fill(true).map((item, index) => ({ id: String(index + 1), name: `name${index + 1}` }))
app.use((req, res, next) => {
setTimeout(next, 1000);
});
app.get('/users', (req, res) => {
res.json(users.map(user => ({ ...user, name: user.name + '#' + new Date().toLocaleString() })));
});
app.get('/user', (req, res) => {
const userId = req.query.userId;
const user = users.find(user => user.id === userId);
if (user)
res.json(user);
else
+ res.sendStatus(404)
});
app.listen(8080, () => console.log('started on port 8080'));
CancelToken
有一个source
静态方法,调用之后返回一个对象,该对象包含一个token
属性,用于标记请求和一个cancel
方法,用于取消请求cancel
取消请求方法在调用取消请求的时候可以将取消原因message
字符串传递进去,这样请求在被取消之后会被catch
捕获,你可以在这里将取消原因打印出来或者提示给用户,比如提示用户不要频繁点击发送请求get
的cancelToken
放置在第二个参数的对象里面,post
的cancelToken
放置在第三个参数对象里面src\App.js
import React from 'react';
import { useQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
+import request, { CancelToken } from './request';
function User({ userId }) {
const { data, isLoading, isError, error, isFetching, isIdle } = useQuery(['user', userId], () => {
+ const source = CancelToken.source();
+ const promise = request.get('/user', {
+ params: { userId }, cancelToken: source.token
+ }).catch(error => {
+ if (request.isCancel(error)) {
+ console.log(error.message);
+ }
+ });
+ promise.cancel = () => source.cancel('请求被React Query取消');
+ return promise;
}, {
enabled: !!userId,
retry: 3,
retryDelay: 1000,
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
})
if (isIdle) return null;
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>{error.message}</div>
return (
(
<>
{data.id ? <p>{data.id}:{data.name}</p> : <p>{userId}对应的用户不存在</p>}
{isFetching && <div>更在更新数据...</div>}
</>
)
)
}
function App() {
const [userId, setUserId] = React.useState('');
return (
<>
<input value={userId} onChange={(event) => setUserId(event.target.value)} />
<User userId={userId} />
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
src\request.js
+import axios, { CancelToken } from 'axios';
axios.interceptors.response.use(response => response.data);
axios.defaults.baseURL = 'http://localhost:8080';
export default axios;
+export { CancelToken }
api.js
const express = require('express');
const cors = require('cors');
const logger = require('morgan');
const app = express();
app.use(express.json());
app.use(cors({
allowedHeaders: ["Content-Type"],
allowMethods: ["GET", 'POST', "PUT", "DELETE", "OPTIONS"]
}));
app.use(logger('dev'));
const users = new Array(10).fill(true).map((item, index) => ({ id: String(index + 1), name: `name${index + 1}` }))
app.use((req, res, next) => {
+ setTimeout(next, 1000 * req.query.userId);
});
app.get('/users', (req, res) => {
res.json(users.map(user => ({ ...user, name: user.name + '#' + new Date().toLocaleString() })));
});
app.get('/user', (req, res) => {
const userId = req.query.userId;
const user = users.find(user => user.id === userId);
if (user)
res.json(user);
else
res.sendStatus(404)
});
app.listen(8080, () => console.log('started on port 8080'));
src\App.js
import React from 'react';
import { useQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request, { CancelToken } from './request';
function User({ userId }) {
const { data, isLoading, isError, error, isFetching, isIdle } = useQuery(['user', userId], () => {
const source = CancelToken.source();
const promise = request.get('/user', {
params: { userId }, cancelToken: source.token
}).catch(error => {
if (request.isCancel(error)) {
console.log(error.message);
}
});
promise.cancel = () => source.cancel('请求被React Query取消');
return promise;
}, {
enabled: !!userId,
retry: 3,
retryDelay: 1000,
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
})
+ const postsResult = useQuery(['posts', data?.id],
+ () => request.get(`/posts`, { params: { userId: data?.id } }),
+ {
+ enabled: !!(data?.id)
+ });
if (isIdle) return null;
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>{error.message}</div>
return (
(
<>
{data.id ? <p>{data.id}:{data.name}</p> : <p>{userId}对应的用户不存在</p>}
{isFetching && <div>更在更新数据...</div>}
+ {postsResult.data && <p>帖子数:{postsResult.data.length}</p>}
+ <p>{postsResult.isFetching && '正在更新贴子数据...'}</p>
</>
)
)
}
function App() {
const [userId, setUserId] = React.useState('');
return (
<>
<input value={userId} onChange={(event) => setUserId(event.target.value)} />
<User userId={userId} />
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
const express = require('express');
const cors = require('cors');
const logger = require('morgan');
const app = express();
app.use(express.json());
app.use(cors({
allowedHeaders: ["Content-Type"],
allowMethods: ["GET", 'POST', "PUT", "DELETE", "OPTIONS"]
}));
app.use(logger('dev'));
const users = new Array(10).fill(true).map((item, index) => ({ id: String(index + 1), name: `name${index + 1}` }))
+const posts = new Array(10).fill(true).map((user, index) => ({ id: String(index + 1), title: `title${index + 1}`, userId: String(index + 1) }))
app.use((req, res, next) => {
setTimeout(next, 1000 * req.query.userId);
});
app.get('/users', (req, res) => {
res.json(users.map(user => ({ ...user, name: user.name + '#' + new Date().toLocaleString() })));
});
app.get('/user', (req, res) => {
const userId = req.query.userId;
const user = users.find(user => user.id === userId);
if (user)
res.json(user);
else
res.sendStatus(404)
});
+app.get('/posts', (req, res) => {
+ const userId = req.query.userId;
+ res.json(posts.filter(post => post.userId === userId));
+});
app.listen(8080, () => console.log('started on port 8080'));
src\App.js
import React from 'react';
import { useQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
const initialUser = { id: "1" }
const initialPosts = [];
function User({ userId }) {
const { data, isLoading, isError, error, isFetching, isIdle } = useQuery(['user', userId], () => request.get('/user', {
params: { userId }
}), {
enabled: !!userId,
retry: 3,
retryDelay: 1000,
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
initialData: initialUser,
initialStale: false,
staleTime: 5000
})
const postsResult = useQuery(['posts', data?.id],
() => request.get(`/posts`, { params: { userId: data?.id } }),
{
enabled: !!(data?.id),
initialData: initialPosts,
initialStale: false,
staleTime: 5000
});
if (isIdle) return null;
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>{error.message}</div>
return (
(
<>
{data.id ? <p>{data.id}:{data.name}</p> : <p>{userId}对应的用户不存在</p>}
{isFetching && <div>更在更新数据...</div>}
{postsResult.data && <p>帖子数:{postsResult.data.length}</p>}
<p>{postsResult.isFetching && '正在更新贴子数据...'}</p>
</>
)
)
}
function App() {
const [userId, setUserId] = React.useState('');
return (
<>
<input value={userId} onChange={(event) => setUserId(event.target.value)} />
<User userId={userId} />
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
src\App.js
import React from 'react';
+import { useQuery, useQueries } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function User() {
+ const [usersQuery, postsQuery] = useQueries([
+ { queryKey: ['users'], queryFn: () => request.get('/users') },
+ { queryKey: ['posts'], queryFn: () => request.get(`/posts`) },
+ ]);
return (
(
<>
+ {usersQuery.data && <p>用户数:{usersQuery.data.length}</p>}
+ {postsQuery.data && <p>帖子数:{postsQuery.data.length}</p>}
</>
)
)
}
function App() {
return (
<>
<User />
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
api.js
const express = require('express');
const cors = require('cors');
const logger = require('morgan');
const app = express();
app.use(express.json());
app.use(cors({
allowedHeaders: ["Content-Type"],
allowMethods: ["GET", 'POST', "PUT", "DELETE", "OPTIONS"]
}));
app.use(logger('dev'));
const users = new Array(10).fill(true).map((item, index) => ({ id: String(index + 1), name: `name${index + 1}` }))
const posts = new Array(10).fill(true).map((user, index) => ({ id: String(index + 1), title: `title${index + 1}`, userId: String(index + 1) }))
app.use((req, res, next) => {
setTimeout(next, 1000 * 1);
});
app.get('/users', (req, res) => {
res.json(users.map(user => ({ ...user, name: user.name + '#' + new Date().toLocaleString() })));
});
app.get('/user', (req, res) => {
const userId = req.query.userId;
const user = users.find(user => user.id === userId);
if (user)
res.json(user);
else
res.sendStatus(404)
});
app.get('/posts', (req, res) => {
+ res.json(posts);
});
app.listen(8080, () => console.log('started on port 8080'));
src\App.js
import React from 'react';
import { useQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
+function Users({ setUserId }) {
+ const usersResult = useQuery('users', () => request.get('/users'), { staleTime: 5000 });
+ if (usersResult.isLoading) {
+ return '用户列表加载中......';
+ }
+ return (
+ <>
+ <h3>用户列表</h3>
+ <ul>
+ {
+ usersResult.data?.map(user => <li key={user.id} onClick={() => setUserId(user.id)}>{user.name}</li>)
+ }
+ </ul>
+ </>
+ )
+}
+function User({ userId, setUserId }) {
+ const userResult = useQuery(['user', userId], () => request.get('/user', {
+ params: { userId }
+ }), { staleTime: 5000 });
+ if (userResult.isLoading) {
+ return '单个用户加载中......';
+ }
+ return (
+ <div>
+ <button onClick={() => setUserId(-1)}>返回</button>
+ {userResult.data && <p>ID:{userResult.data.id},NAME:{userResult.data.name}</p>}
+ </div>
+ )
+}
function App() {
+ const [userId, setUserId] = React.useState(-1);
+ return (
+ <>
+ {
+ userId > -1 ? (
+ <User userId={userId} setUserId={setUserId} />
+ ) : <Users setUserId={setUserId} />
+ }
+ <ReactQueryDevtools initialIsOpen={false} />
+ </>
+ )
}
export default App;
QueryCache
交互,而是使用QueryClient
src\App.js
import React from 'react';
+import { useQuery, useQueryClient } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function Users({ setUserId }) {
const usersResult = useQuery('users', () => request.get('/users'), { staleTime: 5000 });
if (usersResult.isLoading) {
return '用户列表加载中......';
}
return (
<>
<h3>用户列表</h3>
<ul>
{
usersResult.data?.map(user => <li key={user.id} onClick={() => setUserId(user.id)}>{user.name}</li>)
}
</ul>
</>
)
}
function User({ userId, setUserId }) {
const queryClient = useQueryClient();
const userResult = useQuery(['user', userId], () => request.get('/user', {
params: { userId }
}), {
staleTime: 5000,
+ initialData: () => queryClient.getQueryData('users')?.find(user => user.id === userId),
+ initialStable: false
});
if (userResult.isLoading) {
return '单个用户加载中......';
}
return (
<div>
<button onClick={() => setUserId(-1)}>返回</button>
{userResult.data && <p>ID:{userResult.data.id},NAME:{userResult.data.name}</p>}
</div>
)
}
function App() {
const [userId, setUserId] = React.useState(-1);
return (
<>
{
userId > -1 ? (
<User userId={userId} setUserId={setUserId} />
) : <Users setUserId={setUserId} />
}
<ReactQueryDevtools initialIsOpen={false} />
</>
)
}
export default App;
src\App.js
import React from 'react';
import { useQuery, useQueryClient } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function Users({ setUserId }) {
+ const queryClient = useQueryClient();
+ const usersResult = useQuery('users', async () => {
+ const users = await request.get('/users');
+ users.forEach(user => {
+ queryClient.setQueryData(['user', user.id], user);
+ });
+ return users;
}, { staleTime: 5000 });
if (usersResult.isLoading) {
return '用户列表加载中......';
}
return (
<>
<h3>用户列表</h3>
<ul>
{
usersResult.data?.map(user => <li key={user.id} onClick={() => setUserId(user.id)}>{user.name}</li>)
}
</ul>
</>
)
}
function User({ userId, setUserId }) {
- const queryClient = useQueryClient();
const userResult = useQuery(['user', userId], () => request.get('/user', {
params: { userId }
}), {
- staleTime: 5000,
- initialData: () => queryClient.getQueryData('users')?.find(user => user.id === userId),
- initialStable: false
});
if (userResult.isLoading) {
return '单个用户加载中......';
}
return (
<div>
<button onClick={() => setUserId(-1)}>返回</button>
{userResult.data && <p>ID:{userResult.data.id},NAME:{userResult.data.name}</p>}
</div>
)
}
function App() {
const [userId, setUserId] = React.useState(-1);
return (
<>
{
userId > -1 ? (
<User userId={userId} setUserId={setUserId} />
) : <Users setUserId={setUserId} />
}
<ReactQueryDevtools initialIsOpen={false} />
</>
)
}
export default App;
src\App.js
import React from 'react';
import { useQuery, useQueryClient } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
+import { ToastContainer, toast } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
import request from './request';
function Users({ setUserId }) {
const queryClient = useQueryClient();
const usersResult = useQuery('users', async () => {
const users = await request.get('/users');
users.forEach(user => {
queryClient.setQueryData(['user', user.id], user);
});
return users;
}, {
+ onSuccess(data) {
+ //console.log('查询成功', data);
+ toast("查询成功!")
+ },
+ onError(error) {
+ //console.log('查询失败', error);
+ toast("查询失败!")
+ },
+ onSettled(data, error) {
+ console.log('查询结束');
+ }
});
if (usersResult.isLoading) {
return '用户列表加载中......';
}
return (
<>
<h3>用户列表</h3>
<ul>
{
usersResult.data?.map(user => <li key={user.id} onClick={() => setUserId(user.id)}>{user.name}</li>)
}
</ul>
</>
)
}
function User({ userId, setUserId }) {
// const queryClient = useQueryClient();
const userResult = useQuery(['user', userId], () => request.get('/user', {
params: { userId }
}));
if (userResult.isLoading) {
return '单个用户加载中......';
}
return (
<div>
<button onClick={() => setUserId(-1)}>返回</button>
{userResult.data && <p>ID:{userResult.data.id},NAME:{userResult.data.name}</p>}
</div>
)
}
function App() {
const [userId, setUserId] = React.useState(-1);
return (
<>
{
userId > -1 ? (
<User userId={userId} setUserId={setUserId} />
) : <Users setUserId={setUserId} />
}
+ <ToastContainer />
<ReactQueryDevtools initialIsOpen={false} />
</>
)
}
export default App;
useQuery
一起呈现之前预取查询src\App.js
import React from 'react';
import { useQuery, useQueryClient } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import request from './request';
+function fetchUsers() {
+ return request.get('/users');
+}
function Users({ setUserId }) {
const usersResult = useQuery('users', fetchUsers);
if (usersResult.isLoading) {
return '用户列表加载中......';
}
return (
<>
<h3>用户列表</h3>
<ul>
{
usersResult.data?.map(user => <li key={user.id} onClick={() => setUserId(user.id)}>{user.name}</li>)
}
</ul>
</>
)
}
function App() {
+ const queryClient = useQueryClient();
+ const [show, setShow] = React.useState(false);
+ React.useEffect(() => {
+ queryClient.prefetchQuery('users', fetchUsers, { staleTime: 5000 });
+ })
return (
<>
+ <button onClick={() => setShow(!show)} onMouseOver={() =>
+ queryClient.prefetchQuery('users', fetchUsers, { staleTime: 5000 })}>show</button>
+ {
+ show && <Users />
+ }
<ReactQueryDevtools initialIsOpen={false} />
</>
)
}
export default App;
src\App.js
import React from 'react';
+import { useQuery, useQueryClient, useMutation } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function fetchUsers() {
return request.get('/users');
}
function Users({ setUserId }) {
const usersResult = useQuery('users', fetchUsers);
if (usersResult.isLoading) {
return '用户列表加载中......';
}
return (
<>
<h3>用户列表</h3>
<ul>
{
usersResult.data?.map(user => <li key={user.id} onClick={() => setUserId(user.id)}>{user.name}</li>)
}
</ul>
</>
)
}
function App() {
+ const nameRef = React.useRef();
+ const queryClient = useQueryClient();
+ const { mutate, isLoading, isError, isSuccess, error } = useMutation(
+ (values) => request.post('/users', values), {
+ onSuccess() {
+ //queryClient.invalidateQueries('users');
+ },
+ onError(error) {
+ //alert(error.response.data.message);
+ },
+ onSettled(data, error) {
+ queryClient.invalidateQueries('users');
+ }
+ });
+ const handleSubmit = (event) => {
+ event.preventDefault();
+ const name = nameRef.current.value;
+ const user = { name };
+ mutate(user);
+ }
return (
<>
<Users />
+ <form onSubmit={handleSubmit}>
+ <input ref={nameRef} />
+ <input type="submit" value={isLoading ? '保存中...' : isError ? '保存失败' : isSuccess ? "保存成功" : "保存"} />
+ </form>
+ {isError && <pre style={{ color: 'red' }}>{error.response.data.message}</pre>}
<ReactQueryDevtools initialIsOpen={false} />
</>
)
}
export default App;
api.js
const express = require('express');
const cors = require('cors');
const logger = require('morgan');
const app = express();
app.use(express.json());
app.use(cors({
allowedHeaders: ["Content-Type"],
allowMethods: ["GET", 'POST', "PUT", "DELETE", "OPTIONS"]
}));
app.use(logger('dev'));
const users = new Array(10).fill(true).map((item, index) => ({ id: String(index + 1), name: `name${index + 1}` }))
const posts = new Array(10).fill(true).map((user, index) => ({ id: String(index + 1), title: `title${index + 1}`, userId: String(index + 1) }))
app.use((req, res, next) => {
setTimeout(next, 1000 * 1);
});
app.get('/users', (req, res) => {
res.json(users.map(user => ({ ...user, name: user.name + '#' + new Date().toLocaleString() })));
});
app.get('/user', (req, res) => {
const userId = req.query.userId;
const user = users.find(user => user.id === userId);
if (user)
res.json(user);
else
res.sendStatus(404)
});
+app.post('/users', (req, res) => {
+ let user = req.body;
+ if (user.name) {
+ user.id = (users.length + 1) + '';
+ user.createdAt = new Date().toLocaleDateString();
+ users.push(user);
+ res.json(user);
+ } else {
+ res.status(400).send({ message: '用户名不能为空!' });
+ }
+});
app.get('/posts', (req, res) => {
res.json(posts);
});
app.listen(8080, () => console.log('started on port 8080'));
onMutate
函数将在突变函数被触发之前触发,并传递突变函数将接收的相同变量src\App.js
import React from 'react';
import { useQuery, useQueryClient, useMutation } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function fetchUsers() {
return request.get('/users');
}
function Users({ setUserId }) {
const usersResult = useQuery('users', fetchUsers);
if (usersResult.isLoading) {
return '用户列表加载中......';
}
return (
<>
<h3>用户列表</h3>
<ul>
{
usersResult.data?.map(user => <li key={user.id} onClick={() => setUserId(user.id)}>{user.name}</li>)
}
</ul>
</>
)
}
function App() {
const nameRef = React.useRef();
const queryClient = useQueryClient();
const { mutate:saveUser, isLoading, isError, isSuccess, error } = useMutation(
(values) => request.post('/users', values), {
+ onMutate(values) {
+ queryClient.setQueryData('users', oldUsers => [...oldUsers, { ...values, id: String(Date.now()) }]);
+ },
onSuccess() {
//queryClient.invalidateQueries('users');
},
onError(error) {
//alert(error.response.data.message);
},
onSettled(data, error) {
queryClient.invalidateQueries('users');
}
});
const handleSubmit = (event) => {
event.preventDefault();
const name = nameRef.current.value;
const user = { name };
saveUser(user);
}
return (
<>
<Users />
<form onSubmit={handleSubmit}>
<input ref={nameRef} />
<input type="submit" value={isLoading ? '保存中...' : isError ? '保存失败' : isSuccess ? "保存成功" : "保存"} />
</form>
{isError && <pre style={{ color: 'red' }}>{error.response.data.message}</pre>}
<ReactQueryDevtools initialIsOpen={false} />
</>
)
}
export default App;
const express = require('express');
const cors = require('cors');
const logger = require('morgan');
const app = express();
app.use(express.json());
app.use(cors({
allowedHeaders: ["Content-Type"],
allowMethods: ["GET", 'POST', "PUT", "DELETE", "OPTIONS"]
}));
app.use(logger('dev'));
const users = new Array(10).fill(true).map((item, index) => ({ id: String(index + 1), name: `name${index + 1}` }))
const posts = new Array(10).fill(true).map((user, index) => ({ id: String(index + 1), title: `title${index + 1}`, userId: String(index + 1) }))
app.use((req, res, next) => {
setTimeout(next, 1000 * 1);
});
app.get('/users', (req, res) => {
+ res.json(users);
});
app.get('/user', (req, res) => {
const userId = req.query.userId;
const user = users.find(user => user.id === userId);
if (user)
res.json(user);
else
res.sendStatus(404)
});
app.post('/users', (req, res) => {
let user = req.body;
if (user.name) {
user.id = (users.length + 1) + '';
user.createdAt = new Date().toLocaleDateString();
users.push(user);
res.json(user);
} else {
res.status(400).send({ message: '用户名不能为空!' });
}
});
app.get('/posts', (req, res) => {
res.json(posts);
});
app.listen(8080, () => console.log('started on port 8080'));
src\App.js
import React from 'react';
import { useQuery, useQueryClient, useMutation } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function fetchUsers() {
return request.get('/users');
}
function Users() {
const usersResult = useQuery('users', fetchUsers);
if (usersResult.isLoading) {
return '用户列表加载中......';
}
return (
<>
<h3>用户列表</h3>
<ul>
{
usersResult.data?.map(user => <li key={user.id}>{user.name}</li>)
}
</ul>
</>
)
}
function App() {
const nameRef = React.useRef();
const queryClient = useQueryClient();
+ const { mutate: saveUser, reset, isLoading, isError, isSuccess, error } = useMutation(
(values) => request.post('/users', values), {
+ retry: false,
+ onMutate(values) {
+ queryClient.cancelQueries('users');
+ const oldUsers = queryClient.getQueryData('users');
+ queryClient.setQueryData('users', oldUsers => [...oldUsers, { ...values, id: String(Date.now+)) }]);
+ //return oldUsers;
+ return () => queryClient.setQueryData('users', oldUsers)
+ },
+ onSuccess(data) {
+ queryClient.setQueryData('users', oldUsers => oldUsers.map((user, index) => {
+ if (index === oldUsers.length - 1) {
+ return data;
+ }
+ return user;
+ }));
+ //queryClient.invalidateQueries('users');
+ },
+ onError(error, values, rollback) {
+ //alert(error.response.data.message);
+ //queryClient.setQueryData('users', rollbackValue);
+ console.log(rollback);
+ rollback && rollback();
+ },
+ onSettled(data, error) {
+ queryClient.invalidateQueries('users');
+ setTimeout(() => {
+ nameRef.current.value = ''
+ reset();
+ }, 3000)
+ }
});
const handleSubmit = (event) => {
event.preventDefault();
const name = nameRef.current.value;
const user = { name };
saveUser(user);
}
return (
<>
<Users />
<form onSubmit={handleSubmit}>
<input ref={nameRef} />
<input type="submit" value={isLoading ? '保存中...' : isError ? '保存失败' : isSuccess ? "保存成功" : "保存"} />
</form>
{isError && <pre style={{ color: 'red' }}>{error.response.data.message}</pre>}
<ReactQueryDevtools initialIsOpen={false} />
</>
)
}
export default App;
src\App.js
import React from 'react';
import { useQuery, useQueryClient, useMutation } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
+function fetchUsers({ queryKey: [_, { pageNumber }] }) {
+ return request.get('/users', {
+ params: {
+ pageNumber
+ }
+ });
+}
function Users() {
+ const queryClient = useQueryClient();
+ const [pageNumber, setPageNumber] = React.useState(1);
+ const usersResult = useQuery(['users', { pageNumber }], fetchUsers, {
+ onSuccess() {
+ queryClient.prefetchQuery(['users', { pageNumber: pageNumber + 1 }], fetchUsers);
+ }
+ });
return (
<>
<h3>用户列表</h3>
<ul>
{
+ usersResult.data?.data.map(user => <li key={user.id}>{user.name}</li>)
}
</ul>
+ {<button disabled={pageNumber <= 1} onClick={() => setPageNumber(pageNumber => pageNumber - 1)}>上一页</button>}
+ <span>{pageNumber}</span>
+ {<button disabled={usersResult.data?.pageNumber >= usersResult.data?.totalPage} onClick={() => setPageNumber(pageNumber => pageNumber + 1)}>下一页</button>}
</>
)
}
function App() {
const nameRef = React.useRef();
const queryClient = useQueryClient();
const { mutate: saveUser, reset, isLoading, isError, isSuccess, error } = useMutation(
(values) => request.post('/users', values), {
retry: false,
onMutate(values) {
queryClient.cancelQueries('users');
const oldUsers = queryClient.getQueryData('users');
queryClient.setQueryData('users', oldUsers => [...oldUsers, { ...values, id: String(Date.now()) }]);
//return oldUsers;
return () => queryClient.setQueryData('users', oldUsers)
},
onSuccess(data) {
queryClient.setQueryData('users', oldUsers => oldUsers.map((user, index) => {
if (index === oldUsers.length - 1) {
return data;
}
return user;
}));
//queryClient.invalidateQueries('users');
},
onError(error, values, rollback) {
//alert(error.response.data.message);
//queryClient.setQueryData('users', rollbackValue);
console.log(rollback);
rollback && rollback();
},
onSettled(data, error) {
queryClient.invalidateQueries('users');
setTimeout(() => {
nameRef.current.value = ''
reset();
}, 3000)
}
});
const handleSubmit = (event) => {
event.preventDefault();
const name = nameRef.current.value;
const user = { name };
saveUser(user);
}
return (
<>
<Users />
<form onSubmit={handleSubmit}>
<input ref={nameRef} />
<input type="submit" value={isLoading ? '保存中...' : isError ? '保存失败' : isSuccess ? "保存成功" : "保存"} />
</form>
{isError && <pre style={{ color: 'red' }}>{error.response.data.message}</pre>}
<ReactQueryDevtools initialIsOpen={false} />
</>
)
}
export default App;
api.js
const express = require('express');
const cors = require('cors');
const logger = require('morgan');
const app = express();
app.use(express.json());
app.use(cors({
allowedHeaders: ["Content-Type"],
allowMethods: ["GET", 'POST', "PUT", "DELETE", "OPTIONS"]
}));
app.use(logger('dev'));
+const users = new Array(30).fill(true).map((item, index) => ({ id: String(index + 1), name: `name${index + 1}` }))
const posts = new Array(30).fill(true).map((user, index) => ({ id: String(index + 1), title: `title${index + 1}`, userId: String(index + 1) }))
app.use((req, res, next) => {
setTimeout(next, 1000 * 1);
});
app.get('/users', (req, res) => {
+ const pageNumber = Number(req.query.pageNumber);
+ const totalPage = Math.floor(users.length / 10);
+ const offset = (pageNumber - 1) * 10;
+ res.json({
+ data: users.slice(offset, offset + 10),
+ pageNumber,
+ totalPage
+ });
});
app.get('/user', (req, res) => {
const userId = req.query.userId;
const user = users.find(user => user.id === userId);
if (user)
res.json(user);
else
res.sendStatus(404)
});
app.post('/users', (req, res) => {
let user = req.body;
if (user.name) {
user.id = (users.length + 1) + '';
user.createdAt = new Date().toLocaleDateString();
users.push(user);
res.json(user);
} else {
res.status(400).send({ message: '用户名不能为空!' });
}
});
app.get('/posts', (req, res) => {
res.json(posts);
});
app.listen(8080, () => console.log('started on port 8080'));
src\App.js
import React from 'react';
+import { useInfiniteQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
+function fetchUsers({ pageParam = 1 }) {
+ return request.get('/users', {
+ params: {
+ pageNumber: pageParam
+ }
+ });
+}
function Users() {
+ const { data, hasNextPage, fetchNextPage } = useInfiniteQuery(['users'], fetchUsers, {
+ getNextPageParam: (lastPageData) => {
+ console.log(lastPageData);
+ return lastPageData.pageNumber < lastPageData.totalPage ? lastPageData.pageNumber + 1 : false;
+ }
+ });
return (
<>
<h3>用户列表</h3>
<ul>
+ {
+ data?.pages?.map((page, index) => {
+ return (
+ <React.Fragment key={index}>
+ {
+ page.data?.map(user => <li key={user.id}>{user.id}:{user.name}</li>)
+ }
+ </React.Fragment>
+ )
+ })
+ }
</ul>
+ <button disabled={!hasNextPage} onClick={() => fetchNextPage()}>加载更多</button>
</>
)
}
function App() {
return (
<>
<Users />
<ReactQueryDevtools initialIsOpen={false} />
</>
)
}
export default App;