React Hook 学习笔记(一)

2022/7/8 React

# 1. 为什么需要 Hooks

  • 在组件之间复用状态逻辑很麻烦

    • 现有方案是通过 render props 、高阶组件 HOC 、context (providers + consumers),很容易形成组件中的嵌套地狱

    • 如果使用Hook从组件中提取状态逻辑,使得这些逻辑可以单独测试并复用。Hook 使你在无需修改组件结构的情况下复用状态逻辑。这使得在组件间或社区内共享Hook变得更便捷

  • 复杂组件变得难以理解

    • 组件变复杂时,会被各种状态逻辑和副作用充斥。每个生命周期常常包含一些不相关的逻辑。比如组件常常在 componentDidMount 和 componentDidUpdate 中获取数据。但是同一个 componentDidMount中可能包含很多其他的逻辑,如设置事件监听,而之后需要在componentWillUnmount中清除。相互关联且需要按照修改的代码进行拆分,而完全不相关的代码却在同一个地方组合在一起。如此很容易产生BUG,并且导致逻辑不一致。

    • 大部分情况下,不可能将组件拆分为更小的粒度,因为状态无处不在。这也是 Redux 和 Mobx 等状态管理库出现的原因之一但这又增加了很多抽象概念增加成本。

    • Hook将组件中相互管理的部分拆分成更小的函数,而非强制按照生命周期划分。

  • 难以理解的class

    • 需要理解 this 的工作方式 增加开发者的心智负担,虽然这是js的基本功。

    • class 不能很好的压缩,并且会使热重载出现不稳定的情况

    • Hook使开发者在非class的情况下可以使用更多的React特性。从概念上讲,React组件一直更像是函数。而 Hook 则拥抱了函数。

    • 要解决 class 中生命周期函数经常包含不相关的逻辑,但又把相关逻辑分离到了几个不同方法中的问题

# 2.Hook是啥?

专业解读: 系统运行到某一时期时,会调用被注册到该时机的回调函数。

# 几个概念

  • 关注点分离

把做什么和怎么做分离

  • 副作用

副作用是函数式编程中的概念,其中函数式编程是一种编程范式,和 OOP 面向对象编程一样。

  • 类组件是数据和逻辑的封装

也就是说,组件的状态和操作方法是封装在一起。如果选择了类的写法,就应该把相关的数据和操作,都写在同一个 class 里面

  • 函数组件一般来说,只应该做一件事,就是返回一个值。

如果你多个操作,每个操作应该写成一个单独的函数。而且,数据的状态应该与操作方法分离。根据这种理念,React的函数组件只应该做一件事情:返回组件的 HTML 代码,而没有其他功能。

# 总结

React Hook 就是一个特殊的函数,可以让你 钩入 React的特性。

一句话,钩子就是 React 函数组件的副作用解决方案,用来为函数组件引入副作用。函数组件的主体只应该用来返回组件的 HTML 代码,所有的其他操作(副作用)都必须通过钩子引入。

# 3.对Hook的正确认知

先理解一个初中的数学知识,自变量和因变量,

eg: 2x + 1 = y , 其中x的变化会导致y的变化,因此 x 是自变量, y 是因变量。

在react中常见的7个 hooks ,除了 useRef 之外可以通过自变量和因变量进行归类

  • 自变量
/** 保存状态,让函数组件可以使用state */
useState()
/** useState 的替代方案,其实就是用 Redux 的方式合并管理state */
useReducer()
/** 保存上下文  */
useContext()
1
2
3
4
5
6
  • 因变量
/**用于缓存一个因变量。需要显示的指定该因变量依赖的自变量。 */
useMemo()
const y = useMemo(() => 2 * x + 1, [x])
/**第一个参数是接收一个包含命令式,且可能有副作用代码的函数。 */
useEffect()
/**用于缓存一个函数类型的因变量,也需要显示的指定该因变量依赖的自变量。 */
useCallback()
1
2
3
4
5
6
7
  • 其它
/** 保存引用  */
useRef()
1
2

# useState:


tu


tu


tu


# useEffect:

  • 更新机制:默认情况下,useEffect它会在第一次渲染之后和每次更新之后都会执行。因此我们还需掌握如何控制它。

  • 先了解一个什么是纯函数: 对于一个函数,如果固定的输入一定会产生固定的输出
function calcNum(x:number):number {
  return 2 * x + 1
}
1
2
3
  • 通过对纯函数的简单了解,在来理解副作用就会好多了: 列如下面的代码,我们引入了随机数 z 就会导致 输入的 X 是固定的,但输出是不固定的。这时 calcNum 这个函数是包含副作用的。

在 hooks 中 可以使用 useEffect 来定义有副作用的因变量

  • 列如在useEffect中 ,操作DOM 请求数据 这些有副作用的逻辑
function calcNum(x) {
  const  z = Math.random()
  return 2 * x + 1 + z
}

useEffect (()=> {
  document.title = x
},  [x])
1
2
3
4
5
6
7
8

总结: hooks 中 useState定义自变量,useMemo、 useCallback 定义无副作用的因变量,useEffect定义的是有副作用的因变量。 useReducer可以看作是进阶版的useState,它使用 redux 的理念将多个 state 合并为一个,本质上也是因变量。

# useContext

如何跨组件层级来传递自变量,如果不想通过props来一级一级的传递。此时就需要使用 useContext 实现了。

tu

# useRef

useRef(initialValue)
1

useRef 返回一个可变的ref对象,其 .current属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期 内保持不变。

  • 解决引用问题---useRef 会在每次渲染时返回一个 ref 对象
  • 解决一些 this 指向问题
  • 对比 createRef --- 在初始化阶段两个是没有区别的,但是在更新阶段两者是有区别的。
  • 我们知道,在一个局部函数中,函数每一次 update,都会在把函数的变量重新生成一次。 所以我们每更新一次组件, 就重新创建一次 ref, 这个时候继续使用 createRef 显然不合适,所以官方推出 useRef。useRef 创建的 ref 仿佛就像在函数外部定义的一个全局变量,不会随着组件的更新而重新创建。但组件销毁,它也会消失,不用手动进行销毁

总结

createRef 每次渲染都会返回一个新的引用,而 useRef 每次都会返回相同的引用

tu

# 4.Hook使用规则

  • 只在最顶层使用 Hook ,不要在循环,条件或嵌套函数中调用 Hook,确保总是在你的React函数的最顶层以及任何 return 之前调用他们。
  • 只在 React 函数中调用 Hook
  • 在 React 的函数组件中调用 Hook
  • 在自定义 Hook 中调用其他 Hook
  • 用 useXxxx 小驼峰命名自定义 Hook
  • 自定义 Hook 不需要具有特殊的标识。我们可以自由的决定它的参数是什么,以及它应该返回什么(如果需要的话)。换句话说,它就像一个正常的函数。但是它的名字应该始终以 use 开头

# 5.代数效应

  • react的hooks是对代数效应的践行
  • 代数效应是函数式编程的一个概念

一句总结,就是将副作用当参数一样传递

其实我对这个代数效应还是云里雾里的,直到看了 蛋总的这篇 通俗易懂的 代数效应,才慢慢有点思路

蛋总的文章写的真棒👍🏻 (opens new window)

Last Updated: 2022/8/16 下午7:39:26