useState、useReducer、useContext

本文由 AI 协助更新

本文介绍三个与状态相关的 Hook:useState(局部状态)、useContext(跨层级读取数据)、useReducer(复杂状态逻辑)。


React Hook 简介

Hook 是让我们在函数组件里「勾入」React 状态与生命周期的函数。

组件负责渲染,React Hook 负责行为和数据。

下面这段代码里,每次调用 setCurrentTodosetInputText 都会触发一次重新渲染(即 App 再次执行并返回新元素)。同一轮渲染里,useState 会返回当前的 state 和稳定的 setter 引用——setter 可以安全地传给子组件或放进依赖数组。

function App() {
  const [currentTodo, setCurrentTodo] = useState('')
  const [inputText, setInputText] = useState('')

  return (
    <View style={styles.container}>
      <TodoItem title={currentTodo || '请输入待办事项'} />
      <TextInput
        value={inputText}
        onChangeText={setInputText}
        placeholder="输入待办事项"
        style={styles.input}
      />
      <Button title="添加" onPress={() => setCurrentTodo(inputText)} />
    </View>
  )
}

原理细节可看 官方文档open in new window。下面分别说三个 Hook。


useState

用于在组件内维护一块局部状态

const [state, setState] = useState(initialState)
  • 返回当前 state 和更新函数 setState
  • 首次渲染时,state 等于 initialState;之后渲染中,initialState 会被忽略。

初始值

若初始状态依赖较重计算,可传函数,只在首次渲染时执行一次:

const [todos, setTodos] = useState(() => loadTodosFromStorage())

更新 state

调用 setState(newState) 会把一次更新加入队列,React 在后续渲染中把 useState 返回的 state 更新为最新值。

若新状态要基于上一次 state 计算,应传函数,避免闭包拿到旧值:

setTodos((prevTodos) => [...prevTodos, newTodo])

注意

  • 能从其它 state 推导出的值,不要再用 useState 存一份,直接计算即可。

useContext

用于在组件树中跨层级读取同一份数据,无需层层传 props。

const value = useContext(SomeContext)

需要先用 createContext 创建 Context,再在祖先节点用 Context.Provider 提供 value,子组件(任意深度)用 useContext 消费。

示例:把「筛选状态」通过 Context 传给深层子组件,子组件不必通过 props 一层层接收。

import React, { createContext, useContext, useState } from 'react'

const TodoFilterContext = createContext<string>('all')

function TodoItem({ title }: { title: string }) {
  const filter = useContext(TodoFilterContext)
  return <Text>{filter}{title}</Text>
}

function TodoList() {
  const todos = ['买菜', '写代码', '运动']
  return (
    <View>
      {todos.map((todo, index) => (
        <TodoItem key={index} title={todo} />
      ))}
    </View>
  )
}

function App() {
  const [filter, setFilter] = useState('all')

  return (
    <TodoFilterContext.Provider value={filter}>
      <View style={styles.container}>
        <Button
          title="切换筛选"
          onPress={() => setFilter(filter === 'all' ? 'active' : 'all')}
        />
        <TodoList />
      </View>
    </TodoFilterContext.Provider>
  )
}

更多在 React Native 里用 Context 做跨层级通讯的实践,可参考 React Native 可复用 UI 小技巧:分离布局组件和状态组件


useReducer

我们很少使用 useReducer;若不确定是否需要,可以先用 useState

const [state, dispatch] = useReducer(reducer, initialArg, init)

适合:状态结构复杂、有多子字段,或下一状态强依赖上一状态、更新逻辑较多时。可减少「多个 useState + 多处 setState」带来的重复逻辑。用法与示例见 官方文档 useReduceropen in new window


目录

  1. 组件 — 认识组件和元素、Props、State、单向数据流
  2. useState、useReducer、useContext
  3. useEffect、useEffectEvent
  4. 闭包陷阱、useRef
  5. useCallback、useMemo、React.memo
  6. Hook 规则、自定义 Hook
上次更新: