react中的副作用?

开篇

最近在研究react源码嘛,当正好我研究到hooks的时候,脑海就忍不住想了有关react副作用的连续问题,这这篇文章我们只少量的分析源码,主要是描述一个思考的思路。先玩点抽象的概念,react就像个女孩,她不是很纯洁effect,但她很尽力的表现得她很纯洁,当我们去使用useEffect可以从中窥得她的一部分不纯洁

思考过程

什么是副作用 -> React中的副作用,以及怎么做副作用分离 -> 简单的看看实现

副作用

先想想我们平常如果在日常会怎么理解副作用,这个词经常会用在我们日常生活中的吃药比如:我原本要吃一片感冒药为了治好我的感冒,但是这药,在治好的感冒的同时可能还造成了我的拉肚子

我们可以把这个例子反馈到我们的函数中,治好感冒是我们的目的是我们函数返回值,而拉肚子是在我们获得这个函数返回值的过程中的副作用,这个描述可能不太准确,比较准确的说法是函数在执行过程中对外部造成的影响称之为副作用

我们可以举一个最简单的例子:下述代码,除了返回了我们的6,还同时产生了附加的影响,将arr数组删除了最后一项。而这个附加的影响就可以被称之为副作用。(有关纯函数,非纯函数,引用透明这些概念性编程范式的东西,可以去看看其他博主的,网上有挺多的。)

let arr = [1,2,3,4,5,6];
let lastOne = arr.pop(); 

React副作用分离

这里就涉及一个知识点,代数效应(算了这东西有很多解释大家可以去看看,强抓八股文并不利于我们思考)。简单的说就是在React hook中以这个理念,去实现了一套让我们在api使用层面可以去不关注副作用,将副作用分离出来,交给内部处理,举个例子:本质上useState是一个有副作用的api但是react内部帮你抽离了让你在使用层面上看起来像一个纯函数

下述例子,useEffect函数中我们可以不用去关心可能它在count改变后,对fiber渲染所做的事情,他在内部已经给你处理了。其实到这不关心源码的朋友们已经可以结束了。

const [count, setCount] = useState(0)
useEffect(() => {// 随便做点啥
}, [count]) 

是如何分离的,又拿副作用做了什么?

因为实际上这个问题太大了,react的内部处处是副作用,但我们可以单独从一个小点useEffect去分析,因为我们翻译一下就可以知道这个hook就是抛出的最直观的去使用副作用的地方, useEffect的调用时机是在fiber树渲染的过程中,他可以改变状态,发起新的一轮的fiber构造等去引发一个连续的副作用,其实本质上感觉hook或者说后续回调函数完成后的修改了状态,对函数以外的东西产生了影响都可以视为副作用

简单说说把,useEffect会创建一个effect环形链表并保存在fiber.updateQueuehook.memoizedState中,当我们渲染开始的时候会将useEffect放入调度中,等到flushPassiveEffectsImpl去异步执行它。此时会有一些副作用留存到fiber上。

 try {const create = effect.create;if (enableProfilerTimer &&enableProfilerCommitHooks &&fiber.mode & ProfileMode) {try {startPassiveEffectTimer();effect.destroy = create();} finally {recordPassiveEffectDuration(fiber);}} else {// 执行effect.destroy = create();}} catch (error) {invariant(fiber !== null, 'Should be working on an effect.');captureCommitPhaseError(fiber, error);} 

等到我们更新的时候,这些副作用又会派上用场,依次循坏。

function updateEffectImpl(fiberFlags, hookFlags, create, deps): void {// 获取当前hookconst hook = updateWorkInProgressHook();const nextDeps = deps === undefined ? null : deps;let destroy = undefined;// 分析依赖if (currentHook !== null) {// 留存下来的`effect`链表const prevEffect = currentHook.memoizedState;// 继续使用先前effect.destroydestroy = prevEffect.destroy;if (nextDeps !== null) {const prevDeps = prevEffect.deps;// 浅比较依赖是否变化if (areHookInputsEqual(nextDeps, prevDeps)) {如果依赖不变, 新建effect(tag不含HookHasEffect)pushEffect(hookFlags, create, destroy, nextDeps);return;}}}// 如果依赖改变, 更改fiber.flag, 新建effectcurrentlyRenderingFiber.flags |= fiberFlags;hook.memoizedState = pushEffect(HookHasEffect | hookFlags,create,destroy,nextDeps,);
} 

总结

以上的都是自己的一些思考,并不能完全的代表是正确的,欢迎纠错。没用的知识又增加洛!

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

你可能感兴趣的:(react.js,前端,javascript)