19

封装一个useFetch实现页面销毁取消请求

 3 years ago
source link: https://zhuanlan.zhihu.com/p/269519055
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

前端业务经常会出现这样一类问题,当用户网速过慢或是其他特殊情况下,该页面的请求还未完成,用户就已经点击其他页面跳出去了。理想状态下请求也是应该终止掉的,所以我们应该想办法将请求和页面卸载关联在一起。

1 使用AbortController将请求终止。

文档地址:

AbortController developer.mozilla.org

代码:

通过abortController.abort();可以取消正在进行的请求

//创建AbortController对象
const abortController = new AbortController();

fetch(host + 'newsSelectContentByType?type=2', {
	// 这里传入 signal 进行关联
	signal: abortController.signal,
})
.then(response => response.json())
.then(res => {
	// 处理拿到的数据并渲染
	showData(res);
	//初始化的loading设为false
	setInitLoading(false);
})

//这里我们设置三秒后取消请求
setTimeout(()=>{
	// 这里调用 abort 即可取消请求
	abortController.abort();        
},3000)

浏览器控制台 Network 设置网络十秒延迟

JvEBjeQ.jpg!mobile 设置延迟为10s 32uiyaB.gif!mobile 请求在3s后就自动结束掉了

同理我们可以使用 useRef 来创建 AbortController 对象,同时在组件销毁时去执行方法,完成请求的取消,但是我们不可能在每一个页面都去写这么多代码,所以这里我们去封装一个useFetch hook。

新建一个 UseFetch.js

同理我们的UseFetch不仅仅可以做这一个需求,我们可以根据我们的业务需要去封装更多的功能进去,比如说请求的loading以及拼接一些公共参数或是说做一个统一的错误处理

//自定义fetchhook  封装组件卸载自动结束未完成的请求功能和loading功能
import React, { useState, useEffect , useRef } from 'react';

const useFetch = (url,args) => {
    //全局设定AbortController
    const abortController = useRef();
    //loading
    const [loading,setLoading] = useState(false);
    //结果
    const [result,setResult] = useState();

    //开启请求的方法
    const beginFetch = ()=>{
        abortController.current = new AbortController();
        //开启loading
        setLoading(true);
        //拼接参数
        let argsStr = '';
        if(args!=''){
            for(let key in args) {
                argsStr += key + '=' + args[key] + '&';
            }
            argsStr = '?' + argsStr.substr(0, argsStr.length-1);
        }
        //请求
        fetch(url+argsStr, {
            // 这里传入 signal 进行关联
            signal: abortController.current.signal,
        })
        .then(response => response.json())
        .then(response => setResult(response))
        .finally(() => setLoading(false));//无论请求成功还是失败都强制结束loading

    }

    //组件卸载
    useEffect(()=>{
        //组件清除时终止请求
        return () => {
            abortController.current.abort()
        }
    },[])
    
    return{ result,loading,beginFetch }  
}

export default useFetch;

使用 useFetch

//将之前的页面数据请求根据useFetch做修改

//result 拿到的数据  loading页面是否展示loading beiginFetch开始进行请求
const {result,loading,beginFetch} = useFetch(
        host + 'newsSelectContentByType',
        {type:2}
);

//点击加载更多 直接通过beginFetch()方法去获取数据
const onLoadMore = () => {
       //设一个loading动画 先渲染空数据
       setListData(listData.concat([...new Array(3)].map(() => ({ loading: true, name: {} ,img:[,,,]}))))
       //因为这里拿数据很快 所以做一个暂停的动画展示
       beginFetch();
};

//监控result变化 当result发生变化重写处理数据并渲染到页面上
useEffect(()=>{
        //处理数据并渲染
        showData(result);
},result)

该useFetch主要封装了loading功能和自动结束请求功能,在路由发生切换,Tab 发生切换等场景下,被卸载掉的组件发出的请求会被终止掉,同时在请求开始和结束的情况下会自动判定loading的状态。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK