设计动机
- 解决组件之间状态难以复用的问题
- 让组件更容易理解
hooks 可以理解为更底层的实现,内部会自动帮我们管理状态,不用像ClassComponent
组件一样在生命周期里写各种逻辑。
hooks
能够让函数式组件拥有内部状态的基本原理,就是利用闭包的特性「闭包对象持久存在」。这让函数组件下次执行时能够获取到上次函数执行结束时state
的值。
回顾下闭包的定义:闭包是一个特殊的对象。它由两部分组件,执行上下文 A 以及 A 中创建的函数 B。当 B 执行时,如果访问了 A 中的变量,那么闭包就产生了。
在 chrome 中,执行上下文 A 的函数名代指闭包。
闭包与模块
当我们定义一个 React 组件,并在其他模块在使用,这时候思考一下模块与闭包的关系:
在模 Counter.jsx 中定义一个 Counter 组件
1 2
| export default function Counter() {}
|
然后在 App 模块中使用 Counter 组件
1 2 3 4 5
| 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; }
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
| const HooksAndClosure = () => { const StateModule = (function SM() { let state = null; return function myUseState(value) { state = state || value;
function dispatch(newValue) { state = newValue; }
return [state, dispatch]; }; })();
const myUseState = StateModule; debugger; const [counter, setCounter] = myUseState(0);
const addCounter = () => { 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 结果。