手写实现 React Hooks useState

287 阅读2分钟

首先,这不是React useState的源码,只是一种实现方法。

useState基本用法就是:传入一个初始值,然后返回该值和修改该值的方法,达到在函数组件修改值并触发视图更新的效果。

const [state,setState] = useState(0)

可以看出,hooks useState 函数通过数组的形式返回了两个值,类似下面这样的代码,通过解构的方式导出为不同的名称。

function useState (initialState) {
    return [_state,_setState]
}

函数中应该保存一份初不同和一个改变该值的方法,需要注意,因为每次setState都会执行render(),导致_state每次都是初始值。所以需要判断是否已经存在_state,不存在则赋予初始值,否则取上次的值。

let _state
function useState (initialState) {
    _state = _state === undefined ? initialState :_state
    const _setState = (newValue) => {
        _state = newValue
        render()
    }
    return [_state,_setState]
}
function render () {
    ReactDOM.render (
        <App />
        document.querySelect('#app')
    )
}
return {useState}

每次设置新值时,手动调用render方法,可以看到值在发生变化,当然在React源码中做了很多处理,不会这么简单。

const {useState} = MyState
function App (){
  const [count ,setCount] = useState(0)
  return (
    <div>
      <h2>{count}</h2>
      <button onClick={()=>setCount(count+1)}>+1</button>
    </div>
  )
}

不过有一种情况,当setState时传入一个回调函数,需要执行该回调函数,可以写出下面的的代码

const _setState =  (params) => {
      if(typeof params === 'function'){
        _state = params(_state)
      }else{
        _state = params
      }
      render()
    }

以上写法只适用单个useState的情况,不过在业务中极少只写一个setState,更多是以下的情形

const [name,setName] = useState('ahjet')
const [guitar,setGuitar] = useState('ibanez')

这种情况就需要保存多个值的标识,通过标识取值个设置新值,方法是通过有序列表数组的方法保存各个state和setState函数,当设置新值时,就去执行对应下标下的函数。

const AhjetState = (()=>{
    let states = [] //保存值的数组
    let stateSetters = [] //保存设置新值函数的数组
    let stateIndex = 0 // 下标
})

首先是取值的方法,判断值数组中对应下标是否已存在值,是则取该值,否则赋予新值。

function createState(initialState,stateIndex){
    return states[stateIndex] !== undefined ? states[stateIndex] :initialState
  }

然后是设置新值的方法,也是通过判断是否是一个函数,是则执行该函数。通过返回一个设置新值的函数,保存在函数数组中。

function createStateSetter (stateIndex){
    return function(newState){
      if(typeof newState === 'function'){
        states[stateIndex] = newState(states[stateIndex])
      }else{
        states[stateIndex] = newState
      }
      render()
    }
  }

最后就是useState主方法。每次调用useState时,通过闭包的方式保存了stateIndex的值,每次stateIndex会增加1,如果函数数组中没有更新值的方法,则会保存该方法,然后通过下标取出对应的值和对应的更新方法。

function useState(initialState){
    states[stateIndex] = createState(initialState,stateIndex)
    if(!stateSetters[stateIndex]){  stateSetters.push(createStateSetter(stateIndex))
    }
    const _state = states[stateIndex],
          _setState = stateSetters[stateIndex];
    stateIndex ++
    return [_state,_setState]
  }