ee3022d71c95d6d11b9af141e4cbdbbd.png

本文用来记录和回顾react hook 的使用,因为react的函数式组件式本身是没有状态的和其他类似于class组件的功能,所以一开始,函数组件一般只作为容器组件存在,用来展示父级组件传入的值。而16.8之后出现的 hooks(钩子),打开了函数式组件的新大门。

useState

作用:用来记录函数式组件的状态

使用方式

import React, { useState } from 'react'

const [n, setN] = React.useState(0)

举个 ,做一个简单的计数器,可以增加和减少

import React, { useState } from 'react‘
import ReactDOM from 'react-dom‘

const App: React.FC = (props: any) => {
  const [n, setN] = useState(0)
  const handlePlus = () => {
    setN(n+1)
  }
  const handleMinus = () => {
    setN(n-1)
  }
  return (
    <div>
      <span>{ n }</span>
      <button onClick={ handlePlus }>+1</button>
      <button onClick={ handleMinus }>-1</button>
    </div>
  )
}
ReactDOM.render(<App />, document.getElementById('root‘))

此时就能记录该计数器的状态了。

使用注意事项:

  1. 不可以局部更新

如果我们创建的state是一个对象,是否能只更改state中的某个属性。

const [user, setUser] = useState({
  name: 'habitat‘,
  age: 18
})
// 如果我只想修改其中的name属性 
<button onClick={() => { setName('ck') }}>change name </button>
// 预期的话 只会改变其中的name属性,并保留age属性
// 但是实际上 user会直接变成 { name: 'ck‘ },而不保留age属性
// 正确操作是浅拷贝原来的所有属性,然后用新的属性覆盖老的属性
<button onClick={ () => { setName({ ...user, name: 'ck' )} }>change name </button>

2. setXX(obj)如果重新修改对象,其对象地址一定要改变,如果没有改变react就不能监听到其变化。

探究useState如何实现

第一次尝试

// 尝试改写 React.useState

function myUseState(initialValue) {
  var state = initialValue
  const setState = (newState) => {
    state = newState
    // 更新页面
    render()
  }
  return [state, setState]
}

const render = () => ReactDOM.render(<App />, document.getElementById('root‘))

const App: React.FC = (props: any) => {
  const [n, setN] = myUseState(0)
  const handlePlus = () => {
    setN(n+1)
  }
  const handleMinus = () => {
    setN(n-1)
  }
  return (
    <div>
      <span>{ n }</span>
      <button onClick={ handlePlus }>+1</button>
      <button onClick={ handleMinus }>-1</button>
    </div>
  )
}
ReactDOM.render(<App />, document.getElementById('root‘))

失败告终,因为每一次重新render的时候,会使用初始值去改变了原有的已经改变了的值,即myUseState会讲state重置。

第二次冲刺:

将state提升为全局变量

let _state

function myUseState(initialValue) {
  _state = _state === undefined ? initialValue : _state;
  const setState = (newState) => {
    _state = newState
    render()
  }
  return [_state, setState];
}

此时利用全局变量,再一次渲染时,因为全局变量已经被修改了,所以重新渲染调用

const [n, setN] = useState(0)的时候,就不会被直接覆盖掉,所以暂时是成功。为什么说是暂时的呢,因为此时只能使用一个useState。如果使用多个useState的时候,则会导致有很多个全局变量,不好管理。

第三次尝试:

将state记录成数组,并使用index去进行创建和修改

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 = 0
  ReactDOM.render(<App />, document.getElementById('root‘))
}

将state记录成一个数组,当进行初始化时,每一次初始化都能记录到对应的下标,而改变时,也都能都对其对应下标进行改变。但是不能够条件性的进行初始化,这样的话会导致对应下标的数据错误。

总结:

每个函数组件都会对应一个React节点

每个节点都保存着state和index

useState会读取state[index]

index由useState的出现的顺序决定

setState会修改state,并触发更新

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