React之Hook篇

useState

什么是state?

在react中,数据不称为data,而称为state

data = > state(状态)

React,其实是一个view library (view库—只关注视图)

view => update => 视图的具体状态

state <=> view

​ state和视图是相关联的。

视图是某一个状态发生了变化,所以视图要进行相应的更新。

useState()=> state setState

这个可以解释为,视图需要state状态。通过useState()创建了一个状态和设置状态的方法。

react设计理念

  • react设计理念:一切操作函数化。

  • 在react中,贯彻js—–“函数是一等公民”的理念。

  • react提供的东西都是朴素的,简单的。

  • react大部分都是运行时;vue都是编译时的行为。

使用姿势

根据状态变更,有以下俩种:

  • setXxx(xxx) 简单情况
  • setXxx((x)=>{ return 表达式}) 复杂情况
  1. 常用写法一:setXxx(xxx)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    import { useState } from "react";

    let initialState = 0;
    export default function useStateHook() {
    const [count, setCount] = useState(initialState);

    return (
    <div>
    <h1>{count}</h1>
    <button onClick={() => setCount(count + 1)}>+ </button>
    </div>
    );
    }

    // 变式
    import { useState } from "react";

    let initialState = 0;
    export default function useStateHook() {
    const [count, setCount] = useState(initialState);

    function handleClick() {
    return setCount(count + 1);
    }
    return (
    <div>
    <h1>{count}</h1>
    <button onClick={handleClick}>+</button>
    </div>
    );
    }

  2. setXxx((x)=>{ return 表达式})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { useState } from "react";

let initialState = 1;
export default function useStateHook() {
const [count, setCount] = useState(initialState);

function handleClick() {
return setCount((count) => {
return count * 2;
});
}
return (
<div>
<h1>{count}</h1>
<button onClick={handleClick}>X</button>
</div>
);
}

useReducer

这个hook存在的价值

当存在逻辑分支如:+ — * / => 都是为了计算count,也就是count=>多种操作方案,每一种方案可能有很多地方都需要使用。

reducer是一个非常好去解决,集成、对状态修改的方案集合的一种方法。

useReducer是什么

useReducer含义、包含的概念

含义

useReducer => 会收集所有操作一个数据的方案。

1
const [ count , dispatch ] = useReducer(reducer,0)
包含的概念
dispath

dispatch派发器 => 传入的不同操作类型 => 调用不同的逻辑

dispatch({ type, payload })

以下为拿计算器+ - * /为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
*
* @param {*} count --- 初始值
* @param {*} action --- { type, payload }
* * type --- 动作类型
* * payload --- 动作携带的数据
* @returns
*/
function countReducer(count, { type, payload }) {
switch (type) {
case "PLUS":
return count + payload;
case "MINUS":
return count - payload;
case "TIMES":
return count * payload;
case "DIVIDE":
return count / payload;
default:
return count;
}
}

useReducer用法

useReducer(reducer,initialState)接受俩个参数,第一个是reducer的函数,第二个是initialState初始值。

1
const [ count , dispatch ] = useReducer(reducer,0)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// usReducer

export default function Hook() {
/**
*
* @param {*} count --- 初始值
* @param {*} action --- { type, payload }
* * type --- 动作类型
* * payload --- 动作携带的数据
* @returns
*/
function countReducer(count, { type, payload }) {
switch (type) {
case "PLUS":
return count + payload;
case "MINUS":
return count - payload;
case "TIMES":
return count * payload;
case "DIVIDE":
return count / payload;
default:
return count;
}
}

/**
* * count --- 状态
* * dispatch --- 触发动作 --dispatch({ type,payload});
* * type --- 动作类型
* * payload --- 动作携带的数据
* @returns
*/
const [count, dispatch] = useReducer(countReducer, 0);

/**
* dispatch 触发动作
* dispatch({ type: "PLUS", payload: 1 });
*/
function plus() {
dispatch({ type: "PLUS", payload: 1 });
}
function minus() {
dispatch({ type: "MINUS", payload: 1 });
}
function times() {
dispatch({ type: "TIMES", payload: 2 });
}
function divide() {
dispatch({ type: "DIVIDE", payload: 2 });
}

return (
<div>
<h1>Count: {count}</h1>
<button onClick={plus}>+</button>
<button onClick={minus}>-</button>
<button onClick={times}>x</button>
<button onClick={divide}>÷</button>
</div>
);
}

