基本写法
function App(){const [n,setN] = useState(0);return (<div><p>{n}</p><button onClick={()=>setN(n+1)}>{n}</button></div>)}ReactDOM.render(<App />,rootElement)
如上代码所示,这是一个简单的useState应用,单击button,n+1。
那么useState的运行原理是怎样的呢?setN直接将n变成n+1?
运行原理
首次渲染 render ,调用App(),得到一个虚拟的div,然后React将其变成一个真实的div。
用户点击button,调用setN(n+1),再次 render ,调用App(),得到一个新的虚拟div,React将新旧对比获得一个新的真实div。
每次调用App(),都会运行useState(0)。但每次n的值都不同,它是如何做到的?
几个问题
执行setN的时候会发生什么?n会变吗?App()会重新执行吗?
n不会变,App()会重新执行如果App()会重新执行,那么useState(0)的时候,n每次的值会有不同吗?
n的值会不同
分析
每个组件都有自己的数据x,将其叫做state
setN:
setN一定修改了某个数据x,即将n+1存入x
setN一定会触发重新渲染
useState:
useState一定会从x中读取n的最新值
尝试实现useState
let _stateconst myUseState = (initialValue)=>{_state = _state === undefined ? initialValue : _state;const setState = (newValue) => {_state = newValue;render();//这里的render指的是重新渲染API,这里是最无脑的写法}return [_state,setState]};const render = () =>{ReactDOM.render(<App />,rootElement)};function App(){const [n,setN] = myUseState(0);return (<div><p>{n}</p><button onClick={()=>setN(n+1)}>{n}</button></div>)};ReactDOM.render(<App />,rootElement)
但这种写法存在问题,那就是无法使用多个useState。
如何使用多个useState
思路:将_state变成一个数组。
let _state = [];let index = 0;//声明一个数组和顺序function myUseState(initialValue) {const currentIndex = index;index += 1;_state[currentIndex] = _state[currentIndex] || initialValue;const setState = newState => {_state[currentIndex] = newState;render();};return [_state[currentIndex], setState];}const render = () => {index = 0;//每次重新渲染需要重置index,否则数组长度会增加,导致失败ReactDOM.render(<App />, rootElement);};function App() {const [n, setN] = myUseState(0);const [m, setM] = myUseState(0);console.log(_state);return (<div className="App"><p>{n}</p><p><button onClick={() => setN(n + 1)}>+1</button></p><p>{m}</p><p><button onClick={() => setM(m + 1)}>+1</button></p></div>);}ReactDOM.render(<App />, rootElement);
这样做就需要每个useState需要按一定顺序调用,否则会出现问题,不能缺项,顺序不能发生变化。
React也是这样规定的。
简单总结
用自己的话,简单总结下useState
- 每个函数组件对应一个React节点
- 每个节点保存这state和index
- useState会读取state[index]
- index由useState出现的顺序决定
- steState会修改state,触发重新渲染
- useState不能局部更新
- setState(obj)后,obj的地址会发生变化
- useState也能够接收函数
更新问题
function App() {const [n, setN] = React.useState(0);const log = () => setTimeout(() => console.log(`n: ${n}`), 3000);return (<div className="App"><p>{n}</p><p><button onClick={() => setN(n + 1)}>+1</button><button onClick={log}>log</button></p></div>);}ReactDOM.render(<App />, rootElement);
这是一个简单的页面,点击+1button时n+1,点击log时三秒后log n。
这里有两种情况:
先点击+1,再点击log
n+1,log也是n+1先点击log,再点击+1
先显示n+1,log的结果却是n
这是为什么?
第二种情况的执行原理是:
点击log,然后log函数读取现在的n,当点击+1时,触发重新渲染得到一个新的n,注意新旧两个n是同时存在的,不是同一个,因为React不会直接修改原值。
