react中的副作用?

文章探讨了React中的副作用概念,如何在React中通过useState和useEffect进行副作用管理。特别地,useEffect允许开发者在不关注副作用的情况下处理它们,实现了副作用的分离。在React的fiber架构中,useEffect的执行时机和副作用的处理机制被详细讨论,包括effect链表的创建和更新过程。

开篇

最近在研究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个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



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

<think> 根据用户的问题,用户想了解React副作用的定义。参考提供的引用内容,我们可以总结出React副作用的定义和相关解释。 引用[1]提到,在React开发中,副作用通常与`useEffect`这个Hook相关,官方明确说它是用来处理副作用的。 引用[2]给出了计算机科学中副作用的定义:指函数或代码块对函数外部环境(除了函数返回值以外的地方)产生的影响。在React组件中,副作用通常是指与组件状态管理以外的操作,如数据获取、订阅、DOM操作、定时器设置等。 引用[3]进一步解释了副作用的概念:副作用这个词来源于函数式编程,在函数式编程中,一个纯粹的函数只依赖于它的输入参数,并且只产生输出而不修改任何外部状态。在React中,我们经常需要执行一些非纯粹的操作,比如数据获取、DOM操作、设置定时器等,这些操作虽然不是纯粹的,但对于应用程序的正常运行是必不可少的。 因此,我们可以将React中的副作用定义为:在组件渲染过程中,除了返回JSX和状态更新之外,对组件外部环境产生的任何影响。这些操作包括但不限于数据获取、订阅、手动修改DOM、设置定时器等。 在React中,我们通常使用`useEffect`这个Hook来管理副作用,因为它可以将副作用操作与组件的渲染逻辑分离,并确保在适当的时机执行这些操作。 例如,下面的代码展示了使用`useEffect`来设置文档标题(这是一个副作用,因为它修改了全局的document对象): ```jsx useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); ``` 此外,引用[4]提到,React内部通过Fiber架构来管理副作用的执行,这涉及到一些内部源码文件(如`ReactFiberCommitWork.old.js`、`ReactFiberWorkLoop.old.js`等),但用户可能更关注概念层面。 综上所述,React中的副作用是指组件中除了渲染和状态更新之外,对组件外部环境产生的操作或影响。 §§相关问题§§ 1. 为什么在React中需要管理副作用? 2. 如何使用`useEffect` Hook处理副作用? 3. 在函数式组件中,除了`useEffect`,还有哪些处理副作用的方法? 4. 副作用React的类组件中是如何处理的? </think> 在React中,**副作用**是指组件执行过程中对组件外部环境产生的变更或影响,这些操作超出了纯粹的渲染逻辑范畴。具体定义和特点如下: ### React副作用的定义 1. **外部环境影响** 指函数或代码块对函数外部环境(除返回值外)产生的修改,例如: - 数据获取(API请求) - DOM直接操作(如修改`document.title`) - 订阅/取消订阅事件 - 设置/清除定时器 - 本地存储操作 ```jsx // 典型副作用:修改全局DOM useEffect(() => { document.title = `当前计数: ${count}`; }, [count]); ``` 2. **非纯粹性操作** 在函数式编程范式中,纯粹函数应仅依赖输入参数并返回输出值。React组件理想状态是纯函数,但实际需要副作用实现功能: ```jsx // 非纯粹操作示例:依赖外部API数据 const [data, setData] = useState(null); useEffect(() => { fetch('/api/data').then(res => setData(res)); // 副作用 }, []); ``` 3. **与渲染流程分离** 副作用不属于组件的渲染逻辑(JSX生成),React通过`useEffect`等Hook将其与渲染解耦: ```jsx function MyComponent() { // 渲染逻辑(纯函数部分) return <div>{content}</div>; // 副作用逻辑(非纯部分) useEffect(() => { /*...*/ }); } ``` ### 为什么需要专门管理 1. **避免不可预测行为** 副作用可能破坏组件一致性,如未清理的定时器导致内存泄漏 2. **支持并发模式** React的Fiber架构(如`ReactFiberWorkLoop.old.js`)需要可控的副作用执行时机[^4] 3. **维护纯函数特性** 保持组件可预测性,便于状态管理和调试 > 总结:React中的副作用是**与组件状态管理无关且影响外部环境的操作**,需通过`useEffect`等API专门管控[^1][^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值