useState
这是 React Hooks 中的一个函数,一般用来声明组件的状态,返回值是数组,第一项为数据,第二项为改变数据的方法,同时可以更新 UI,也就是重新执行函数组件
先准备一个自制的 useState
let _state
const useState2=(initialValue)=>{
_state=_state||initialValue
const setState=(newState)=>{
_state=newState
render()
}
return [_state,setState]
}
const App = () => {
const [n, setN] = useState2(0)
return (
<>
<h2>n:{n}</h2>
<button onClick={() => setN(n + 1)}>+1</button>
</>
)
}
const render = () => {
ReactDOM.render(
<App/>,
document.getElementById('root')
)
};
render()
在更新了 state 后,我们需要更新界面,所以我们调用 render,可以发现,可以使用,虽然将整个组件重新执行了一遍
但是我们发现,如果出现第二个 useState2 的使用者,就会引起覆盖的问题,所以我们需要记录下来每次存储的值,最好用数组的顺序来存储,用一个变量来记录当前的位置
let _state=[]
let index=0
const useState2=(initialValue:any)=>{
_state[index]=_state[index]||initialValue
const setState=(newState:any)=>{
_state[index]=newState
render()
}
return [_state[index++],setState]
}
结果还是不能正常使用,原因是 index 的值不会像我们想象的那样,最终为 1,由于我们粗暴地重新执行了组件,那么组件会再调用 2 次 useState2,所以 index 的值会一直变大,造成读取错位的问题
let _state=[]
let index=0
const useState2=(initialValue:any)=>{
let currentIndex=index
_state[currentIndex]=_state[currentIndex]||initialValue
const setState=(newState:any)=>{
_state[currentIndex]=newState
render()
}
index++
return [_state[currentIndex],setState]
}
我们可以利用一个变量来记住变量的位置,每次调用 setState 的时候,它还可以访问到其作用域中保留下来的下标,也就是 currentIndex
但是由于 render 会再次执行组件,也就是会再次执行 useState2,那么 index 会一直变大,造成变量的值一直为初始值
换句话说,如果你声明了 2 个变量,数组中存储的值会越来越多,从而在更新数据的时候,每次都会生成新的值,而不是在原来的基础上修改,所以我们要在更新界面之前,将 index 恢复到最初的状态,也就是 0
我们虽然不能防止 render 不去再次调用 useState2,但是我们能够保证数据的下标是正确的
最后贴上完整例子
let _state=[]
let index=0
const useState2=(initialValue:any)=>{
let currentIndex=index
_state[currentIndex]=_state[currentIndex]||initialValue
const setState=(newState:any)=>{
_state[currentIndex]=newState
render()
console.log(_state);
}
index++
return [_state[currentIndex],setState]
}
const App = () => {
const [n, setN] = useState2(0)
const [m, setM] = useState2(0)
return (
<>
<h2>n:{n}</h2>
<button onClick={() => setN(n + 1)}>+1</button>
<hr/>
<h2>m:{m}</h2>
<button onClick={() => setM(m + 1)}>+1</button>
</>
)
}
const render = () => {
index=0
ReactDOM.render(
<App/>,
document.getElementById('root')
)
};
render()