Skip to content

第一个Hook-useMount和前置知识点

第一篇正式的ahooks的源码解读,我们首先来看下与生命周期相关的几个Hook,分别是useMountuseUnmountuseUnmountedRef

前置知识点

在直接解读源码前,我们还是重新简单回顾下关于React Hook的基本设计思路和心智模型

Hook的意义

首先,我们看下React官网关于Hook的设计动机

Hook 解决了我们五年来编写和维护成千上万的组件时遇到的各种各样看起来不相关的问题。无论你正在学习 React,或每天使用,或者更愿尝试另一个和 React 有相似组件模型的框架,你都可能对这些问题似曾相识。

那么具体是哪些问题呢?

  • 在组件之间复用状态逻辑很难,比如render props或者高阶组件 - Hook 使你在无需修改组件结构的情况下复用状态逻辑
  • 复杂组件变得难以理解 - Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)
  • 难以理解的class - Hook 使你在非 class 的情况下可以使用更多的 React 特性

什么是Hook

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。它能够让你以函数组件的方式去编写组件,当然它也有些必须遵循几条基本原则

  • 只在最顶层使用 Hook - 不要在循环,条件或嵌套函数中调用 Hook
  • 只在 React 函数中调用 Hook

为什么会有这些限制,事实上了解React Fiber设计思想和源码实现的同学就比较容易理解,Effect是挂载在FiberNode上的,而其实现的数据结构是树,树的遍历在循环或者条件语句的情况下会导致混乱。

事实上,在引入 Hooks 的概念后,函数组件既保留了原本的简洁,也具备了状态管理、生命周期管理等能力,在原来 Class 组件所具备的能力基础上,还解决了 Class 组件存在的理解成本,代码冗余【满篇的this是我个人非常烦躁的一个点】,逻辑难以复用等问题。

其实我也一直在纠结,是不是在这边直接给大家科普下Hook的设计思想,本身的几个类似useState,useEffect等使用,仔细斟酌了下后,我觉得本系列还是应该更聚焦一些,关于这部分更底层更基础的东西,我会在另外个系列同步更新,并使用链接的方式相互引用

那么ahooks的意义就是在React官方提供的基础hook的基础之上,通过实际开发场景的经验进行沉淀,将大量常用的方法以hook的形式进行封装,提升在实际业务开发中开发效率和质量

useMount

源码地址

开整,我们看下第一个,也是最简单的实现

useMount:只在组件初始化时执行

tsx
import { useEffect } from 'react';
import { isFunction } from '../utils';
import isDev from '../utils/isDev';

const useMount = (fn: () => void) => {
  if (isDev) {
    if (!isFunction(fn)) {
      console.error(
        `useMount: parameter \`fn\` expected to be a function, but got "${typeof fn}".`,
      );
    }
  }

  useEffect(() => {
    fn?.();
  }, []);
};

export default useMount;

我们抛开ahook关于dev的设计,我们看到核心实现就是

tsx
const useMount = (fn: () => void) => {
  useEffect(() => {
    fn?.();
  }, []);
};

useEffect(setup, dependencies?)的结构是这样的,第二个参数是依赖项,useMount的实现中是空数组,就表示在只在组件挂载阶段执行一次。useEffect的依赖项决定了setup执行时机,说个题外话vue的实现中是可以自动追踪setup的代码的依赖项的

关于useEffct的作用这里就不多展开了,我之前也已经写过一篇了,传送门

useMount的实现是多么的简单和美妙,可以跟大家说的是,后面的hook实现就会逐步开始有点复杂咯

比如组件卸载的useUnmount是不是就这么写就完咯

tsx
const useUnmount = (fn: () => void) => {
  useEffect(
    () => () => {
      fn?.();// 就这?
    },
    [],
  );
};

实际上没那么简单,我们放到下一篇来说

useMount和ComponentDidMount的效果真的完全一样吗?

大家有没有问过自己这么一个问题?真的完全一样?

799XDJ.png

React将更新的内容挂载到DOM树后,此时如果是类组件会同步执行componentDidMount或componentDidUpdate,而函数组件同步执行useLayoutEffect的effect

请注意这个词,同步,而useEffect是异步的,那么为什么useMount的实现不用useLayoutEffect呢?这个问题留给大家思考下,有兴趣的同学可以关注下喵爸的小作坊,我们在下期开篇的时候揭晓