When we use React to develop front-end applications, it is inevitable to make data requests, and a lot of processing needs to be performed before and after the data requests, for example
- Loading effect is displayed to tell the user that a request is being processed in the background.
- To write async-await, try-catch is wrapped each time;
- If the interface is abnormal, handle the error.
When there are many requests, this operation is repeated each time. Here we can use the React hook to encapsulate a useRequest ourselves.
Umi framework has been implemented in a useRequest method, Userequest-umi -hook, he here to achieve a lot of functions, we only achieve a basic function, other more functions, you can expand.
1. Build a basic structure
In useRequest, we wrap it based on AXIos, but we won’t expose too much of the configuration here. First, let’s clarify the inputs and outputs of useRequest.
Data to enter:
- Url: address of the interface to be requested.
- Data: Requested data (default only get mode).
- Config: other optional configurations, such as manual? : boolean; // Whether manual trigger is required);
Data to return:
- Loading: Whether the data is in the request.
- Data: indicates the data returned by the interface.
- Error: indicates an error when an interface is abnormal.
2. Concrete implementation
After we determine the format of the input and output, we can implement it concretely.
2.1 Without config Configuration
Define input and output data first:
const useRequest = (url, data, config) = > {
const [loading, setLoading] = useState(false);
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
return {
loading,
result,
error,
};
};
export default useRequest;
Copy the code
Then you can add data requests:
const useRequest = (url, data, config) = > {
const [loading, setLoading] = useState(true);
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
const request = async () => {
setLoading(true);
try {
const result = await axios({
url,
params: data,
method: 'get'});if (result && result.status >= 200 && result.status <= 304) {
setResult(result.data);
} else {
setError(new Error('get data error in useRequest')); }}catch (reason) {
setError(reason);
}
setLoading(false);
};
useEffect(() = >{ request(); } []);return {
loading,
result,
error,
};
};
Copy the code
This useRequest is ready to use without considering the third configuration, config.
const App = () = > {
const { loading, result, error } = useRequest(url);
return (
<div>
<p>loading: {loading}</p>
<p>{JSON.stringify(result)}</p>
</div>
);
};
Copy the code
2.2 Adding a Cancellation Request
In the process of requesting an interface, the component may have been destroyed before the interface returned data, so we also need to add a cancellation operation to avoid operating on components that no longer exist. For information on how AXIos cancels requests, you can check out the previous article: Axios Source Code Series on How to cancel requests.
const request = useCallback(() = > {
setLoading(true);
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios({
url,
params: data,
method: 'get'.cancelToken: source.token, // Inject the token into the request
})
.then((result) = > {
setResult(result.data);
setLoading(false);
})
.catch((thrown) = > {
// setError and setLoading are called only for non-canceled requests
// If the component is uninstalled, the method will be called
if(! axios.isCancel(thrown)) { setError(thrown); setLoading(false); }});return source;
}, [url, data]);
useEffect(() = > {
const source = request();
return () = > source.cancel('Operation canceled by the user.');
}, [request]);
Copy the code
2.3 With Config Configuration
Then, we need to consider adding the configuration. In the above call, useRequest is triggered as soon as useRequest is called. But what if the trigger is triggered under certain circumstances (for example, an interface request is generated after the user clicks)? Here we need to add another configuration.
{
"manual": false.If it is false, the request is generated immediately; if it is true, the request is generated immediately
"ready": false // The request is generated only when manual is true
}
Copy the code
Here we consider the condition in useRequest that triggers the reqeUST request:
- No config, or config, but manual is false;
- There is config and both manual and ready are true.
const useRequest = () = > {
// The rest of the code is left unchanged and ignored for now
useEffect(() = > {
if(! config || ! config.manual || (config.manual && config.ready)) {const source = request();
return () = > source.cancel('Operation canceled by the user.');
}
}, [config]);
};
Copy the code
Usage:
const App = () = > {
const [ready, setReady] = useState(false);
const { loading, result, error } = useRequest(url, null, { manual: true, ready });
return (
<div>
<p>loading: {loading}</p>
<p>{JSON.stringify(result)}</p>
<button onClick={()= >SetReady (true)}> Generates a request</button>
</div>
);
};
Copy the code
Of course, there are many ways to implement manual triggering. In this case, I use a config.ready trigger. In umi framework, it returns a run function and then executes the run function to trigger the execution of the useRequest.
const useRequest = () = > {
// The rest of the code is left unchanged and ignored for now
const [ready, setReady] = useState(false);
const run = (r: boolean) = > {
setReady(r);
};
useEffect(() = > {
if(! config || ! config.manual || (config.manual && ready)) {if (loading) {
return;
}
setLoading(true);
const source = request();
return () = > {
setLoading(false);
setReady(false);
source.cancel('Operation canceled by the user.');
};
}
}, [config, ready]);
};
Copy the code
3. Summary
The useRequest we are implementing here is just a few basic features! For larger businesses, there may be more requirements, such as rotation training, shaking prevention, etc., these functions need to be added by themselves!