React之状态管理

180 阅读3分钟

使用状态响应输入&&正确选择状态结构

使用 React,不用直接从代码层面修改 UI,只需要描述组件在不同状态下希望展现的 UI,然后根据用户输入触发状态更改。(此概念详情可以参考卡颂老师的《React设计原理》)

比如

const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState('');
// 以上写法会产生多余的状态
const fullName=firstName+lastName
// 在组件渲染时通过计算fullName 可以简化代码,并方便管理

在组件间共享状态(父==>子)

有时候需求需要两个组件的状态始终同步更改。要完成状态共享,可以将相关状态从这两个组件上移除,并把这些状态移到最近的父级组件,然后通过 props 将状态传递给这两个组件。这被称为“状态提升”

使用方法

将多个组件的共同的state转移到共同的父组件中,然后在父组件中通过 props 把参数传递下去,并向下传递事件处理程序,以便子组件可以改变父组件的 state

受控组件&非受控组件

  • “受控”(由 prop 驱动):组件中的信息是由 props 而不是其自身状态驱动
  • “不受控”(由 state 驱动):即父组件无法控制在子组件中的状态

保留和重置状态

只要在相同位置渲染的是相同组件, React 就会保留状态。

重置使数据更新

  • 可通过向组件传递一个唯一 key来 强制重置其状态,会使用新数据和UI来重新渲染组件。
  • 不要嵌套组件的定义,否则你会意外地导致 state 被重置。

为被切换的组件保留 state

  • 渲染出所有的组件,使用css的方式来控制显隐。不过涉及到大量的DOM节点会影响性能。
  • 进行状态提升在父组件中保存信息。
  • 保存在内存里

提取状态逻辑到 reducer 中

对于那些需要更新多个状态的组件来说,在组件外部将所有状态更新逻辑合并到一个称为 “reducer” 的函数中。使用useReducer来创建对应修改动作的指令集,调用时只需要dispatch对应的方法和参数

  • reducers 必须是纯粹的。  这一点和状态更新函数是相似的,reducers 在是在渲染时运行的(actions 会排队直到下一次渲染)。 这就意味着 reducers 必须纯净,它们不应该包含异步请求、定时器或者任何副作用(对组件外部有影响的操作)。它们应该以不可变值的方式去更新对象和数组(使用 useImmerReducer 简化 reducers)
  • 每个 action 都描述了一个单一的用户交互

简单的使用

//声明
const [tasks, dispatch] = useReducer(
    tasksReducer,
    initialTasks
  );
  
  //定义
 function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [...tasks, {
        id: action.id,
        text: action.text,
        done: false
      }];
    }
    case 'changed': {
      return tasks.map(t => {
        if (t.id === action.task.id) {
          return action.task;
        } else {
          return t;
        }
      });
    }}
    // 调用 
  function handleAddTask(text) {
    dispatch({
      type: 'added',
      id: id,
      text: text,
    });
  }

reducer实现

export function useReducer(reducer, initialState) {
  const [state, setState] = useState(initialState);

  function dispatch(action) {
    const nextState = reducer(state, action);
    setState(n=>reducer(n));
  }

  return [state, dispatch];
}

使用 Context 进行深层数据传递

一般我们通过 props 将参数从父组件传递给子组件,如果是深层次的组件,props就不再方便了,这时候我们可以使用Context来进行透传。

使用方法

  1. 通过 export const MyContext = createContext(defaultValue) 创建并导出 context。
  2. 在子组件中,把 context 传递给 useContext(MyContext) Hook 来读取它。
  3. 在父组件中把 children 包在 <MyContext.Provider value={...}> 中来提供 context

Context 的使用场景 

  • 修改主题模式。
  • 登录的用户信息的透传
  • 使用 context 来保存当前路由。这
  • 状态管理  

Reducer 结合 Context使用

  1. 创建两个 context (一个用于 state,一个用于 dispatch 函数), 让组件的 context 使用 reducer, 使用组件中需要读取的 context。 将reducer和context相关代码组织到同一个文件中,导出一个像 TasksProvider 可以提供 context 的组件,以及像 useTasks 和 useTasksDispatch 这样的自定义 Hook。