useState 原理
2022-06-04 本文已影响0人
bestCindy
首先回顾一下 useState
的用法
import React, { useState } from 'react';
function App() {
const [n, setN] = useState(0);
return (
<div>
<button onClick={() => setN(n+1)}>Click me</button>
{n}
</div>
);
}
export default App;
-
useState
: 返回数据 n 和更改数据的函数 setN -
setN
: 修改 n,
我们来实现 useMyState
let _state;
const useMyState = (intiState) => {
_state = _state ?? intiState;
const setState = (newState) => {
_state = newState;
render();
}
return [_state, setState];
}
const render = () => ReactDOM.render(<App />, document.getElementById('root'));
function App() {
const [n, setN] = useMyState(0);
return (
<div>
<p>{n}</p>
<button onClick={() => setN(n+1)}>Click me</button>
</div>
)
}
render();
有多个 state 的情况呢
// 这就是为什么 React 不允许在 if...else... 里面调用 hooks
let _state = [];
let index = 0;
const useMyState = (intiState) => {
let currentIndex = index;
_state[currentIndex] = _state[currentIndex] ?? intiState;
const setState = (newState) => {
_state[currentIndex] = newState;
index = 0;
render();
}
index+=1;
return [_state[currentIndex], setState];
}
const render = () => ReactDOM.render(<App />, document.getElementById('root'));
function App() {
const [n, setN] = useMyState(0);
const [m, setM] = useMyState(0);
return (
<div>
<p>{n}</p>
<button onClick={() => setN(n+1)}>Click me</button>
<p>{m}</p>
<button onClick={() => setM(n+1)}>Click me</button>
</div>
)
}
render();
useState
中的闭包问题
由于 React Hooks 是函数式组件,在异步操作或者使用 useCallBack
、useEffect
、useMemo
的时候会形成闭包
举个例子
import React, { useState } from 'react';
function Demo() {
const [num, setNum] = useState(0);
const handleClick = () => {
setTimeout(() => {
alert(num)
}, 3000)
};
return (
<div>
<div>当前点击了{num}次</div>
<button onClick={() => { setNum(num + 1)}}>点我</button>
<button onClick={handleClick }>展示现在的值</button>
</div>
)
}
export default Demo;

现在我们:
- 点击 “点我” 增加到 1
- 点击 “展示现在的值”
- alert 之前,点击 “点我”,让 num 增加到 2
结果是这样的:

现在用 class 组件 refactor 一编
class Demo extends React.Component {
stete = {
num: 0
}
handleClick = () => {
setTimeout(() => {
alert(this.state.num);
}, 3000)
}
render() {
const { num } = this.state;
return (
<div>
<div>当前点击了{num}次</div>
<button onClick={() => { this.setState({ num : num + 1}) }}>点我</button>
<button onClick={this.handleClick }>展示现在的值</button>
</div>
)
}
}
重复之前的步骤:

从这个例子可以看出,React Hooks 在某一个特定渲染中 state
和 props
是与其绑定的,然而类组件并不是
解决 useState 中的闭包问题
function Demo() {
const nRef = React.useRef(0); // nRef 是一个对象 { current: 0 }
const handleClick = () => {
setTimeout(() => {
alert(nRef.current);
}, 3000);
}
return (
<div>
<p>当前点击了{nRef.current}次</p>
<button onClick={() => (nRef.current += 1)}>点击</button>
<button onClick={handleClick}>展示现在的值</button>
</div>
)
}

我们用 nRef
解决了闭包的问题,但是现在有另一个问题,UI 并不会重新渲染
这时候要用到 useState
触发更新
function Demo() {
const nRef = React.useRef(0); // nRef 是一个对象 { current: 0 }
const update = React.useState()[1];
const handleClick = () => {
setTimeout(() => {
alert(nRef.current);
}, 3000);
}
return (
<div>
<p>当前点击了{nRef.current}次</p>
<button onClick={() => ((nRef.current += 1), update(nRef.current))}>点击</button>
<button onClick={handleClick}>展示现在的值</button>
</div>
)
}
