- First, implement your own API Client
- Regardless of the size of the project, first create your own API Client, and then send all requests through this Client
- You can do some general configuration and processing for the server to be connected in the API Client, such as Token, URL, error handling, etc
// axios is a more convenient and semantic API than fetch import axios from 'axios' // Define related endpoint s const endPoints = { test: "https://..../", prod: "https://.../" } // Create an instance of axios const instance = axios.create({ baseURL: endPoints.test, timeout: 3000, // Set a common header for all requests headers: { Authorization: "Bear mytoken" } }) // All requests are preprocessed through the axios definition interceptor instance.interceptors.response.use( (res) => { // Some logic of successful request return res }, (err) => { if (err.response.status === 403) { // Uniformly handle unauthorized requests and jump to the login page document.location = '/login' } return Promise.reject(err) } ) export default instance
- Using Hooks to think about asynchronous requests
The essence of Hooks is that everything can be hooked, that is, we can turn any data source into a data source that can be bound in the React component.
For the API of get type, we can treat it as a remote resource. The only difference from local data is that it has three states:
- Data: refers to the data returned by the server after the request is successful;
- Error: if the request fails, the error information will be put into the error status;
- Pending: the request will be in pending status before returning.
Encapsulate the API for getting articles into a remote resource 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]) // Take the three states as the return value of Hook // Three states of remote data reuturn { loading, error, data } }
When you want to display an article, it is no longer a specific API call, but can be regarded as a remote data.
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> ) }
In the project, each Get request can be made into such a Hook. The data request and processing logic are put into Hooks to realize the isolation of Model and View.
Why not provide a general Hook and pass in the API address?
In fact, it is OK, but it is written separately to ensure that each Hook is simple enough. Generally speaking, in order to make the returned data of the server meet the presentation requirements on the UI, it usually needs further processing.
- Multiple API calls: how to handle concurrent or serial requests?
For example, as a complete page, you need to send three requests:
- Get article content
- Get author information, including name and avatar address
- Get a list of comments for an article
These three requests contain both concurrent and serial scenarios: article content and article comment list can be requested concurrently through the same article id. The author information needs to request the author information according to the article information after the article information is returned. It is a serial scene.
If it is realized with traditional ideas:
const [article, comments] = await Promise.all([ fetchArticle(articleId), fetchComments(articleId) ]) const author = await fetchAuthor(article.userId)
Different from the above, the judgment of existence is added to useEffect
So, on the display page of the article