vuvivian's blog

越努力,越幸运.

  1. 1. 函数式组件
  2. 2. useState
  3. 3. useEffect
  4. 4. 多个 State 和 Effect情况下
    1. 4.1. 多个 State
    2. 4.2. 多个Effect
  5. 5. useMemo
  6. 6. useCallback
  7. 7. useReducer
  8. 8. useContext
  9. 9. Redux基本思想

Hooks 只能用于 React 函数式组件
函数式组件严格遵循 UI = render(data) 的模式

函数式组件

使用函数式组件时需要将组件申明为 React.FC类型,也就是Function Component的意思,props也需要申明各个参数的类型,并通过泛型传递给React.FC. 还支持children的传入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React from 'react'

type UserInfo = {
name: string,
age: number
}

export const User: React.FC<UserInfo> = ({name, age, children}) => {
return (
<div>
<p>{name}</p>
<p>{age}</p>
<div>{children}</div>
</div>
)
}

const user = <User name="vvvv" age={25} >children text!</User>

useState

const [state, setState] = useState(initialValue);

state 就是一个状态变量,setState 是一个用于修改状态的 Setter 函数,而 initialValue 则是状态的初始值。

优化了this.state 和 this.setState ,不用频繁使用this., 参数可以为基本类型,也可以为对象类型,为对象类型的时候,setXXX并不会像this.setState一样合并旧的状态,而是完全替代了旧的状态,如果要实现合并的话,需要使用扩展运算符, 如下面第二句

1
2
3
4
5
const [cout, setCount] = useState<number>(0)
setArticle({
title:'文章',
...article
})

useEffect

useEffect(effectFn, deps)

effectFn 是一个执行某些可能具有副作用的 Effect 函数(例如数据获取、设置/销毁定时器等),它可以返回一个清理函数(Cleanup),例如大家所熟悉的 setInterval 和 clearInterval

可以将useEffect看作 componentDidMount, componentDidUpdate 和 componentWillUnmount三个函数的组合

1
2
3
4
useEffect(() => {
const intervalId = setInterval(doSomething(), 1000);
return () => clearInterval(intervalId);
});
  1. 每个 Effect 必然在渲染之后执行,因此不会阻塞渲染,提高了性能
  2. 在运行每个 Effect 之前,运行前一次渲染的 Effect Cleanup 函数(如果有的话)
  3. 当组件销毁时,运行最后一次 Effect 的 Cleanup 函数
  4. 将 Effect 推迟到渲染完成之后执行是出于性能的考虑,如果你想在渲染之前执行某些逻辑(不惜牺牲渲染性能),那么可使用 useLayoutEffect[10] 钩子,使用方法与 useEffect 完全一致,只是执行的时机不同。
  5. 在最极端的情况下,我们可以指定 deps 为空数组 [] ,这样可以确保 Effect 只会在组件初次渲染后执行
  6. 强烈建议你不要把 Effect 写成一个 async 函数,useEffect 约定 Effect 函数要么没有返回值,要么返回一个 Cleanup 函数。而这里 async 函数会隐式地返回一个 Promise,直接违反了这一约定,会造成不可预测的结果。

多个 State 和 Effect情况下

多个 State

  1. 在初次渲染时,我们通过 useState 定义了多个状态;
  2. 每调用一次 useState ,都会在组件之外生成一条 Hook 记录,同时包括状态值(用 useState 给3. 定的初始值初始化)和修改状态的 Setter 函数;
  3. 多次调用 useState 生成的 Hook 记录形成了一条链表;
  4. 触发 onClick 回调函数,调用 setS2 函数修改 s2 的状态,不仅修改了 Hook 记录中的状态值,还即将触发重渲染

多个Effect

  1. useState 和 useEffect 在每次调用时都被添加到 Hook 链表中;
  2. useEffect 还会额外地在一个队列中添加一个等待执行的 Effect 函数;
  3. 在渲染完成后,依次调用 Effect 队列中的每一个 Effect 函数。

useMemo

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

其中第一个参数是一个函数,这个函数返回值的返回值(也就是上面 computeExpensiveValue 的结果)将返回给 memoizedValue ,因此以下两个钩子的使用是完全等价的:
useCallback(fn, deps);
useMemo(() => fn, deps);

useMemo 则是一个” 全能型选手 “,能够同时胜任引用相等和节约计算的任务。实际上,useMemo 的功能是 useCallback 的超集。与 useCallback 只能缓存函数相比,useMemo 可以缓存任何类型的值(当然也包括函数)。

useCallback

const memoizedCallback = useCallback(callback, deps);

第一个参数 callback 就是需要记忆的函数,第二个参数就是大家熟悉的 deps 参数,同样也是一个依赖数组(有时候也被称为输入 inputs)。在 Memoization 的上下文中,这个 deps 的作用相当于缓存中的键(Key),如果键没有改变,那么就直接返回缓存中的函数,并且确保是引用相同的函数。

在大多数情况下,我们都是传入空数组 [] 作为 deps 参数,这样 useCallback 返回的就始终是同一个函数,永远不会更新。

useCallback 主要是为了解决函数的” 引用相等 “问题

useReducer

const [state, dispatch] = useReducer(reducer, initialArg, init);

第一个参数 reducer 显然是必须的,它的形式跟 Redux 中的 Reducer 函数完全一致,即 (state, action) => newState。
第二个参数 initialArg 就是状态的初始值。
第三个参数 init 是一个可选的用于懒初始化(Lazy Initialization)的函数,这个函数返回初始化后的状态。
· 使用前提

  1. 需要维护的状态本身比较复杂,多个状态之间相互依赖
  2. 修改状态的过程比较复杂

useContext

通过 useContext ,我们就可以轻松地让所有组件都能获取到 dispatch 函数
// 在某个文件中定义 MyContext
const MyContext = React.createContext(‘hello’);

// 在函数式组件中获取 Context
function Component() {
  const value = useContext(MyContext);
  // ...
}

Redux基本思想

![image-20200508141650250](/Users/wangjuan/Library/Application Support/typora-user-images/image-20200508141650250.png)

本文最后更新于 天前,文中所描述的信息可能已发生改变