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 /…

python主循环方法mainloop_python gobject.mainloop吞噬信号事件

没有人感兴趣,所以让我尝试. 只是在同一页面上: import signal from gi.repository import GObject GObject.threads_init() mainloop GObject.MainLoop() signal.signal(signal.SIGINT, lambda n, f: mainloop.quit()) mainloop.run() 此代码有效: imp…

JVM垃圾收集和优化

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

redis desktop manager连不上redis_Redis安装教程

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

python打开方式错误_浅谈python 调用open()打开文件时路径出错的原因

昨晚搞鼓了一下python的open()打开文件 代码如下 def main(): infile open("C:\Users\Spirit\Desktop\bc.txt",r) data infile.read() print(data) main() 然而结果总报错invaild argument 或者cant found such file *** 查找问题后 发现是由…

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

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

mysql sql 去除重复行_mysql – sql自连接表删除重复行

我有下表:╔════════╦════════════╗║ USERID ║ LANGUAGEID ║╠════════╬════════════╣║ 1 ║ 2 ║║ 1 ║ 7 ║║ 1 ║ 8 ║║ 2 ║ 10 ║║ 2 ║ 3 ║╚════════╩════════════╝现在我想为每…

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

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

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

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

易语言 字段重复_使对易失性字段的操作原子化

易语言 字段重复总览 易失性字段的预期行为是,它们在多线程应用程序中的行为应与在单线程应用程序中的行为相同。 禁止它们表现相同的方式,但是不能保证它们表现相同的方式。 Java 5.0中的解决方案是使用AtomicXxxx类,但是这些类在内存&…

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

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

qt4 连接mysql_Qt4访问mysql 数据库的简单教程

编译问题:1、系统中安装有VC,所以头文件冲突手工修改makefile删除所有-I 加载VC头文件的项目2、mysql cannot find -llibmysql指定libmysql.a的路径-LQt 4 访问 mysql 数据库的简单教程云帆 2006/5/18因为目前无法使用linux, 所以 Qt 4.1.2 是…

python多个函数_什么是在Python中使用多个构造函数的干净的、pythonic的方法?

其实None对于“魔法”价值观来说要好得多:class Cheese(): def __init__(self, num_holes None): if num_holes is None: ... 现在,如果您想完全自由地添加更多参数:class Cheese(): def __init__(self, *args, **kwargs): #args -- tuple o…

Java 8和Java 14之间的新功能

从版本9开始,Java每6个月就会有新功能,因此很难跟踪这些新更改。 互联网上的大多数信息都描述了最近两个Java版本之间的变化。 但是,如果您的情况与我相似,则说明您使用的不是Java的最新版本,而是使用了较旧的Java版本…

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

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

python io_NumPy IO

NumPy IO Numpy 可以读写磁盘上的文本数据或二进制数据。 NumPy 为 ndarray 对象引入了一个简单的文件格式:npy。 npy 文件用于存储重建 ndarray 所需的数据、图形、dtype 和其他信息。 常用的 IO 函数有: load() 和 save() 函数是读写文件数组数据的两个…

Java 14:instanceof的模式匹配

Java 14引入了pattern Matching for instanceof (另一种预览语言功能) ,从而消除了在使用instanceof时进行强制转换的需要。 例如,考虑以下代码: if (obj instanceof String) { String s (String) obj; System.out.pr…

centos7 mysql启动后端口_centos7 修改mysql5.7默认端口后启动异常

关闭selinux的方法有两种:临时关闭和永久关闭。查看selinux的状态:sestatus[root162-219-29-3 ~]# sestatusSELinux status: enabledSELinuxfs mount: /sys/fs/selinuxSELinux root directory: /etc/selinuxLoaded policy name: targetedCurrent mode: e…

computed怎么使用_Vuex 基本使用

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

自然数 素数 质数_在Java中获取素数的无限列表

自然数 素数 质数一个常见的问题是确定数字的素因式分解。 蛮力方法是审判部门( 维基百科 , 可汗学院 ),但是如果必须考虑多个数字,这需要大量的浪费工作。 一种广泛使用的解决方案是Eratosthenes筛( 维基…