取消请求

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

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

  1. useEffect(() => {
  2. return () => {
  3. // 页面卸载时执行
  4. };
  5. }, []);
  6. 复制代码

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

  1. const abortController = new AbortController();
  2. fetch(url, {
  3. // 这里传入 signal 进行关联
  4. signal: abortController.signal,
  5. });
  6. // 这里调用 abort 即可取消请求
  7. abortController.abort();
  8. 复制代码

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

  1. export function useFetch = (config, deps) => {
  2. const abortController = new AbortController()
  3. const [loading, setLoading] = useState(false)
  4. const [result, setResult] = useState()
  5. useEffect(() => {
  6. setLoading(true)
  7. fetch({
  8. ...config,
  9. signal: abortController.signal
  10. })
  11. .then((res) => setResult(res))
  12. .finally(() => setLoading(false))
  13. }, deps)
  14. useEffect(() => {
  15. return () => abortController.abort()
  16. }, [])
  17. return { result, loading }
  18. }
  19. 复制代码

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

深比较依赖

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

比如说:

  1. const getDep = () => {
  2. return {
  3. foo: 'bar',
  4. };
  5. };
  6. useEffect(() => {
  7. // 无限循环了
  8. }, [getDep()]);
  9. 复制代码

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

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

  1. const getDep = () => {
  2. return {
  3. foo: 'bar',
  4. };
  5. };
  6. const dep = JSON.stringify(getDeps());
  7. useEffect(() => {
  8. // ok!
  9. }, [dep]);
  10. 复制代码

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

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

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

useDeepCompareEffect 大致原理:

  1. import { isEqual } from 'lodash';
  2. export function useDeepCompareEffect(fn, deps) {
  3. const trigger = useRef(0);
  4. const prevDeps = useRef(deps);
  5. if (!isEqual(prevDeps.current, deps)) {
  6. trigger.current++;
  7. }
  8. prevDeps.current = deps;
  9. return useEffect(fn, [trigger.current]);
  10. }
  11. 复制代码

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

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

以 URL 为数据仓库

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

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

  1. export function useQuery() {
  2. const history = useHistory();
  3. const { search, pathname } = useLocation();
  4. // 保存query状态
  5. const queryState = useRef(qs.parse(search));
  6. // 设置query
  7. const setQuery = handler => {
  8. const nextQuery = handler(queryState.current);
  9. queryState.current = nextQuery;
  10. // replace会使组件重新渲染
  11. history.replace({
  12. pathname: pathname,
  13. search: qs.stringify(nextQuery),
  14. });
  15. };
  16. return [queryState.current, setQuery];
  17. }
  18. 复制代码

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

  1. const [query, setQuery] = useQuery();
  2. // 接口请求依赖 page 和 size
  3. useEffect(() => {
  4. api.getUsers();
  5. }, [query.page, query, size]);
  6. // 分页改变 触发接口重新请求
  7. const onPageChange = page => {
  8. setQuery(prevQuery => ({
  9. ...prevQuery,
  10. page,
  11. }));
  12. };
  13. 复制代码

这样,所有的页面状态更改都会自动同步到 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/OS-III---I笔记1---概述

    在裸板上跑一些程序对于一些电子设计是足够的,所谓裸板上的程序就是传统的前后台系统,而我的理解它应该是一种"过程类"的程序,一个大循环(作为后台)做一些处理,中断程序(作为前台)来处 ...

  2. VSCode 开放式架构的产品实现思路

    VSCode 开放式架构的产品实现思路 https://code.visualstudio.com/ 源码 https://github.com/microsoft/vscode https://gi ...

  3. 如何使用 js 写一个正常人看不懂的无聊代码

    如何使用 js 写一个正常人看不懂的无聊代码 代码质量, 代码可读性, 代码可维护性, clean code WAT js WTF https://www.destroyallsoftware.com ...

  4. PM2 All In One

    PM2 All In One https://pm2.keymetrics.io/ https://pm2.io/ $ yarn global add pm2 # OR $ npm install p ...

  5. how to publish a dart package using Github Actions?

    how to publish a dart package using Github Actions? dart package flutter package Github Actions publ ...

  6. Interview Questions All In One

    Interview Questions All In One web fullstack System Design Operating System Object-Oriented Design O ...

  7. CSS Architecture & CSS Design Patterns

    CSS Architecture & CSS Design Patterns BEM Block, Element, Modifier https://en.bem.info/methodol ...

  8. 如何在ASP.NET Core中编写高效的控制器

    ​通过遵循最佳实践,可以编写更好的控制器.所谓的"瘦"控制器(指代码更少.职责更少的控制器)更容易阅读和维护.而且,一旦你的控制器很瘦,可能就不需要对它们进行太多测试了.相反,你可 ...

  9. 04、数组与Arrays工具类

    目录 前言 一.一维数组 基本认识 内存空间 二.二维数组 基本认识 三.工具类Arrays 前言 去年四月份大一下半学期正式开始学习Java,一路从java基础.数据库.jdbc.javaweb.s ...

  10. PHP Webshell List

    目录 基础类 编码替换 无关键字函数类型 躲避检测记录 MySQL写入一句话 基础类 很容易被扫描.检测出来 <?php @eval($_GET['phpcode']);?> <?p ...