用法
const [count, setCount] = useState(0)
const handleClick = () => {
setCount((count) => count + 1)
}
實(shí)現(xiàn):
- 要返回一個數(shù)組旬盯,第一個是當(dāng)前的 state台妆,第二個是修改 state 的方法
const useState = (inital) => {
const stateHook = {
state: inital
}
const setState = (action) => {
stateHook.state = action(stateHook.state)
}
return [stateHook.state, setState]
}
- 更新頁面使用直接的 update 里的邏輯修改 wipRoot
let currentFiber = wipFiber
const setState = (callback) => {
stateHook.state = callback(stateHook.state)
wipRoot = {
...currentFiber,
alternate: currentFiber
}
nextWorkOfUnit = wipRoot
}
- 因?yàn)槊看挝覀兏马撁嫖覀兊膕tate 的值都會重置所以我們需要存儲我們的 state 的值,存在我們之前的 fiber 上
const useState = (state) => {
let currentFiber = wipFiber
const oldHook = currentFiber.alternate?.stateHook
const stateHook = {
state: oldHook ? oldHook.state : state
}
currentFiber.stateHook = stateHook
const setState = (callback) => {
stateHook.state = callback(stateHook.state)
wipRoot = {
...currentFiber,
alternate: currentFiber
}
nextWorkOfUnit = wipRoot
}
return [stateHook.state, setState]
}
- 實(shí)現(xiàn)多個 useState
上面的 useState 如果我們頁面里有多個后面的會把前面的覆蓋
- demo
const Foo = () => {
console.log('foo render')
const [count, setCount] = React.useState(0)
const [age, setAge] = React.useState(1)
const handleClick = () => {
setCount((count) => count + 1)
setAge((age) => age + 2)
}
return (
<div>
<h1>foo</h1>
{count}
{age}
<button onClick={handleClick}>click</button>
</div>
)
}
點(diǎn)擊都會每次 +2
解決方式:使用一個數(shù)組和索引來對應(yīng)關(guān)系
每次updateFunctionComponent 的時候初始化數(shù)組和索引值
let stateHooks
let stateHooksIndex
const updateFunctionComponent = (fiber) => {
+ stateHooks = []
+ stateHooksIndex = 0
wipFiber = fiber
const children = [fiber.type(fiber.props)]
initChildren(fiber, children)
}
const useState = (inital) => {
let currentFiber = wipFiber
const oldHook = currentFiber.alternate?.stateHooks[stateHooksIndex]
const stateHook = {
state: oldHook ? oldHook.state : inital
}
stateHooks.push(stateHook)
currentFiber.stateHooks = stateHooks
const setState = (action) => {
stateHook.state = action(stateHook.state)
wipRoot = {
...currentFiber,
alternate: currentFiber
}
nextWorkOfUnit = wipRoot
}
stateHooksIndex++
return [stateHook.state, setState]
}
- 批量處理 action
const useState = (inital) => {
let currentFiber = wipFiber
const oldHook = currentFiber.alternate?.stateHooks[stateHooksIndex]
const stateHook = {
state: oldHook ? oldHook.state : inital,
queue: oldHook? oldHook.queue : []
}
stateHook.queue.forEach((action) => stateHook.state = action(stateHook.state))
stateHook.queue = []
stateHooks.push(stateHook)
currentFiber.stateHooks = stateHooks
const setState = (action) => {
stateHook.queue.push(action)
wipRoot = {
...currentFiber,
alternate: currentFiber
}
nextWorkOfUnit = wipRoot
}
stateHooksIndex++
return [stateHook.state, setState]
}
每次steState 不會直接修改胖翰,是在dom重新渲染后才修改
- 支持直接傳入值
setAge(4)
const setState = (action) => {
+ stateHook.queue.push(typeof action === 'function' ? action : () => action)
...
}
- 減少不必要的更新
我們上面的代碼不管我們后面修改的 state和之前一不一樣都不重新 render
const setState = (action) => {
const eagerState = typeof action === 'function' ? action(stateHook.state) : action
if (eagerState === stateHook.state) {
return
}
....
}