- 首先实现自己的API Client
- 无论大小项目,先创建自己的API Client,之后所有请求都通过这个Client发出去
- 可以在API Client中对需要连接的服务端做一些通用的配置和处理,比如Token、URL、错误处理等
// axios相对于fetch,更加方便,语义化的API
import axios from 'axios'
// 定义相关的endpoint
const endPoints = {
test: "https://..../",
prod: "https://.../"
}
// 创建axios的实例
const instance = axios.create({
baseURL: endPoints.test,
timeout: 3000,
// 为所有请求设置通用的header
headers: { Authorization: "Bear mytoken" }
})
// 通过axios定义拦截器预处理所有请求
instance.interceptors.response.use(
(res) => {
// 请求成功的一些逻辑
return res
},
(err) => {
if (err.response.status === 403) {
// 统一处理未授权请求,跳转到登录页面
document.location = '/login'
}
return Promise.reject(err)
}
)
export default instance
- 使用Hooks思考异步请求
Hooks的一个本质,就是万物皆可钩,即我们可以把任何一个数据源变成React组件中可以绑定的一个数据源。
对于get类型的API,我们完全可以将它看作一个远程的资源。只是和本地数据不同的地方在于,它有三个状态:
- Data:指的是请求成功后服务器返回的数据;
- Error:请求失败的话,错误信息将放到Error状态里;
- Pending:请求发出去,在返回前会处于Pending状态。
将获取文章的 API 封装成一个远程资源Hook
import { useState, useEffect } from 'react'
import apiClient from './apiClient'
const useArticle = (id) => {
const [data,setDate] = useState(null)
const [loading,setLoading] = useState(false)
const [error,setError] = useState(null)
useEffect(() => {
setLoading(true)
setError(null)
setData(null)
apiClient.get(`/getArticle/${id}`)
.then((res) => {
setData(res.data)
setLoading(false)
})
.catch((err) => {
setError(err)
setLoading(false)
})
},[id])
// 将三个状态作为 Hook 的返回值
// 即远程数据的三个状态
reuturn {
loading,
error,
data
}
}
那么要显示一篇文章时,就不再是一个具体的API调用了,而可以把它看作一个远程数据。
import useArticle from './useArticle'
const ArticleView = ({ id }) => {
const { data, loading, error } = useArticle(id)
if (error) return 'Failed'
if (!data || loading) return 'Loading...'
return (
<div>
// ...
</div>
)
}
在项目中,可以把每一个Get请求都做成这样一个Hook。数据请求和处理逻辑都放到Hooks中,从而实现Model和View的隔离。
为什么不提供一个通用的Hook,然后把API地址传进去?
其实也可以,但单独写是为了保证每个Hook自身足够简单,一般来说,为了让服务器的返回数据满足UI上的展现要求,通常需要进一步处理。
- 多个API调用:如何处理并发或串行请求?
比如作为一个完整的页面,就需要发送三个请求:
- 获取文章内容
- 获取作者信息,包括名字和头像的地址
- 获取文章的评论列表
这三个请求同时包含了并发和串行的场景:文章内容和文章评论列表可以并发请求,通过同一个文章id。而作者信息需要文章信息返回后,根据文章信息去请求作者信息,是一个串行的场景。
传统的思路去实现的话:
const [article, comments] = await Promise.all([
fetchArticle(articleId),
fetchComments(articleId)
])
const author = await fetchAuthor(article.userId)
跟上面不同的是,在useEffect里加入是否存在的判断
那么,在文章的展示页面