检查点:使用外部包
用 npm 安装并在页面中使用第三方依赖。
3 天
Checkpoint: 外部包集成
你将完成什么
在 Todo 应用里集成第三方包:
- 用
axios替代原生fetch - 用
react-query管理服务端状态 - 用
date-fns格式化日期 - 用
react-hot-toast做消息提示
验收标准
- 用 npm 安装并配置第三方包
- 理解包的文档,正确使用 API
- 处理包的加载状态和错误
核心代码
1. Axios 替代 fetch
npm install axios
// api.js
import axios from 'axios';
const api = axios.create({
baseURL: 'http://localhost:3000/api',
timeout: 10000
});
api.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
api.interceptors.response.use(
response => response.data,
error => {
if (error.response?.status === 401) {
localStorage.removeItem('token');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
export const login = (username, password) =>
api.post('/login', { username, password });
export const getTodos = () =>
api.get('/todos');
export const createTodo = (text) =>
api.post('/todos', { text });
export const updateTodo = (id, data) =>
api.put(`/todos/${id}`, data);
export const deleteTodo = (id) =>
api.delete(`/todos/${id}`);
2. React Query 管理状态
npm install @tanstack/react-query
// main.jsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
ReactDOM.createRoot(document.getElementById('root')).render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
);
// TodoPage.jsx
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { getTodos, createTodo, deleteTodo } from './api';
function TodoPage() {
const queryClient = useQueryClient();
const [text, setText] = useState('');
// 查询
const { data, isLoading } = useQuery({
queryKey: ['todos'],
queryFn: getTodos
});
// 创建
const createMutation = useMutation({
mutationFn: createTodo,
onSuccess: () => {
queryClient.invalidateQueries(['todos']);
setText('');
}
});
// 删除
const deleteMutation = useMutation({
mutationFn: deleteTodo,
onSuccess: () => {
queryClient.invalidateQueries(['todos']);
}
});
if (isLoading) return <p>加载中...</p>;
return (
<div>
<h1>我的 Todo</h1>
<form onSubmit={(e) => {
e.preventDefault();
createMutation.mutate(text);
}}>
<input value={text} onChange={e => setText(e.target.value)} />
<button type="submit" disabled={createMutation.isLoading}>
{createMutation.isLoading ? '提交中...' : '添加'}
</button>
</form>
<ul>
{data?.data?.map(todo => (
<li key={todo.id}>
<span>{todo.text}</span>
<button onClick={() => deleteMutation.mutate(todo.id)}>
删除
</button>
</li>
))}
</ul>
</div>
);
}