Hooks与闭包

设计动机

  1. 解决组件之间状态难以复用的问题
  2. 让组件更容易理解

hooks 可以理解为更底层的实现,内部会自动帮我们管理状态,不用像ClassComponent组件一样在生命周期里写各种逻辑。

hooks能够让函数式组件拥有内部状态的基本原理,就是利用闭包的特性「闭包对象持久存在」。这让函数组件下次执行时能够获取到上次函数执行结束时state的值。

回顾下闭包的定义:闭包是一个特殊的对象。它由两部分组件,执行上下文 A 以及 A 中创建的函数 B。当 B 执行时,如果访问了 A 中的变量,那么闭包就产生了。
在 chrome 中,执行上下文 A 的函数名代指闭包。

闭包与模块

当我们定义一个 React 组件,并在其他模块在使用,这时候思考一下模块与闭包的关系:
在模 Counter.jsx 中定义一个 Counter 组件

1
2
// Counter.jsx
export default function Counter() {}

然后在 App 模块中使用 Counter 组件

1
2
3
4
5
// App.jsx
import Counter from './Counter';
export default function App() {
return <Counter />;
}

上面的代码转换成伪代码

1
2
3
4
5
6
7
8
9
10
11
const CounterModule = (function() {
return function Counter() {}
})()

const AppModule = (function A() {
const Counter = CounterModule

return funtion App() {
return Counter()
}
})()

当 App 函数执行时,访问了 AppModule 中定义的变量对象 Counter,那么闭包「Closure[A]」就产生了。

也就说,每一个 JS 模块都可以认为是一个独立的作用域,当代码执行时,该词法作用域创建执行上下文,如果模块内部,创建了可供外部引用访问的函数时,就为闭包的产生提供了条件,只要该函数在外部执行访问了模块内部的其他变量,闭包就会产生

函数组件本质上就是一个函数,当我们在一个函数组件 A 中导入另一个函数组件 B,并在 A 中执行时 B 时,闭包就会产生。

hooks 与闭包

这是一个很常规的组件,我们用伪代码来分析下

1
2
3
4
5
6
7
8
9
10
import { useState } from 'react';

function Demo() {
// 使用数组解构的方式,定义变量
const [counter, setCounter] = useState(0);

return (
<div onClick={() => setCounter(counter + 1)}>hello world, {counter}</div>
);
}

先看 useState 的伪代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function SM() {
let state = null;
return function myUseState(value) {
state = state || value; // 第一次调用没有初始值,因此使用传入的初始值赋值

function dispatch(newValue) {
state = newValue;
// 假设此方法能触发页面渲染
// render()
}

return [state, dispatch];
};
}

Demo 模块转为伪代码,代码如下:

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
// hooks与闭包
const HooksAndClosure = () => {
// 验证:myUseState执行时闭包产生了
const StateModule = (function SM() {
let state = null;
return function myUseState(value) {
state = state || value; // 第一次调用没有初始值,因此使用传入的初始值赋值

function dispatch(newValue) {
state = newValue;
// 假设此方法能触发页面渲染
// render()
}

return [state, dispatch];
};
})();

const myUseState = StateModule;
debugger;
const [counter, setCounter] = myUseState(0);

const addCounter = () => {
// 调用setCounter时也会产生闭包
debugger;
setCounter(counter + 1);
};

return (
<>
<h1>状态是如何被保存的</h1>

<button onClick={addCounter}>add Counter</button>
</>
);
};
export default HooksAndClosure;

StateModule 返回 内部函数 myUseState 赋值给 myUseState,myUseState执行时(第 22 行)访问了 StateModule 的 state 变量(第 8 行),此时闭包对象「Closure(SM)」就产生了

现在我们知道了调用 useState 会产生闭包,那么调用 setState 会不会产生闭包呢?答案是会



可以看到,调用 dispatch 时,闭包对象Closure(SM)产生了,这个闭包对象保存着上次的 state 结果。

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×