取消请求

React 中当前正在发出请求的组件从页面上卸载了,理想情况下这个请求也应该取消掉,那么如何把请求的取消和页面的卸载关联在一起呢?

这里要考虑利用 useEffect 传入函数的返回值:

useEffect(() => {
return () => {
// 页面卸载时执行
};
}, []);
复制代码

假设我们的请求是利用 fetch,那么还有一个需要运用的知识点:AbortController,简单看一下它的用法:

const abortController = new AbortController();

fetch(url, {
// 这里传入 signal 进行关联
signal: abortController.signal,
}); // 这里调用 abort 即可取消请求
abortController.abort();
复制代码

那么结合 React 封装一个 useFetch 的 hook:

export function useFetch = (config, deps) => {
const abortController = new AbortController()
const [loading, setLoading] = useState(false)
const [result, setResult] = useState() useEffect(() => {
setLoading(true)
fetch({
...config,
signal: abortController.signal
})
.then((res) => setResult(res))
.finally(() => setLoading(false))
}, deps) useEffect(() => {
return () => abortController.abort()
}, []) return { result, loading }
}
复制代码

那么比如在路由发生切换,Tab 发生切换等场景下,被卸载掉的组件发出的请求也会被中断。

深比较依赖

在使用 useEffect 等需要传入依赖的 hook 时,最理想的状况是所有依赖都在真正发生变化的时候才去改变自身的引用地址,但是有些依赖不太听话,每次渲染都会重新生成一个引用,但是内部的值却没变,这可能会让 useEffect 对于依赖的「浅比较」没法正常工作。

比如说:

const getDep = () => {
return {
foo: 'bar',
};
}; useEffect(() => {
// 无限循环了
}, [getDep()]);
复制代码

这是一个人为的例子,由于 getDeps 函数返回的对象每次执行都是一个全新的引用,所以会导致触发渲染->effect->渲染->effect 的无限更新。

有一个比较取巧的解决办法,把依赖转为字符串:

const getDep = () => {
return {
foo: 'bar',
};
}; const dep = JSON.stringify(getDeps()); useEffect(() => {
// ok!
}, [dep]);
复制代码

这样对比的就是字符串 "{ foo: 'bar' }" 的值,而不是对象的引用,那么只有在值真正发生变化时才会触发更新。

当然最好还是用社区提供的方案:useDeepCompareEffect,它选用深比较策略,对于对象依赖来说,它逐个对比 key 和 value,在性能上会有所牺牲。

如果你的某个依赖触发了多次无意义的接口请求,那么宁愿选用 useDeepCompareEffect ,在对象比较上多花费些时间可比重复请求接口要好得多。

useDeepCompareEffect 大致原理:

import { isEqual } from 'lodash';
export function useDeepCompareEffect(fn, deps) {
const trigger = useRef(0);
const prevDeps = useRef(deps);
if (!isEqual(prevDeps.current, deps)) {
trigger.current++;
}
prevDeps.current = deps;
return useEffect(fn, [trigger.current]);
}
复制代码

真正传入 useEffect 用以更新的是 trigger 这个数字值。用useRef 保留上一次传入的依赖,每次都利用 lodash 的 isEqual 对本次依赖和旧依赖进行深比较,如果发生变化,则让 trigger 的值增加。

当然我们也可以用 fast-deep-equal 这个库,根据官方的 benchmark 对比,它比 lodash 的效率高 7 倍左右。

以 URL 为数据仓库

在公司内部的后台管理项目中,无论你做的系统面向的人群是运营还是开发,都会涉及到分享,那么保留「页面状态」就非常重要了。比如我是运营 A,在使用一个内部数据平台,我一定是想向运营 B 分享某 App 的消费数据的第二页,并且筛选为某个用户的状态的网页,并且进行讨论。那么状态和 URL 同步就尤为重要了。

在传统的状态管理思路中,我们需要在代码里用reduxrecoil等库去做一系列的数据管理,但是如果把 URL 后面的那串 query 想象成数据仓库呢?是不是也可以,尝试配合react-router封装一下。

export function useQuery() {
const history = useHistory();
const { search, pathname } = useLocation();
// 保存query状态
const queryState = useRef(qs.parse(search));
// 设置query
const setQuery = handler => {
const nextQuery = handler(queryState.current);
queryState.current = nextQuery;
// replace会使组件重新渲染
history.replace({
pathname: pathname,
search: qs.stringify(nextQuery),
});
};
return [queryState.current, setQuery];
}
复制代码

在组件中,可以这样使用:

const [query, setQuery] = useQuery();

// 接口请求依赖 page 和 size
useEffect(() => {
api.getUsers();
}, [query.page, query, size]); // 分页改变 触发接口重新请求
const onPageChange = page => {
setQuery(prevQuery => ({
...prevQuery,
page,
}));
};
复制代码

这样,所有的页面状态更改都会自动同步到 URL,非常方便。

