State:组件的记忆
-
定义:组件通常需要更具交互更改屏幕上显示的内容,在 React 中,这种组件特有的记忆被称为 state。
-
背景:普通变量无法满足
-
原因:
- 局部变量无法在多次渲染中持久保存。react再次渲染组件是,它会从头开始渲–不会考虑之前对局部变量的任何更改。
- 更改局部变量不会触发渲染。React没有意识到它需要使用新数据再次渲染组件。
-
想要达到预期的效果:
- 保留,渲染之间的数据
- 触发 React 使用新数据渲染组件(重新渲染)
-
如何解决:
useState Hook 提供了这两个功能- State 变量用于保存渲染间的数据
- State setter 函数 更新变量并触发React 再次渲染组件。
-
使用:
### 添加:
import { userState } from 'react' // 从文件顶部的React中导入 useState
### 使用
const [thing, setThing] = useState(0)
// thing 是一个 state 变量, setThing 是对应的 setter 函数
// 这里的 [ 和 ] 语法称为数组结构。它允许你从数组中读取值。useState 返回的数组总是正好有两项
// 惯例是将这对返回值命名为 const [thing, setThing]。你也可以将其命名为任何你喜欢的名称,但遵照约定俗成能使跨项目合作更易理解。
useState 的唯一参数是 state 变量的初始值。在这个例子中,thing 的初始值被useState(0)设置为 0。
每次你的组件渲染时,useState 都会给你一个包含两个值的数组:
state 变量 (thing) 会保存上次渲染的值。
state setter 函数 (setThing) 可以更新 state 变量并触发 React 重新渲染组件。
- react - 第一个 Hook ( 英 [hʊk] 美 [hʊk] : 钩子 )
在 React 中,useState 以及任何其他以 ‘use’开头的函数 都被称为Hook
Hook 是特殊的函数,只在 React 渲染时有效(ps:Hooks – 以 use 开头的函数 – 只能在组件或者自定义 Hook 的最顶层 调用。)
一个组件可以定义多个 state 变量
State 是隔离且私有的(state 完全私有于声明它的组件,父组件无法更改它。)
- 总结
- 当一个组件需要在多次渲染间“记住”某些信息时使用 state 变量。
- State 变量是通过调用 useState Hook 来声明的。
- Hook 是以 use 开头的特殊函数。它们能让你‘hook’到像 state 这样的React 特性中。
- Hook 可能会让你想起 import:它需要在非条件语句中调用。调用时,仅在组件顶层被调用才有效。
- useState Hook 返回一堆值:当前的 state 及 更新它的函数
- 你可以拥有多个 state 变量。在内部,React 按顺序匹配他们。
- State 是组件私有的。每个副本都有独属于自己的state。
文章参考:https://react.docschina.org/learn/state-a-components-memory
state 如同一张快照
–设置它不会更改你已有的 state 变量,但会触发重新渲染。
- 设置 state 会触发渲染
- 渲染会及时生成一张快照
- 随时间变化的 state
把一些列 state 更新 加入队列
- 设置 state 不会更改现有渲染中的变量,但会请求一次新的渲染。
- React 会在事件处理函数执行完成之后处理 state 更新。这被称为批处理。
- 要在一个事件中多次更新某些 state,你可以使用 setNumber(n => n + 1) 更新函数。
更新 state 中的对象
state 中可以保存任意类型的JavaScript值,包括对象
不应该直接修改保存在 React state中的对象。
更新对象时,需要创建一个新的对象,然后将state更新为此对象
- 什么事 mutation ?
- JS 中 不可变的:数字 字符串 布尔值 不可变(immutable),意味着不能被改变或是只读。更改时可以通过替换他们的值触发一次重新渲染。
- state 存放对象:
const [position,setPosition] = useState({x:0,y:0});
从技术上来讲,可以改变对象自身的内容。 这样做的时候,就知道了一个 mutation
严格来说 React state 中存放的对象是可变的,但是最好像处理JS中那样视为不可变。修改时替换值。
- 将 state 视为只读(把所有存放在 state 中的 JavaScript 对象都视为只读的)
- 使用展开语法复制对象
展开语法 ...xxx
只修改 first 参数
const [thing,setThing] = useState({
first:'23',
second:'232',
third:'2345'
})
// 第一种
setThing({
first:'12',
second:'232',
third:'2345'
})
// 第二种 对象展开语法 ...
setThing({
...thing,// 复制 thing 中所有字段
first:'12' // 但是覆盖 first 字段
})
!!!请注意 ... 展开语法本质是是“浅拷贝”——它只会复制一层。这使得它的执行速度很快,但是也意味着当你想要更新一个嵌套属性时,你必须得多次使用展开语法。
更新一个嵌套对象
const [thing,setThing] = useState({
first:'1',
seconds:{
secFirst:'21',
secSecond:'22',
secThird:'23'
}
})
setThing({
...thing,
seconds:{
...thing.seconds,
secSecond:'24'
}
})
使用 Immer 编写简洁的更新逻辑
- 扁平化
- immer 是一个非常流行的库
updateThing(draft =>{
draft.seconds.secSecond = '24'
})
※ Immer 如何运行
> 由 Immer 提供的 draft 是一种特殊类型的对象,被称为 Proxy,
> 它会记录你用它所进行的操作。这就是你能够随心所欲地直接修改对象的原因所在!
> 从原理上说,Immer 会弄清楚 draft 对象的哪些部分被改变了,
> 并会依照你的修改创建出一个全新的对象。
- 总结
将 React 中所有的 state 都视为不可直接修改的。
当你在 state 中存放对象时,直接修改对象并不会触发重渲染,并会改变前一次渲染“快照”中 state 的值。
不要直接修改一个对象,而要为它创建一个 新 版本,并通过把 state 设置成这个新版本来触发重新渲染。
你可以使用这样的 {…obj, something: ‘newValue’} 对象展开语法来创建对象的拷贝。
对象的展开语法是浅层的:它的复制深度只有一层。
想要更新嵌套对象,你需要从你更新的位置开始自底向上为每一层都创建新的拷贝。
想要减少重复的拷贝代码,可以使用 Immer。