const App = () => {
const [n, setN] = useState(0)
return (
<div className="App">
<p>{n}</p>
<p>
<button onClick={() => setN(n + 1)}>+1</button>
</p>
</div>
)
}
export default App;
分析
setState
-
setState一定會(huì)修改中間變量state,將n+1存入state -
setState一定會(huì)觸發(fā)<App/>重新渲染(re-render)
useState
-
useState肯定會(huì)從state讀取n的最新值
setParam(Object.assign({},param,{name:event.target.name}))
/*上下兩種寫法等價(jià)*/
setParam({...param,name: event.target.name})
myUseState
根據(jù)上面的分析模擬一個(gè)myUseState
下面是錯(cuò)誤的寫法
錯(cuò)誤的原因:每次重新渲染
App都會(huì)重新執(zhí)行一次,myUseState也重新執(zhí)行一次let state = initialState
const myUseState = (initialState) => {
let state = initialState //此處出錯(cuò)
const setState = (newState) => {
state = newState
render()
}
return [state,setState]
}
const render = () => {
ReactDOM.render(<App/>,root);
}
const App = () => {
const [n, setN] = myUseState(0)
return (
<div className="App">
<p>{n}</p>
<p>
<button onClick={() => setN(n + 1)}>+1</button>
</p>
</div>
)
}
利用閉包儲(chǔ)存上次的數(shù)據(jù),防止被覆蓋
let _state
const myUseState = (initialState) => {
//_state = _state || initialState _state 為 0 時(shí)產(chǎn)生 bug
_state = _state === undefined ? initialState : _state
const setState = (newState) => {
_state = newState
render()
}
return [_state, setState]
}
const render = () => {
ReactDOM.render(<App/>, root);
}
const App = () => {
const [n, setN] = myUseState(2)
return (
<div className="App">
<p>{n}</p>
<p>
<button onClick={() => setN(n - 1)}>-1</button>
</p>
</div>
)
}
不能寫成
_state = _state || initialState,當(dāng)state為0時(shí),判定為falsy值,自動(dòng)返回initialState的值
解決了上面的問(wèn)題,又出現(xiàn)新的問(wèn)題
如果一個(gè)組件,使用兩次
myUseState怎么辦?
由于所有的數(shù)據(jù)都放在_state里,后面的會(huì)覆蓋前面的_state
多個(gè)myUseState
- 思路一:把
_state變成一個(gè)對(duì)象
每次傳值都得傳遞一個(gè)
key(屬性名),例如:const [n, setN] = myUseState('m':2)
但是useSate并沒有傳遞屬性名,所以_state不是對(duì)象
- 思路二:把
_state變成一個(gè)數(shù)組- 例如:
_state=[0,0]看著還可以,試一試
- 例如:
let _state = []
let index = 0
const myUseState = (initialState) => {
const currentIndex = index //保證返回結(jié)果的數(shù)組的下標(biāo)固定
_state[currentIndex] = _state[currentIndex] === undefined ? initialState : _state[currentIndex]
const setState = (newState) => {
_state[currentIndex] = newState
console.log(_state)
render()
}
index += 1
return [_state[currentIndex], setState]
}
const render = () => {
index = 0 // index重置
ReactDOM.render(<App/>, root);
}
const App = () => {
const [n, setN] = myUseState(2)
const [m, setM] = myUseState(2)
return (
<div className="App">
<p>{n}</p>
<p>
<button onClick={() => setN(n - 1)}>-1</button>
</p>
<p>{m}</p>
<p>
<button onClick={() => setN(m - 1)}>-1</button>
</p>
</div>
)
}
每次
render都會(huì)調(diào)用<App/>,讀取_state內(nèi)的數(shù)據(jù),重新渲染頁(yè)面,因此index要在調(diào)用<App/>之前重置為 0
總結(jié)
- 每個(gè)函數(shù)組件對(duì)應(yīng)一個(gè)FiberNode(虛擬節(jié)點(diǎn))
- 每個(gè)節(jié)點(diǎn)都保存著
state和index -
useState會(huì)讀取對(duì)應(yīng)節(jié)點(diǎn)的state[index] -
index由useState調(diào)用(出現(xiàn))的順序決定,因此,React Hook 只能在頂層調(diào)用,不能條件調(diào)用和在純函數(shù)內(nèi)嵌套調(diào)用 - 在
setState里修改state并觸發(fā)更新 - React 中
state的名字叫memorizedState,index是用鏈表實(shí)現(xiàn)的