Tips
如何组织好状态?
如何在组件之间共享状态?
Keywords
state的结构, 重复/冗余的state, 状态提升, props逐层透传, reducer, context
Center Paragraph
state
的数据结构
先思考下面 5 个问题
- state 是否关联
- state 是否冗余,是否可以通过已存在的state计算出来
- state 是否重复?
- state 是否足够扁平?
cases
ahooks 的 useReactive; use-immer; copy state
state
共享/提升
针对每个 独一无二的状态,我们必须选择一个地方去“拥有”它,并不是说所有的状态只能存在一个位置,而是去一个指定的组件拥有这段信息
受控/非受控组件
非受控组件需要更少的配置
受控组件更加灵活
状态提升步骤
- Remove state form the child components(删除子组件的状态)
- Pass hardcoded data form the closest common parent(最近的祖先,减少层层传递)
- Add state to the common parent(此时的state的数据结构一般会存在区别)
state
保存和重置
联想一张左边存在而右边不存在的图
可以通过设置key来重置组件的状态 React 在乎的是组件在UI中的位置而不是在JSX中的位置
重置
相同的位置,不同的组件 状态重置
保存
相同的位置,相同的组件 状态保留
如果想重置,考虑key
state
迁移成reducer
从设置状态,改成设置动作,每一个action object
代表一组state setting
步骤
- 编写
reducer
纯函数- 接收两个参数 state 和 action
- 将每一个用户操作, handEvent 抽离成逻辑, 使用
switch
语法, 注意break 和 return
- 在组件中使用
reducer
函数
context
可以让
props
直达组件, 解决状态提升后,props
逐层传递的问题
创建一个context
- createContext
- xxxContext.Provider 提供 value
- useContext() 使用 value
- 工作方式类比:CSS 的属性继承(不同的 context 不会彼此覆盖,类似于
color
和background-color
)
Best practice
- I18nProvider GlobalContext LanguageContext
- Connector
- Radio
- Menu
reducer 和 context 相结合
提供 app/page 级别的数据管理
如何组合
- 封装逻辑:
useContext(TasksContext)
- 封装逻辑: 管理
reducer
,提供context value
,并将children
作为props
- 将提供状态的逻辑和
ContextProvider
的逻辑提到一个文件之中
export function TasksProvider({ children }) {
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
return (
<TasksContext.Provider value={tasks}>
{children}
</TasksContext.Provider>
);
}