react生命周期函数_如何优雅的消灭掉react生命周期函数

05ef16072014df6dc2899079c37f9774.png

开源不易,感谢你的支持,❤ star concent^_^

序言

在react应用里,存在一个顶层组件,该组件的生命周期很长,除了人为的调用unmountComponentAtNode接口来卸载掉它和用户关闭掉浏览器tab页窗口,该顶层组件是不会有被销毁的时机的,它一直伴随着整个应用,所以我们都会在该组件的componentDidMount函数里发起一些请求来获取服务器端的配置型数据并缓存起来,方便整个应用全局使用。

对于由路由系统挂载的页面组件,我们通常也会在它的componentDidMount函数里发起请求来获取该页面,如果状态是由store管理的(如redux、或者mobx),若需要在页面组件的卸载的时候清理相应的store状态,则还会选择在componentWillUnmount里调用相应的方法做清理。

a57e8a34cfc156d2f45fcccccb30f2b1.png

当然了,对于函数组件来说使用useEffect钩子函数做起来就一步到位,比起类组件显得更简单

function PageComp(){useEffect(()=>{/** 等效于 componentDidMount 发起请求调用 */return ()=>{/** 等效于 componentWillUnmount 做相应的清理 */}}, [])
}

当前生命周期函数的使用体验

那本文题目提到的消灭生命周期又作何解释呢?看起来没有了它们我们是无法完成类似需求的,在对此作出解释之前,我们先列举一下现在的生命周期的使用体验问题。

无法共用一套逻辑

类组件和函数组件是无法做到0修改共用一套逻辑的,类组件在未来的很长一段时间内都将一直存在,这是我们无法避免的问题,但类组件和函数组件的设计理念导致它们的生命周期函数使用方式是完全不同的,所以共享逻辑需要一定的改造

初始化流程和组件耦合在一起

已提升到store的状态的初始化流程却还是和组件耦合在一起,这一点一定要注意一个前提,就是我们通常在顶层组件的生命周期函数里完成store的某个节点的状态初始化,不管是根组件还是页面组件,它们都具有顶层组件的性质,但是把store某节点的状态初始化流程写在组件里会带来一些额外的问题, - 如果另一个页面组件也需要使用该节点数据时,需要额外的检查状态有没有初始化好 - 当重构顶层组件的时候要小心翼翼的维护好这些声明周期逻辑

接下里让我们看看在concent里是如何处理这些问题并消灭掉生命周期函数的呢。

使用组合api统一逻辑

虽然类组件和函数的生命周期声明方式和使用方式完全不一样,但是我们可以依靠组合api来抹掉这层差异,达到让类组件和函数组件都真正的只充当ui载体的目的

假设有以下两个自管理状态的组件,他们都具有相同的功能,一个是类组件

class ClsPageComp extends React.Component{state = {list: [],page: 1,};componentDidMount(){fetchData();}componentWillUnmount(){/** clear up */}fetchData = () => {const { page } = this.state;fetch('xxxx', { page }).then(list => this.setState({ list }))}nextPage = () => {this.setState({ page: this.page + 1 }, this.fetchData);}render() {/** ui logic */}
}

一个是函数组件

