首先,必须称赞一下React官方文档更新了, 虽然只是beta版,但是对于用户学习V18版本的React来说,可以说是新手福音。
这说明:hook
写法被广泛应用,被广大的开发者所认可,Class写法被逐渐遗弃
那么,新文档相比旧文档有什么不同呢?
- 所有代码示例 基于 Hook 编写 而不是 class(类)。
- 添加了 交互式示例 以及可视化的图表。
- 指南部分包含了 挑战赛(和答案!) 以检查你的学习情况。
本文就初窥React提供新手解惑,助您少走弯路,与之共勉
为什么设置状态后不会立即更新
state状态更新
为什么调用3次+1
函数后,只会增加一次呢?
/*
你也可以理解为覆盖操作
*/
setCount1(count1 + 1)
setCount1(count1 + 1)
setCount1(count1 + 1)
// 1
React官方称state为Snapshot
,翻译成中文就是快照
设置状态只会为下一次渲染更改它。请记住,state的值在渲染中永远都不会改变。简单的说:设置状态请求新的重新渲染,但不会在已经运行的代码中更改它。。如同快照一样,此时它的值是固定的
。
state的渲染过程如下所示:
-
setCount1(count1 + 1): count1是0, 这样的setCount1(0 + 1)
React 准备在下一次渲染时更改
count1
为 =>1
-
setCount1(count1 + 1): count1是0, 这样的setCount1(0 + 1)
React 准备在下一次渲染时更改
count1
为 =>1
-
setCount1(count1 + 1): count1是0, 这样的setCount1(0 + 1)
React 准备在下一次渲染时更改
count1
为 =>1
如果你想在重新渲染之前读取最新状态怎么办?
那么需要使用状态更新函数。也叫做“批处理”
批处理
处理状态更新之前, React 会等待事件处理程序中的所有代码都已运行。这也意味着在您的事件处理程序及其中的任何代码完成之前, UI 不会更新。这种行为,也称为批处理
/**
代码同上
*/
setCount2(n => n + 1)
setCount2(n => n + 1)
setCount2(n => n + 1)
// 3
state的渲染过程如下所示:
-
setCount2(n => n + 1):
n => n + 1
是一个函数。React 将其添加到队列中。
-
setCount2(n => n + 1):
n => n + 1
是一个函数。React 将其添加到队列中。
-
setCount2(n => n + 1):
n => n + 1
是一个函数。React 将其添加到队列中。
当您useState
在下一次渲染期间调用时,React 会遍历队列。之前的number
状态是0
,所以这就是 React 传递给第一个更新函数的参数n
。然后 React 将你之前更新函数的返回值作为 传递给下一个更新函数n
,依此类推:
状态更新 + 批处理更新
如果同时使用状态函数和更新函数进行渲染状态,会发生什么呢?
const [number, setNumber] = useState(0);
/**
代码同上
*/
setNumber(number + 5);
setNumber(n => n + 1);
setNumber(number + 10);
setNumber(n => n + 1);
// 11
state的渲染过程如下所示:
-
setNumber(number + 5): count1是0, 这样的setCount1(0 + 5)
React 准备在下一次渲染时更改
number
为 =>5
-
setNumber(n => n + 1):
n => n + 1
是一个函数。React 将其添加到队列中。
-
setNumber(number + 10): count1是0, 这样的setCount1(0 + 10)
React 准备在下一次渲染时更改
number
为 =>10
-
setNumber(n => n + 1):
n => n + 1
是一个函数。React 将其添加到队列中。
异步代码中获取当前的state变量
在异步代码
中读取最新的状态,我们会发现,状态就像快照一样工作,所以你不能从像超时这样的异步操作中
读取最新的状态
我们点击按钮后,重新编辑输入框的内容, 我们会发现,显示的是点击按钮时的内容 而不是当前的内容
我们需要使用ref
存储当前的信息
状态变量
React允许用户使用多个state变量保存state,同时也允许设置单个state来编写程序。
如何去构建state的结构,可以说仁者见仁,智者见智。没有正确错误之分, 但是良好的state可以让组件可读性更强,更容易修改和调试
单一状态变量 vs 状态变量对象
我们以登陆作为功能分析,去分析如何定义state
- 提交输入用户名
- 提交用户输入的密码
- 提交用户是否记住密码
- 用户选择
接受条款
,启用 登陆 按钮
首先,我们定义一个form对象,去存储提交的信息,并将其中的变量赋初值
const [form, setForm] = useState({})
首先,用户名和密码统一成一个变量。
然后,问题来了,记住密码是否作为单独的变量,是否接受条款是否作为单独的变量?
在这里,我觉得以功能作为划分
,所有的提交信息放入到一起,而记住密码通常是应用程序特定的,而与用户信息无关,需要单独去处理这个逻辑
const [form, setForm] = useState({
name: '',
password: '',
comfirmPassword: ''
})
const [isRemember, setIsRemember] = useState(false)
const [isAccept, setIsAccept] = useState(false)
迭代到V2.0,业务需求进行了调整。
- 新增:Root和IMA user
- IMA user需要添加公司账号
- IMA user不能记住密码
于是,是否将Root和IMA用户权限作为单独的变量
就产生分歧了?
正常来说是添加到state对象中的,但是不排除root具有特殊的行为和权限
,作为单独的状态对象可能更合适。
迭代到3.0...
所以说,使用多个state或者单一state,并不是绝对的。是根据业务逻辑和场景进行划分的。但不管怎样: 合并功能类似或者同步更新的状态变量,是最优解
冗余对象和重复对象 vs 变量
从状态中删除冗余和重复数据有助于确保其所有部分保持同步,尽可能的让你的状态变得简单
常见的冗余对象定义:
// fullName = firstName + lastName
const [fullName, setFullName] = useState('')
// length
const [total, setTotal] = useState(0)
很多时候,冗余对象都可以通过设置变量去解决
const fullName = firstName + ' ' + lastName;
const total = array.length;
更新状态数组和对象
更新状态对象
更新对象数组,推荐使用解构赋值
。
setUser({
...user,
name: e.target.value
})
更新状态数组
更新数组时不要使用改变数组的方法,也不要去改变原数组
// 新增
setArr([...arr, {id: 1, name: e.target.value}])
// 编辑
let newArr = arr.map(item => {
if(item.id == id){
return {
...item,
name: e.target.value
}
} else {
return item
}
})
setArr(newArr)
// 删除
let newArr = arr.filter(item => item.id != id)
setArr(newArr)
useState vs useImmer
我们发现,编辑操作的时候,代码非常臃肿,有没有简洁的代码呢?
import { useImmer } from 'use-immer';
const [todos, updateTodos] = useImmer(initialTodos);
function handleChangeTodo(nextTodo) {
updateTodos(draft => {
const todo = draft.find(t =>
t.id === nextTodo.id
);
todo.title = nextTodo.title;
todo.done = nextTodo.done;
});
}
useImmer需要安装
use-immer npm
才能运行 只有来自 state 的数据会被 draft
性能优化
useMemo 缓存优化
useMemo
用于优化代码, 可以让对应的函数只有在依赖发生变化时才返回新的值
useMemo主要用于缓存计算。比如:计算商品的价格,前面所讲的fullName计算,有点类似Vue的computed
function sum() {
return a + b;
}
// 只有在a或者b的值变化时才会触发sum函数执行
const result = useMemo(() => {sum()}, [a, b])
useMemo返回的是return返回的内容
useCallback
使用方式与useMemo类似,只是返回的是一个函数
useCallback返回的是函数
结语
React使用上限很高,下限很低,useState
算是习的入门, 开启了组件化编程之旅。
那么祝您React编程愉快