React Hooks -- useState

8,098 阅读3分钟

一、每次渲染都是独立的闭包

每次渲染都有它自己的props、state和事件处理函数。当点击事件更新 state 时,函数组件会重新被调用,因为每次渲染都是独立的,所以取到的值不会受后面的操作影响。

import React, { useState } from 'react'

function Example1(){
  const [number, setNumber] = useState(0)
  const alertNumber = () => {
    setTimeout(()=>{
      alert(number)
    },3000)
  }
  return (
    <div>
      <p>{number}</p>
      <button onClick={() => setNumber(number+1)}>+</button>
      <button onClick={alertNumber}>alertNumber</button>
    </div>
  )
}

export default Example1

二、函数式更新

如果新的 state 需要通过使用先前的 state 计算得出,那么可以将回调函数当作参数传递给 setState。该回调函数将接收先前的 state,并返回一个更新后的值。

import React, { useState } from 'react'

function Example2(){ 
  const [number, setNumber] = useState(0)

  const lazy1 = () => { 
    setTimeout(() => { 
      // 获取点击按钮时的 state
      setNumber(number+1)
    }, 3000)
  } 

  const lazy2 = () => { 
    setTimeout(() => { 
      // 每次执行时都会再去获取新的 state,而不是使用点击触发时的 state
      setNumber(number=> number+1)
    }, 3000)
  }

  return (
    <div> 
      <p>{number}</p> 
      <button onClick={() => setNumber(number+1)}>+</button><br />
      <button onClick={lazy1}>lazy1:只能获取点击按钮时候的状态</button><br />
      <button onClick={lazy2}>lazy2:每次执行都会重新获取state, 所以获取的都是最新的state</button> 
    </div> 
  )
}

export default Example2

三、惰性 initialState

initialState 参数只会在组件的初始化渲染中起作用,后续重新渲染时会被忽略。如果 initialState 需要通过复杂计算获得,则需要传入一个函数,在函数中计算并返回 initialState,此函数只会在初始渲染时被调用一次。

import React, { useState } from 'react'

function Example3(props){
  console.log('Counter render')
  // getInitState 函数只会在初始渲染时执行一次,后续更新状态重新渲染组件时,不会再被调用
  const getInitState = () => { number: props.number }
  const [counter, setCounter] = useState(getInitState)

  return (
    <div>
      <p>{counter.number}</p>
      <button onClick={() => setCounter({ number: counter.number + 1 })}>+</button>
      <button onClick={() => setCounter(counter)}>setCounter</button>
    </div>
  )
}

export default Example3

四、替换非合并

Hook 内部使用 Object.js 来比较新/旧 state 是否相等。

  • 与 class 组件中的 setState 方法不同,useState 传入的状态值没有变化,则不重新渲染。
  • 与 class 组件中的 setState 方法不同,useState 不会自动合并更新对象。

可以用函数式的 setState 结合展开运算符来达到合并更新对象的效果

import React, { useState } from 'react'

function Example4(){ 
  const [counter, setCounter] = useState({ name:'计数器', number:0 })
  console.log('render Counter') 
  
  return(
    <div> 
      <p>{counter.name}:{counter.number}</p> 
      <button onClick={() => setCounter({ ...counter, number: counter.number + 1 })}>+</button> 
      <button onClick={() => setCounter(counter)}>++</button> 
    </div> 
  )
}

export default Example4

总结

  1. 通过在函数组件中调用 useState 来给函数组件添加内部state。
  2. useState 会返回一对值:当前状态 和 更新状态的函数。
  3. react 会在重复渲染时保留这个 state。
  4. 当更新状态函数被调用后,函数组件会被重新渲染并更新最新的当前 state。
  5. 更新状态函数是更新 state 变量的方式是替换而不是像 class 的合并。
  6. useState 唯一的参数就是 initialState,initialState 参数只有在第一次渲染时会被使用。
  7. 一般来说,在函数退出后变量就会“消失”,内存空间会被释放,而 state 中的变量会被 react 保留。
  8. 函数内部不存在this调用。

参考/引用