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

序言

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

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

当然了,对于函数组件来说使用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

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

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

FFCreator

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

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

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

  1. react 布局容器_如何使用容器模式开发React超能力

    react 布局容器 Hello everyone! ? 大家好! ? This time I'm going to tell you about this very useful pattern i ...

  2. react转跳_您跳过的这些React基础知识可能会杀死您

    react转跳 Often times, the inability to debug a certain error stems from not understanding some founda ...

  3. react在线文件_编程界“滥竽充数者”?React是否名不副实?

    全文共 3672字,预计学习时长 11分钟 图源:Aphinya Dechalert提供 年初,笔者试着真正使用了一回React库.由于对Angular有一定的了解,笔者对库中提出的概念保持开放包容的 ...

  4. vue 声明周期函数_【Vue】详解Vue生命周期

    Vue实例的生命周期全过程(图) (这里的红边圆角矩形内的都是对应的Vue实例的钩子函数) 在beforeCreate和created钩子函数间的生命周期 在beforeCreate和created之 ...

  5. react 流程图框架_【赠书】Preact(React)核心原理详解Preact(React) 核心原理解析...

    豆皮粉儿们,又见面了,今天这一期,由字节跳动数据平台的"winge(宝丁)",带大家见识见识前端"轮子"之一Preact框架. 提到Preact,你肯定会先想到 ...

  6. 线程的生命周期_[线程设计]通过观察者模式监控线程生命周期

    推荐书籍:<Java高并发编程详解> 一.无法掌控 Thread 的生命周期 在 Java 中线程的实现只有一个,就是 Thread . 下面是启动一个线程的通用代码,如下: 通过 new ...

  7. java虚拟机的生命周期_深入理解Java虚拟机——JVM的生命周期

    package test; public class JVMTestLife { public static void main(String[] args) { new Thread(new Run ...

  8. native react 折线图_【详解】纯 React Native 代码自定义折线图组件(译)

    本文为 Marno 翻译,转载必须保留出处! 公众号[ Marno ],关注后回复 RN 加入交流群 React Native 优秀开源项目大全:http://www.marno.cn 一.前言 在移 ...

  9. sdlc 瀑布式 生命周期_管理信息系统中的系统开发生命周期(SDLC)

    sdlc 瀑布式 生命周期 系统开发生命周期(SDLC) (Systems Development Life Cycle (SDLC)) SDLC stands for "Systems D ...

最新文章

  1. 【连载】优秀程序员的45个习惯之39——架构师必须写代码
  2. Face++印奇:从无人问津到融资4.6亿美金|纪录影片
  3. 网元查看一个无厘头的core dump问题定位
  4. python文件操作和集合(三)
  5. MySQL查看表占用空间大小
  6. 405 宝塔钩子_点击数据库管理,nginx提示出现405 Not Allowed错误,是怎么回事?
  7. BadZipfile: File is not a zip file
  8. 计算机考研408真题百度云,2021计算机408考研计算机网络百度云+408历年真题
  9. html5 js实现ppt制作,impress.js前端制作酷炫ppt详细教程
  10. 电脑桌面云便签怎么设置开启邮箱提醒待办事项?
  11. 利用matlab将三维数据画成三维立体图
  12. 【UVM实战】第二章:一个简单的UVM验证平台(4)UVM 的终极大作:sequence
  13. LiveData浅析
  14. 基于Paddlehub实现的秒换证件照底色
  15. windbg符号下载
  16. 如何分析线程Dump和堆Dump
  17. 多旋翼飞行器设计与控制(二)—— 基本组成
  18. 计算机教研组活动目的,教研组活动方案
  19. jQuery源码分析之$.grep()函数四问
  20. 如何快速删除卸载残余-注册表

热门文章

  1. 在GWT中序列化/反序列化Json
  2. Java 9模块系统(拼图)@ LJC的HackTheTower
  3. neo4j cypher_优化Neo4j Cypher查询
  4. 适用于Java开发人员的Elasticsearch教程
  5. java nosql_使用NoSQL实现实体服务–第4部分:Java EE
  6. java docx文档解析_带有docx4j的Java Word(.docx)文档
  7. Spark简介,您的下一个REST Java框架
  8. 嵌入式Jetty和Apache CXF:借助Spring Security来保护REST服务
  9. ClassNotFoundException:是否减慢了您的JVM?
  10. Spring Data JPA教程第一部分:配置