年初的时候,从头大的flutter终于转回了老本行js代码,就此第一次开始接触了hooks。
一开始看useState这些api其实很奇怪的,返回一个数组,第一个数组项目是value,第二个是setValue的函数。然后就是useEffect,useCallback这些,有点套不上原来class组件的思维。但一年下来,感觉非常爽,就个人感觉hooks理念相比其他前端框架跃进了一大截。
为什么需要hooks
从React官方提案中,列举了三个理由
- Hooks let you reuse logic between components without changing your component hierarchy.
- Hooks let you split one component into smaller functions based on what pieces are related.
- Hooks let you use React without classes.
我说下我个人理解:
增强function组件
这个hooks最招牌的好处,function组件本身非常简洁,但在React原来的写法中,只能承担渲染无state变化逻辑的纯组件,但引入hooks后,能使函数组件使用管理state和“管理声明周期”的能力。
从理解上更符合React理念
React核心过程UI=f(state),其本身就是一个函数组件。函数组件精准的阐述了React对应构建用户界面的理念。class组件更像是非必要的妥协。而且有时也有问题:函数式组件与类组件有何不同?
而hooks对应生成最终UI的state管理就像函数式编程的嵌套调用。
// 以下是伪代码
// 函数组件我们这么写
const APP = function () {
const [state1] = useState();
const [state2] = useState();
render({
state1,
state2
});
}
// 就如同函数式编程的嵌套调用
APP = React.render(
useState2(
useState1(
initState
)
)
);
这一切看起来非常自然且易于调试理解,甚至就像dan的博客提到“Bug-O” 表示法的bug复杂度是bug(o)。
逻辑复用
这个写过的都知道,hooks的状态复用非常爽。其实其他框架在之前都解决了ui的组件化问题,但常常复杂工程带来的就是集成这些小组件的主控组件非常庞大,需要管理非常多的子组件状态。而hooks则可以将这些state拆分出去。
hooks原理简述
来看看hooks在React的数据结构。在新版filber架构中,用fiber树来表示整个vdom结构,函数组件也是一个fiber节点。fiber用child和return,sibling来指示fiber节点的子节点,父节点和兄弟节点。因此看似是个树结构,实际是个链表来串联起来的。
而在每个fiber的数据结构中,有一个memoizedState属性,这个就是存储hooks的地方,每个函数组件有多个hooks,因此memoizedState也是一个链表结构,来串联起来每一个hooks。而在每个hooks节点里面,也有一个memoizedState,这里面存储着每个hooks的数据(例如state,依赖的effect等等)。
在第一次mount阶段,会根据顺序将hooks对象生成并按照顺序一个个加入到memoizedState链表结构中。在hooks执行的时候,会有个workInProgressHook在不断指向hooks链表的最新一个节点,从而对应找到相应的hooks数据,根据更新队列queue,计算出hooks的存储数据memoizedState(所以顺序很重要)。
将hooks加入链表后,React将hook.memoizedState和dispatch返回,dispatch则已经附上了当前的hooks状态,和当前fiber关联,然后用闭包返回保存起来,更新的时候就和当前的fiber绑定关系了。hooks执行阶段,会调用这个dispatchAction,在函数内部,会创建update对象,形成链表,存在hooks数据的queue下。
const queue = (hook.queue = {
pending: null,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: (initialState: any),
});
// 设置queue.dispatch, 当前fiber被关联到queue.dispatch中
const dispatch: Dispatch<
BasicStateAction<S>,
> = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingFiber,
queue,
): any));
//4. 返回dispatchAction操作接口
return [hook.memoizedState, dispatch];
hooks核心就两点 1. 链表 2.闭包(保留了当前的现场)。
hooks的实践
hooks的分散和集中
之前说hooks的一点非常好的地方就是逻辑的复用非常方便,可以将管理state逻辑非常容易的拆分出去,但有时经常碰到“复用”过度的地方:hooks嵌套组合非常复杂,导致阅读调试非常烦。我曾经遇到的例子如下图:
类似于这样吧,嵌套了四五层,设计者必须小心管理和划分每个hooks职责,但即使这样,也使得很多逻辑分散,不知道一个state的变化到底是哪里导致的,难以阅读。但如果过于hooks设计的集中又会使得代码逻辑集中庞杂,失去了hooks的优势。因此复杂项目需要把握好hooks的内聚和耦合。
依赖追踪的陷阱
hooks引入了副作用及依赖变化的概念。但即使hooks用了很长的时间,也经常会碰到useEffect死循环渲染,和一些不必要的重复render。官方设计了useCallback和useMemo来解决,但大型项目参与人多和代码量增大的情况下,追踪依赖就变成了挺大的调试负担。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!