React实用技巧的更多相关文章

  1. Notepad++ 实用技巧

    Notepad++是一款开源的文本编辑器,功能强大.很适合用于编辑.注释代码.它支持绝大部分主流的编程语言. 本文主要列举了本人在实际使用中遇到的一些技巧. 快捷键 自定义快捷键 首先,需要知道的是: ...

  2. javascript实用技巧、javascript高级技巧

    字号+作者:H5之家 来源:H5之家 2016-10-31 11:00 我要评论( ) 三零网提供网络编程. JavaScript 的技术文章javascript实用技巧.javascript高级技巧 ...

  3. iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式

    iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式 说明: 1)该文简短介绍在iOS开发中遍历字典.数组和集合的几种常见方式. 2)该文对应的代码可以在下面的地址获得:https:// ...

  4. iOS开发实用技巧—在手机浏览器头部弹出app应用下载提示

    iOS开发实用技巧—在手机浏览器头部弹出app应用下载提示 本文介绍其简单使用: 第一步:在本地建立一个访问的服务端.  打开本地终端,在本地新建一个文件夹,在该文件夹中存放测试的html页面.   ...

  5. iOS开发实用技巧—项目新特性页面的处理

    iOS开发实用技巧篇—项目新特性页面的处理 说明:本文主要说明在项目开发中会涉及到的最最简单的新特性界面(实用UIScrollView展示多张图片的轮播)的处理. 代码示例: 新建一个专门的处理新特性 ...

  6. IOS 网络浅析-(十三 SDWebImage 实用技巧)

    IOS 网络浅析-(十三 SDWebImage 实用技巧) 首先让我描述一下为了什么而产生的实用技巧.(在TableView.CollectionView中)当用户所处环境WiFi网速不够快(不能立即 ...

  7. NSString的八条实用技巧

    NSString的八条实用技巧 有一篇文章写了:iOS开发之NSString的几条实用技巧 , 今天这篇,我们讲讲NSString的八条实用技巧.大家可以收藏起来,方便开发随时可以复制粘贴. 0.首字 ...

  8. ###《VIM实用技巧》

    ###<VIM实用技巧> #@author: gr #@date: 2015-11-20 #@email: forgerui@gmail.com <VIM实用技巧>阅读笔记. ...

  9. PowerDesigner实用技巧小结(3)

    PowerDesigner实用技巧小结(3) PowerDesigner 技巧小结 sqlserver数据库databasevbscriptsqldomain 1.PowerDesigner 使用 M ...

随机推荐

  1. 手工数据结构系列-C语言模拟队列 hdu1276

    #include <stdio.h> #include <stdlib.h> #define init_size 1000 typedef struct { int head, ...

  2. git cli all in one

    git cli all in one https://www.atlassian.com/git/tutorials/learn-git-with-bitbucket-cloud git create ...

  3. macOS 升级后导致 dart bug

    macOS 升级后导致 dart bug macOS 10.15.5 $ demo_app git:(master) flutter doctor # /Users/xgqfrms-mbp/Docum ...

  4. Wi-Fi 6

    Wi-Fi 6 802.11ax https://en.wikipedia.org/wiki/IEEE_802.11ax https://www.wi-fi.org/discover-wi-fi/wi ...

  5. Chrome DevTools & Slow 3G Network

    Chrome DevTools & Slow 3G Network shortcuts https://developers.google.com/web/tools/chrome-devto ...

  6. 2018-1-6-IDEA快速代码生成

    2018-1-6-IDEA快速代码生成 Java 自动生成 Intellij IDEA 利用IDEA编辑器的Live Templates可以实现自定义方法.属性.注释等,下面是我自己的常用模板. 属性 ...

  7. 从零开始搞后台管理系统(1)——shin-admin

      shin 的读音是[ʃɪn],谐音就是行,寓意可行的后台管理系统,shin-admin 的特点是: 站在巨人的肩膀上,依托Umi 2.Dva 2.Ant Design 3和React 16.8搭建 ...

  8. Window下Scala开发环境搭建

    在Windows下搭建Scala开发环境,需要做以下几个步骤 1) 安装JDK 2) 安装Scala,并配置环境变量 3) Idea安装并创建Scala 类 1.安装JDK JDK安装,这里不再介绍, ...

  9. 鸿蒙Java开发模式11:鸿蒙图片裁剪功能的实现

    鸿蒙入门指南,小白速来!从萌新到高手,怎样快速掌握鸿蒙开发?[课程入口] 目录: 1. 鸿蒙版图片裁剪功能效果展示 2.Java代码实现 3.裁剪工具类实现 4.<鸿蒙Java开发模式>系 ...

  10. WPF MVVM实例三

    在没给大家讲解wpf mwm示例之前先给大家简单说下MVVM理论知识: WPF技术的主要特点是数据驱动UI,所以在使用WPF技术开发的过程中是以数据为核心的,WPF提供了数据绑定机制,当数据发生变化时 ...