// 函数组件
function PageComp() {const [list, setList] = useState([]);const [page, setPage] = useState(1);const pageRef = useRef(page);pageRef.current = page;const fetchData = (page) => {// fetch("xxxx", { page }).then((list) => setList(list));};const nextPage = () => {const p = page + 1;setPage(p);fetchData(p);};useEffect(() => {fetchData(pageRef.current);return () => {/** clear up */};}, []);/** ui logic */
}

两者看起来完完全全不一样,且函数组件里为了消除useEffect依赖缺失警告还是用useRef来固定住目标值,这些比较烧脑的操作对于新用户来说是非常大的障碍。

接下来我们看看基于setup的组合api如何来解除这些障碍,setup是一个普通的函数,仅提供一个参数代表当前的渲染上下文,并支持返回一个新的对象(通常都是一堆方法集合),该对象能够通过settings在渲染块内获取到,装配了setup函数的组件在实例化时,仅被触发执行一次,所以我们可以看看上述示例改造后,会变为:

function setup(ctx) {const { initState, setState, state, effect } = ctx;initState({ list: [], page: 0 });const fetchData = (page) => {fetch('xxxx', { page }).then(list => setState({ list }))};effect(()=>{fetchData(state.page);return ()=>{/** clear up */};}, []);return {nextPage: () => {const p = page + 1;setState({ page: p });fetchData(p);}};
}

接着在类组件里和函数组件里,都可通过渲染上下文ctx拿到数据和方法

import { register, useConcent } from 'concent';@register({ setup })
class ClsComp extends React.Component {render() {const { state: { page, list }, settings: { nextPage } } = this.ctx;// ui logic}
}function PageComp() {const {state: { page, list }, settings: { nextPage },} = useConcent({ setup });// ui logic
}

使用lifecyle消除生命周期

当我们的页面组件状态提升到模块里时,我们可以使用lifecyle.mountedlifecyle.willUnmount来彻底解耦生命周期和组件的关系了,concent内部会维护一个模块对应下的实例计数器,所以依靠这个功能可以精确控制模块状态的初始化时机了。

lifecyle.mounted

当前模块的第一个实例挂载完毕时触发,且仅触发一次,即当该模块的所有实例都销毁后,再次有一个实例挂载完毕,也不会触发了

run({product: { lifecycle: {mounted: (dispatch)=> dispatch('initState')}  }
})

如需反复触发,即只要满足模块的实例数从0到1时就触发,返回false即可

lifecyle.willUnmount

当前模块的最后一个实例将销毁时触发,且仅触发一次,即当该模块再次生成了很多实例,然后又全部销毁,也不会触发了

run({counter: { lifecycle: {willUnmount: dispatch=> dispatch('clearModuleState'),}  }
})

同样的如需反复触发,即只要满足模块的实例数从有变为0时就触发,返回false即可

lifecyle.loaded

如果该模块的状态和有无组件挂载无关系,则直接配置loaded即可

run({counter: { lifecycle: {loaded: (dispatch)=> dispatch('initState'),}  }
})

改造示例

介绍完lifecyle,我们来看看改造上述函数组件和类组件后的实例长为什么样,首先我们定义product模块

import { run } from 'concent';run({product: {state: { list: [], page: 1 },reducer: {async initState() {/** init state logic */},clearState() {/** clear state logic */},async nextPage(payload, moduleState, ac) {const p = moduleState.page + 1;await ac.setState({ paeg: p });const list = await fetch('xxxx', { page: p });return { list };}},lifecycle: {mounted: dispatch => dispatch('initState'),willUnmount: dispatch => dispatch('clearState'),}}
});

接着我们注册组件属于product模块即可,组件实例就可以调用product模块的方法和读取它的数据了。

import { register, useConcent } from 'concent';@register({ module: 'product' })
class ClsComp extends React.Component {render() {const { state: { page, list }, mr: { nextPage } } = this.ctx;// ui logic}
}function PageComp() {const {state: { page, list }, mr: { nextPage },} = useConcent({ module: 'product' });// ui logic
}

我们可以看到此时已没有了setup,是因为我们不需要额外定义方法和数据了,当我们需要为组件定义一些非模块的方法和数据时,依然可以定义setup

function setup(ctx) {const { initState, setState, state, effect } = ctx;initState({ xxxx: 'hey i am private' });effect(()=>{// 等效于useEffect里,当xxxx改变时执行此副作用console.log(state.xxxx);}, ['xxxx']);return {changeXXX: (e)=> setState({xxxx: e.target.value}),};
}

然后组件装配setup即可

import { register, useConcent } from 'concent';@register({ module: 'product', setup })
class ClsComp extends React.Component {render() {const { state: { page, list }, mr: { nextPage }, settings } = this.ctx;// ui logic}
}function PageComp() {const {state: { page, list }, mr: { nextPage }, settings,} = useConcent({ module: 'product', setup });// ui logic
}

结语

综上所述,我们可以看到其实并没有消灭生命周期函数,而是转移并统一了生命周期函数的定义入口,让其和组件的定义彻底分离,这样无论我们怎样重构组件代码,都不怕动到整个模块状态的初始化流程。

附录

和本期主题相近的其他文章

  • 初识组合api
  • recoil vs concent

CloudBase CMS

5e746fa7637a566178b181758c9e940e.png

欢迎小哥哥们来撩CloudBase CMS ,打造一站式云端内容管理系统,它是云开发推出的,基于 Node.js 的 Headless 内容管理平台,提供了丰富的内容管理功能,安装简单,易于二次开发,并与云开发的生态体系紧密结合,助力开发者提升开发效率。

concent已为其管理后台提供强力支持,新版的管理界面更加美观和体贴了。

FFCreator

54d92eee59ffcd609cefb8ad42b69270.png

也欢迎小哥哥们来撩FFCreator,它是一个基于node.js的轻量、灵活的短视频加工库。您只需要添加几张图片或视频片段再加一段背景音乐,就可以快速生成一个很酷的视频短片。

FFCreator是一种轻量又简单的解决方案,只需要很少的依赖和较低的机器配置就可以快速开始工作。并且它模拟实现了animate.css90%的动画效果,您可以轻松地把 web 页面端的动画效果转为视频,真的很给力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/340216.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jolokia_Hawtio和Jolokia的Hibernate统计

jolokia企业Java的很大一部分处理数据。 在企业设置中使用数据的所有不同方式中,仍然存在使用任何种类的O / R映射的行之有效且广泛教授的方法。 JPA标准使每个人都比较容易使用它,并且它也应该是可移植的。 但是,我们不要谈论迁移细节。 O /…

JVM垃圾收集和优化

总览 在对系统进行性能相关问题的故障排除时,内存优化是一个需要深入分析每个系统在内存中存储的内容,存储时间和访问模式的地方。 这篇文章是要在背景信息上进行注释,并在此工作中要注意一些要点,这些工作要专门针对基于Java的实…

redis desktop manager连不上redis_Redis安装教程

Ⅰ 简介Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如字符串(strings),散列(hashes&#…

c++如何将int数组中的值取出*号运算符_如何用动态规划巧妙解决 “双十一” 购物时的凑单问题?羊毛薅起来!!!...

点击上方“程序员大白”,选择“星标”公众号重磅干货,第一时间送达今年过去的 “双十一” ,你有薅到羊毛吗?每年的双十一,会有各种促销活动,比如 “满 300元减 80 元”。假如你女朋友的购物车中有 n 个(n &…

python操作界面_Python使用PyQt5的Designer工具创建UI界面

一、Designer-UI编辑器 Designer是pyqt5-tools带的工具,默认可以在Python安装目录下找到的。我的之前项目导入过pyqt5-tools,所以我直接全盘搜索到了。打开designer后,我们可以编辑我们想要的UI界面,下面是我编辑的测试界面&#…

python统计词频_Python统计四六级考试的词频

Python统计四六级考试的词频此文首发于公众号 「Python知识圈」, 欢迎直接去公众号查看阅读文本大概需要 4.6 分钟。今天是教师节,先祝天下所有老师教师节快乐,感谢您在我学生时代对我的谆谆教诲。现在是开学之初,风华正茂的青年才…

python爬虫数据可视化_适用于Python入门者的爬虫和数据可视化案例

本篇文章适用于Python小白的教程篇,如果有哪里不足欢迎指出来,希望对你帮助。 本篇文章用到的模块: requests,re,os,jieba,glob,json,lxml,pyecharts,heapq,collection 首先 本文我们的目的 抓取周杰伦的所有歌曲, 歌词&#xff0…

mysql中如何卸载插件_Eclipse中如何卸载插件

很久没用Eclipse了,今天打开的时候,突然报Android开发插件需要更新。打开插件管理页面,更新Android插件,又提示和旧版本有冲突,心想只能卸载了重新安装了。找了半天没找到在哪下载,把Eclipse目录下的plugin…

computed怎么使用_Vuex 基本使用

简单介绍iPhone X 是 iPhone, Vuex 并不是 Vue.我们查看官方文档可以知道:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。怎么理解呢?就拿我两位数的资产的银行卡来说吧,基本的存钱取钱,就是状态管理。怎么用我们将以一个简…

windows安装python3步骤_Windows下python3和python2安装与一起使用

一、python2和python3安装 2、安装步骤,直接双击运行,记得勾选添加环境变量就可以。图片1.png 3、安装已经选择了添加这个环境变量,所以不用再去配置。 如果没有勾选,得自己去设置。 鼠标右键我的电脑 -> 属性 -> 点击高级系…

mysql ondelete_MySQL on delete cascade语句

在本教程中,您将学习如何使用MySQL ON DELETE CASCADE引用操作来执行外键从多个相关表中删除数据。在上一个教程中,我们学习了如何使用单个DELETE语句从一个或多个相关表中删除数据。但是,MySQL提供了一种更为有效的方法,称为ON D…

python构建二叉树_BinaryTree:学习二叉树的Python库

Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。简介: 您是否在为考试、作业或技术面试学习二叉树? Binarytree是一个Python库,它通过一个简单的API生成二叉树,可以进行检查和操作。它让您…

mysql union as 注入_sql注入入门 之 mysql 常规注入 [ union方式 ]

1,常规数字型 mysql 实例注入点,如下:1https://www.vuln.com/md_materia_profile_view.php?viewid22,依旧先尝试下经典的单引号,如下,虽然没暴露出明显的数据库报错信息,但我们发现,此时返回的页面已经异常了,经验判断,十有八九是个注入点,先不管那么多,我们继续1https://www.…

gradle docker_带有Gradle的Docker容器分为4个步骤

gradle docker您是否需要通过Java Web应用程序创建Docker映像? 您在使用Gradle吗? 如果是这样,那么您距Docker nivana仅4步之遥。 对于此示例,我将使用一个简单的Spring Boot应用程序。 您可以在我的名为galoshe的Github存储库中…

python socket编程_Python学习记录-socket编程

1. OSI七层模型详解2. Python socket 什么是 Socket? Socket又称”套接字”,应用程序通常通过”套接字”向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。 Python 提供了两个级别访问的网络服务: 低级别的网络服…

判断frame是否已创建_类的创建

1. 士兵 许三多 有一把 AK47 2. 士兵 可以开火 3. 枪 能够 发射 子弹 4. 枪 装填 子弹---增加子弹数量 class Gun:def __init__(self,type):self.type type# 刚开始枪没有子弹self.bullet_count 0def __str__(self):return ("%s 已到位" % self.type)def shoot(sel…

@select注解_Mybatis基本知识十七:Mybatis注解式开发-单表注解式开发

上一篇文章:《Mybatis基本知识十六:查询缓存之第三方查询缓存》若文中有纰漏,请多多指正!!!1.前言使用Mybatis进行开发,不仅可以使用mapper配置文件进行开发,也可以使用注解的方式。映射文件中无…

maven servlet配置_第一个Servlet配置

使用IntelliJ IDEA创建一个Simple Maven项目2. Add Framework Support3. 配置Tomcat打开菜单Run -> 选择Edit Configuration 如果侧边栏没有Tomcat,点击“”号 -> 选择“Tomcat Server” -> 选择“Local”,到此画面 ,Apply -> OK4. 在Tomcat…

ad域管理与维护_U-Mail邮件系统LDAP/AD同步极大提升办公效率

每一位办公族,可能都遇到过这样的问题:1、随着信息化高速发展和企业“互联网”的深入,越来越多的办公平台和软件被开发出来,正如移动互联网端APP应用层出不穷一样,给人们带来了极大地便利性。2、凡事有利有弊&#xff…

c语言c99标准_C语言的灵魂指针,配合这个新增的关键字,能够生成更高效的程序...

正如我前面的文章提到的,C语言虽然已经比较成熟,但是近些年来也是有所发展的——比如增加了许多新特性。遗憾的是,可能因为C语言程序员的工资比不过互联网程序员,国内很多教材比较老旧,几乎不涉及近些年来C语言新增的新…