useEffect

含义

effect—副作用 => 处理视图状态不相关的逻辑

例如:

  • 记时器;
  • console.log();
  • 数据获取;
  • 修改、操作DOM;

React中所有副作用都必须在useEffect()中执行。

在组件挂载的时候存在一系列生命周期函数。在函数式组件中使用useEffect()代替类组件中生命周期的函数(简化)。

作用

  • 手动收集依赖;

  • 处理渲染副作用;

    使用一个返回值(返回值)去处理副作用清理

  • 代替生命周期函数;

    需要根据传递依赖来去代替哪一个生命周期函数

使用

回调函数 + 参数:useEffect(callback,depArr)

第二个参数为:depArr => the Array of dependencies

1
2
3
4
5
6
7
useEffect(() => {

// []:`depArr` => ` the Array of dependencies`

// 清除函数
return(()=>{})
},[])

回调函数callback中的逻辑是否执行,是依赖depArr数组里面状态是否改变。因此depArr一定要在外界保存。

  • 如果第二个参数为undefined => 任何状态改变时,都会重新执行。=> 组件更新的生命周期。

    以下代码:每当按钮点击时,count变更时,useEffect()都会执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    export default function useEffectHook() {
    const [count, setCount] = useState(0);
    useEffect(() => {
    console.log("useEffect");
    });

    return (
    <>
    <div>{count}</div>
    <button onClick={() => setCount((count) => count + 1)}>+</button>
    </>
    );
    }
  • 如果第二个参数不是一个数组则 => 报警告。

    当按钮点击时,会报以下warning

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // react-dom.development.js:86 Warning: useEffect received a final argument that is not an array (instead, received `object`). When specified, the final argument must be an array.

    // Warning: useEffect received a final argument that is not an array (instead, received `object`). When specified, the final argument must be an array.
    export default function useEffectHook() {
    const [count, setCount] = useState(0);
    useEffect(() => {
    console.log("useEffect");
    }, {});

    return (
    <>
    <div>{count}</div>
    <button onClick={() => setCount((count) => count + 1)}>+</button>
    </>
    );
    }

  • 如果第二个参数是一个数组,且是一个空数组 => 回调只会在函数组件调用时执行一次。(是在根组件执行的时候在执行) => componentDidMount

    以下执行,只会执行一次。打印一次。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    export default function useEffectHook() {
    const [count, setCount] = useState(0);
    useEffect(() => {
    console.log("useEffect");
    }, []);

    return (
    <>
    <div>{count}</div>
    <button onClick={() => setCount((count) => count + 1)}>+</button>
    </>
    );
    }
  • 如果第二个参数是一个有元素的数组 => 元素为状态的话,状态更新,回调重新执行一次。=> componentDidUpdate

    以下代码会先执行一次,当按钮点击时,count更新的同时,也会执行打印。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    export default function useEffectHook() {
    const [count, setCount] = useState(0);
    useEffect(() => {
    console.log("useEffect");
    }, [count]);

    return (
    <>
    <div>{count}</div>
    <button onClick={() => setCount((count) => count + 1)}>+</button>
    </>
    );
    }

清楚副作用

1
2
3
4
useEffect(() => {
// 当页面卸载时:compoentWillUnmount
return(()=>{})
},[])

例子:清楚记时器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export default function useEffectHook() {
const [count, setCount] = useState(0);
useEffect(() => {
let t = setInterval(() => {
setCount((count) => count + 1);
}, 1000);

return () => {
clearInterval(t);
t = null;
};
}, []);

return (
<>
<div>{count}</div>
</>
);
}

对比Vue中watchEffect

不同 Vue watchEffect React useEffect
依赖收集 watchEffect自动收集,直接执行我们的回调 需要开发者手动收集
参数 没有第二个参数,第二个参数是watchEffect自动收集、提供的 有第二个参数,需手动追踪依赖
清楚副作用 回调不返回任何,
清楚通过提供的宏(函数执行)onCleanup(xxx)
通过回调 return () => { };去清楚副作用
设计理念 观察副作用 更多去代替生命周期函数

相同点:
都是观察我们的副作用,执行我们的回调