浅谈react的useState
前言
了解一个框架的最好方式,便是从它的数据开始,无论是vue,亦或者react,都是如此。
对于react来说,最重要的数据莫过于状态值state,在官方的新文档中,state被描述为一个组件的记忆,我们可以将其理解为显示在屏幕上的动态数据。
为什么需要状态
如果没有状态,react的组件将会是这样的:
export default function Count(){
let num = 0
const handleClick = () => {
num += 1
}
return (
<>
<div>Count: { num }</div>
<button onClick={ handleClick }>+1</button>
</>
)
}
handleClick()
事件处理函数更新了局部变量 num
。但存在两个原因使得屏幕上的num
不会发生变化:
- 局部变量无法在多次渲染中持久保存。 当 React 再次渲染这个组件时,它会从头开始渲染——不会考虑之前对局部变量的任何更改。
- 更改局部变量不会触发渲染。 React 没有意识到它需要使用新数据再次渲染组件。
要使用新数据更新组件,需要做两件事:
- 保留 渲染之间的数据。
- 触发 React 使用新数据渲染组件(重新渲染)。
useState 介绍
现在我们用useState
来新增一个状态:
const [state, setState] = useState(initialState);
其中state
变量用于保存渲染间的数据,而setState
函数则用来更新变量并触发 React 再次渲染组件(即re-render),re-render会再次调用组件函数,并用新状态值替换旧的状态值去执行相应的渲染或操作,我们将 Count 组件用状态改写如下:
export default function Count(){
const [ num, setNum ] = useState(0)
const handleClick = () => {
setNum(num + 1)
}
return (
<>
<div>Count: { num }</div>
<button onClick={ handleClick }>+1</button>
</>
)
}
现在屏幕上的num
就能实现动态变化了。
状态的不可变性
从useState
的使用形式上看,不知道会不会有人有这样的疑问,我们能不能用let
去声明变量?能不能不使用解构赋值?比如写成这样:
let [state, setState] = useState(initialState);
或者这样:
const stateArr = useState(initialState);
react并没有严格限制这些写法,但是它们都是不规范的写法,这里就不得不谈及状态的不可变性了。
在react的每一次组件渲染切片中,其内部的状态值在其渲染到屏幕上之前的那一刻,就已经确定下来了,后续无论什么样的操作,我们都不应该再对其造成影响,为什么要这么做呢?我们知道动画是由一帧帧快照组成的,react也是如此,在react中,每一次渲染就如同一帧快照切片,这样的每一帧快照,它的状态必须自始至终保持不变,以便我们在进行Time Travel
时不会出现前后渲染不一致的问题。
因此,以上的两种不规范的写法,都比较容易导致state
和setState
被重新赋值,从而在不知不觉中打破状态的不可变性原则,我们在日常开发中要注意避免此类写法,遵守react的开发规范,才能尽量减少开发过程中的一些错误。
这里列举一种常见的不规范的写法:
const [state, setState] = useState([
{ a: 0 },
{ b: 1 }
]);
// 不规范写法
const s = [...state]
s[0].a = -1
setState(s)
// 规范写法
const s = [...state]
s[0] = { ...s[0], a: -1 }
setState(s)
维持状态不可变性的最好方法便是拷贝,如何以最低的成本去进行拷贝,需要我们自己去衡量把握。
总结
useState
是用来创建一个新状态的Hook,它让react拥有了动态更新视图的能力,并具有一定的规范性,只要我们遵守其开发规范,就能减少在开发过程中遇到的许多问题。
创作不易,如果觉得对你有帮助的话,麻烦帮我点点赞,点点关注,这对我真的很重要,感激不尽!!!
转载自:https://juejin.cn/post/7241790368949436453