深入理解 useState

6059f7ab134e0243136fd372f42af270.png

useState

const [state, setState] = useState(initialState)

返回一个 state,以及更新 state 的函数。

在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。

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

const [state, setState] = useState(() => {
  const initialState = []
  for (let i = 0; i < 100; i++) {
    initialState.push(i)
  }
  return initialState
})

如果你更新 State Hook 后的 state 与当前的 state 相同时,React 将跳过子组件的渲染并且不会触发 effect 的执行。(React 使用 Object.is 比较算法 来比较 state。)

function Child () {
  useEffect(() => {
    console.log('Child')
  })
  return <div>Child</div>
}


function App() {
  const [val, setVal] = useState(1)
  const handleClick = (e) => {
    setVal(1)
  }
  return (
    <div className="App">
      <div>{val}</div>
      <button onClick={handleClick}>点击</button>
      <Child></Child>
    </div>
  );
}

某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。

function Child (props) {
  console.log('重新渲染')
  function handleClick () {
    props.dispatch({type: 'increment'})
  }
  return (
    <button onClick={handleClick}>按钮</button>
  )
}


Child = React.memo(Child)


function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);


  function handleClick() {
    dispatch({type: 'increment'})
  }


  return (
    <div>
      <h1 onClick={handleClick}>{state.count}</h1>
      <Child dispatch={dispatch} />
    </div>
  )
}

React 会确保 dispatch 函数的标识是稳定的,并且不会在组件重新渲染时改变。这就是为什么可以安全地从 useEffect 或 useCallback 的依赖列表中省略 dispatch。

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);


  function handleClick() {
    dispatch({type: 'increment'})
  }


  const callback = useCallback(() => {
    dispatch({type: 'increment'})
  }, [])


  return (
    <div>
      <h1 onClick={handleClick}>{state.count}</h1>
      <Child callback={callback} />
    </div>
  )
}

如果直接从useReducer返回操作,则其行为与useState几乎相同。

function App() {
  const [name, setName] = useReducer((_, value) => value, '请输入');
  return (
    <div className="App">
      <input value={name} onChange={e => setName(e.target.value)} />
    </div>
  );
}