React Hooks 原理
这是嘉木开始日更的第1天:) 备注:这个文章是 React Hooks 原理的学习笔记。
前言
现在在项目中使用React Hooks应该是常规操作了吧,都2202年了,应该没有人在新项目使用class来写React组件了吧,至少,我现在都在使用哪个React Hooks,作为一个2019年末才开始接触前端的人来说,其实,我自己是没有自己写过class组件,最多在老项目上修改下class组件,写项目都是用React Hooks。
虽然React Hooks用的很多,用的也比较熟,但是对于React的一些常见问题,却回答不上来:(
上次面字节,面试官问,为什么要用React Hooks,我都回答不出来。然后对于类似于为什么React Hook只能用在函数组件的最外层,我也不知道原因,我可能知道是这么一个行为,毕竟把React Hook 写在条件语句里面,React会报错嘛
看了上面的那篇文章,并自己实现了一下
为什么要使用React Hooks
这是问题其实在React文档讲React Hooks的动机的时候,已经讲得很清楚了
- 在组件之间重用有状态的逻辑比较复杂。React没有提供一种重用行为逻辑的方法,虽然有 render props 和高阶组件可以实现重落逻辑,但是重构组件很麻烦,也无法追踪。
- 复杂的组件变得难以理解。这一块主要是因为一个class组件有很多生命周期函数,相同的一块业务逻辑可能放在不同的生命周期函数中,使得代码理解起来很难。
- 万恶的this,作为一个前端开发,其实不知道或者说不熟悉类的,再加上类里面需要用到this,反正就很难。
useState
实现一个简单的useState其实相当简单。定义一个变量,定义一个改变这个变量的函数,然后返回就好了。每次改变这个变量之后,重新渲染一遍页面,就实现了一个简单的useState。
var _state; // 把 state 存储在外面
function useState(initialValue) {
_state = _state || initialValue; // 如果没有 _state,说明是第一次执行,把 initialValue 复制给它
function setState(newState) {
_state = newState;
render();
}
return [_state, setState];
}
useEffect
实现一个简单的useEffect也相当简单,只需要在全局定义一个变量,这个变量存的是组件上一次render的时候,这个hook的依赖,根据这个依赖与当前这一render的依赖对比,如果依赖数组有更新,则执行一遍callback。
let _deps; // _deps 记录 useEffect 上一次的 依赖
function useEffect(callback, depArray) {
const hasNoDeps = !depArray; // 如果 dependencies 不存在
const hasChangedDeps = _deps
? !depArray.every((el, i) => el === _deps[i]) // 两次的 dependencies 是否完全相等
: true;
/* 如果 dependencies 不存在,或者 dependencies 有变化*/
if (hasNoDeps || hasChangedDeps) {
callback();
_deps = depArray;
}
}
Not Magic, just Arrays
虽然到目前为止,我们实现了useState和useEffect,但是有一个问题是,useState和useEffect只能使用一次,这是我们需要把多个hooks的状态给存起来,useState存state信息,useEffect存deps信息,这时可以使用数组来存,然后使用一个指针cursor来指向当前指向哪一个hook
let memoizedState = []; // hooks 存放在这个数组
let cursor = 0; // 当前 memoizedState 下标
function useState(initialValue) {
memoizedState[cursor] = memoizedState[cursor] || initialValue;
const currentCursor = cursor;
function setState(newState) {
memoizedState[currentCursor] = newState;
render();
}
return [memoizedState[cursor++], setState]; // 返回当前 state,并把 cursor 加 1
}
function useEffect(callback, depArray) {
const hasNoDeps = !depArray;
const deps = memoizedState[cursor];
const hasChangedDeps = deps
? !depArray.every((el, i) => el === deps[i])
: true;
if (hasNoDeps || hasChangedDeps) {
callback();
memoizedState[cursor] = depArray;
}
cursor++;
}
问题解答
- 为什么React Hook只能使用在函数最外层,不能用在条件判断或者循环语句中? 因为React Hook是按照顺序存的,组件每次render hooks必须一一对应,如果某个hook用在条件判断中,渲染的时候有时出现,有时不出现,则会导致它后面所有的hooks信息错乱。
2.自定义hook是如何影响它的函数组件的? 自定义hook也由一堆官方hook组合而成的,它们同享这个函数组件的stateMemorized数组和cursor,也是按照出现的顺序存在那里面。
3.什么的capture value? 这部分后面专门写一篇文章吧
转载自:https://juejin.cn/post/7080929061677039646