概要
- 表格的响应式写法(vue v-model(语法糖)的实现)
用state响应输入(告诉我们以下两点)
- 声明式编程(面向对象编程),千万不要写面向过程的代码
- 定义state的原则(尽量少的)
- 定义尽量少的state。
- 删除无用的state 例:状态相反的state,定义一个另一个取反 。
- 同一属性的多个状态,可以合并成一个state
- 一次想不好正常,react建议您重构state,尽可能让它最少
读 选择state结构 有感
- 没什么好说的,规范化state结构,给我们一些情况的优化方案(知道,有意识优化慢慢写出规范化优雅的react代码)
- 下面是具体规范
- 如果两个 state 变量总是一起更新,请考虑将它们合并为一个。
- 仔细选择你的 state 变量,以避免创建“极难处理”的 state。
- 用一种减少出错更新的机会的方式来构建你的 state。
- 避免冗余和重复的 state,这样您就不需要保持同步。
- 除非您特别想防止更新,否则不要将 props 放入 state 中。
- 对于选择类型的 UI 模式,请在 state 中保存 ID 或索引而不是对象本身。
- 如果深度嵌套 state 更新很复杂,请尝试将其展开扁平化。
- 您还应该从“表”对象中删除已删除的项目(以及它们的子项!)以改善内存使用。还可以 使用 Immer 使更新逻辑更加简洁。
读 在组件中共享状态 有感
- 状态提升,把公共的状态提升到父组件中,
- 父子传值: 通过props传递给子组件使用,及传递事件处理程序以便子组件修改父组件状态
- 受控组件;可以被props驱动的组件,非受控被state驱动
读 对state保留和重置 有感 --- 主要告诉我们如何保留state状态
总结
- 保留和重置由两个因素决定,是否同一组件,是否同一ui位置
- 只有同一组件,同一位置才会被保留
强行重置(同一组件同一位置,state状态默认被保留,需求需要重置)
- 给组件添加key属性,重新渲染dom并且state会被重置, state单独保存和dom不在同一作用域
被移除的组件保留state(当我返回上一级,我想获取之前的state)
- 状态提升,把他放在父组件中,通过事件改变状态(通常用法)
- 隐藏使用css display
- 放在localStorage里面,进行状态持久化
Example
- 之前文章学过渲染同一个组件两次,每个副本都会有完全隔离的 state,用此解释是因为你渲染两次就是有上下级关系,在ui上来说位置肯定不同,所以state被重置,状态不会被保留。
- 代码例子重要,主要是第二个自己会歧义(总结下,不看逻辑,就看代码是否写在同一位置)
isShou? <Counter></Counter> : <Counter></Counter>;
{isPlayerA &&
<Counter person="Taylor" />
}
{!isPlayerA &&
<Counter person="Sarah" />
}
1. 被重置解析
- 起初 `isPlayerA` 的值是 `true`。所以第一个位置包含了 `Counter` 的 state,而第二个位置是空的。
- 当你点击“下一位玩家”按钮时,第一个位置会被清空,而第二个位置现在包含了一个 `Counter`。
读 迁移状态逻辑至reducer 有感
杂谈
- 核心概念好熟悉,vuex 或者 pnia用过?
- 为什么叫reducer(和js的方法重名),它们都接受
目前的状态 和 action ,然后返回 下一个状态。这样,action 会随着时间推移累积到状态中。
目的
- 状态更新逻辑集中到同一函数(在思源,不想定义太多事件名称不是经常做吗)
- 触发类型定义在 action函数中
知识点 useReducer ---useImmerReducer
- useReducer hook都可以定义state,相比于useState的setState 变成了dispath来触发action
- useReducer定义的状态也可以使用immer修改---useImmerReducer
- action概念
- dispath 概念
- ruducer与js的reducer入参相同,第一个参数回调函数(action对应的处理逻辑),第二个变量初始值
reducers必须是纯粹的
- 即当输入相同时,输出也是相同的
- 它们不应该包含异步请求、定时器或者任何副作用(对组件外部有影响的操作)
- 它们应该以不可变值的方式去更新 对象 和 数组。(和state一样的建议,把特们看作只读)
- 每个 action 都描述了一个单一的用户交互,即使它会引发数据的多个变化
Example
import {useReducer} from "react"
const [tasks, dispath] = useReducer(taskReducer, initTasks);
function taskReducer(stateData,action){
switch (action.type) {
case 'added': {
stateData.push({
id: action.id,
text: action.text,
done: false,
});
break;
}
case 'changed': {
const index = stateData.findIndex((t) => t.id === action.task.id);
draft[index] = action.task;
break;
}
case 'deleted': {
return stateData.filter((t) => t.id !== action.id);
}
default: {
throw Error('未知 action:' + action.type);
}
}
}
读 使用context深层次传递 有感--(轻便内置的状态管理工具)
- 轻便,内置在react中,学习成本低微小型应用的选择,减少依赖项
- redux更加全面的api,更加统一的设置,适合大型项目
知识点
- createContext Hook --- 单独context.js文件创建Context--reateContext
- useContext Hook --- use对应的context可以拿到context定义的默认值
- createContext()实例的provide组件--- 提供了
createContext().provide --创建组件的provide组件
- 组件的value属性,可以为创建的实例,动态的传入值,最早的定义只是默认值
- provide组件,将会提供他的值,给到被他包裹的所有组件(递归的,直到无尽层)
- provide的值,由他刚创建时的初始值及,及组件的value属性控制,value优先级较大
- Example
1. context.js中创建context
import { createContext } from 'react';
export const LevelContext = createContext(1);
2. 父组件中使用LevelContext.provide组件提供LevelContext的值
import { LevelContext } from './LevelContext.js';
<LevelContext.Provider value={level}>
{children}
</LevelContext.Provider>
3.子子孙孙组件中使用
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';
const level = useContext(LevelContext);
最佳实践
- 使用context感觉也会出现,值的来源模糊的问题,provide提供的是初始化的值需要看代码,让维护变得没那么简单
- 官方推荐在使用 context之前,先看看嵌套是否太深,不深优先使用props这样state来源会更加新奇
- 抽象组件并 将 JSX 作为
children 传递 给它,具体:抽象出需要使用数据的功能,封装成组件,组件在靠近数据源的层级使用,然后通过默认插槽传递给需要使用的层级页面
推荐context使用的情况 --- 越看越像状态管理工具(有了context,为什么还要redux)
- 主题
- 当前账户
- 路由
- 状态管理 --reducer + context ---这个组合怎么有点像eventbus
- 总结就是全局状态的时候都建议使用context
兄弟组件中的传值 提一嘴
- 使用状态提升,把公用的属性提升到父组件进行保存
reducer + context结合
- 分别创建 state-context 和 dispatchcontext
- 通过 .provide 组件的value属性赋值,
- 返回该组件,通过child 默认插槽进行后代的提供
Example
import { createContext } from 'react';
export const TasksContext = createContext(null);
export const TasksDispatchContext = createContext(null);
export function TasksProvider({ children }) {
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
return (
<TasksContext.Provider value={tasks}>
<TasksDispatchContext.Provider value={dispatch}>
{children}
</TasksDispatchContext.Provider>
</TasksContext.Provider>
);
}