基本写法
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 _state
const 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不会直接修改原值。