useState基础用法
React Hook useState基础用法
# useState概念解释
第一个Hook(钩子函数)是useState,他的作用是"钩住"函数组件中自定义的变量。
"钩住"?
之前那篇"React Hook 简介"中那句话:Hook本身单词意思就是"钩子",作用就是"钩住某些生命周期函数或某些数据状态,并进行某些关联触发调用"。
"如何钩住"?在React底层代码中,是通过自定义dispatcher,采用"发布订阅模式"实现的。
关于"钩子"、"钩住"、"如何钩住"的概念以后再学习其他Hook函数的时候就不再解释啦。
# useState是来解决类组件什么问题的?
答:useState能够解决类组件所有自定义变量只能存储在this.state的问题。
举个🌰:若某个组件需要有2个自定义变量name和age,那么在类组件中只能定义如下
constructor(props) {
super(props);
this.state = {
name: 'liam',
age: 28
}
}
2
3
4
5
6
7
name和age只能作为this.state的一个属性。
没有对比就没有伤害,看一下使用useState后,函数组件是如何实现上述需求的
const [name, setName] = useState("liam");
const [age, setAge] = useState(28)
2
- 函数组件本身是一个函数,不是类,因此没有构造函数constructor(props);
- 任何你想定义的变量都可以单独拆分出去,独立定义,互不影响;
两段代码对比之下,就会发现使用Hook的useState后,会让定义的变量相对独立、清晰简单、便于管理。
# useState函数源码
首先看一下React源码中的ReachHooks.js (opens new window) 。
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
2
3
4
5
6
上述代码看不懂也没事,只是讲述一下如何使用Hook,不是Hook源码分析,之所以贴出源码只是显得本文比较有深度。哈哈哈
哦,对了,是不是以为这是TS代码?其实不是的,是flow,不过也可以完全将TS的泛型知识去套用到 flow 中。
在这里补充一些TypeScript常识:
- React本身采用TypeScript编写,还是补充点TS常识,方便对各个hook函数源码的理解(我就大概看了一下,没具体了解,目前太菜
- 对于useState以及其他hook函数源码,函数参数中会反复出现<S>、<T>、<P>、<L>、<I>,这些大写字母中,React约定他们对应的单词如下:
- state -> S -> 约定表示某种"数据"
- type -> T -> 约定表示某种"类型"
- props -> P -> 约定表示"属性传值对应的props"
- initial -> I -> 约定表示某个"初始值"
- 这种用<X>包裹起来的类型声明,在TS中成为"泛型"。理论上是可以使用任意单词的,上面那些缩写只是React自己约定单词缩写的。
- 对于一段TS代码,如果出现了<S>,那么后面所有的<S>都将表示"某种相同类型的数据"。对于TS的泛型相关的,多看看文档和去github搜索一些优秀的TS项目学习。
# useState基本用法
useState(value)函数会返回一个数组,该数组包含2个元素:第1个元素为我们定义的变量,第2个元素为修改该变量对应的函数名称。
代码形式:
const [variable, setVariable] = useState(value);
// ...
setVariable(newValue); //修改variable的值
2
3
# 拆解说明
- const [a,b] = [a,b] 这种形式为ES6的"解构赋值";
- "variable'为函数组件中自定义的变量名;
- "setVariable"为修改"variable"对应的函数名;
- "useState"为本次学习的Hook函数;
- "value"为变量默认值;
- "setVariable(newValue)"为调用setVariable并将新的值newValue赋值给variable。
# "variable"补充说明
- variable为变量名,实际使用中可以修改成任意变量名,比如name、age、count等等;
- 但是,函数组件接收父级组件属性传值的变量名为props,因此不要将变量名为props,以免混淆;
- 我就是要变量名定义成props,不仅不会报错还会正常执行,开心就好 😄。
# "setVariable"补充说明
- 该名称采用"set"+"变量名"的驼峰命名形式,只是为了提高代码可读性;
- 一般React项目都约定使用此种命名方式;
- 也可以使用自己喜欢的命名方式或者以团队规范来,但是不能以数字开头。
# "value"补充说明
- 必填项,不可缺省,若缺省则实际运行时会提示变量名未定义;
- 值的类型可以是字符串、数字、数组、对象;
- 值还可以是null,但不可以为undefined。
# "newValue"补充说明(非常重要)
setVariable采用"异步直接赋值"的形式,并不会像类组件中的setState()那样做"异步对比累加赋值"。
"异步"?
这里的"异步"和类组件中setState中的异步是同一个意思,都是为了优化React渲染性能而故意为之。
"直接赋值"?
- 在Hook中,对于简单类型数据,比如number、string类型,可以追鹅通过setVariable(newValue)直接进行赋值。
- 但对于复杂类型数据,比如array,object类型、若想修改其中某一个属性值而不影响其他属性,则需要先copy一份,修改某个属性后再整体赋值。具体怎么做,等过几天写下一篇的时候会写出。
# useState使用Demo
// 函数组件内定义变量name
const [name, setName] = useState("liam"); // name默认值为liam
// 在函数组件内,某些事件交互处理函数修改name的值,例如某次鼠标点击的处理函数handleClick
const handleClick = () => {
setName("AiYang");
// 注意一下,setName('AiYang')是异步修改的,如果此时执行console.log(name) 输出的值依然是liam
// 之前写了一篇ahooks的一篇文章中就是为了解决这个问题 或者接下来写的 都会写到
}
2
3
4
5
6
7
8
9
上述代码中,我们进行了一下子操作:
- 声明一个变量name、修改name的方法setName、并将name默认值设置为"liam";
- 通过setName将name值修改为"AiYang";
注意:在一个组件中,可以不限制次数使用useState(),因此,我们可以声明多个变量,例如下面的代码:
const [name, setName] = useState("liamFE");
const [age, setAge] = useState(28);
2
在该代码片段中,我们分别定义了2个变量:name和age以及他们对应的修改函数setName、setAge。
# 练习题
用useState实现一个计数器,默认为0,每次点击+1。
完整🌰:
import React, {useState} from 'react';
const countComponent = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count +1)
}
return <div onclick={handleClick}>
{count}
</div>
}
export default countComponent;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在上述代码中,没有用到this,这就是函数式组件使用Hook的魅力之一,再也不用去关心烦人的this到底指向谁这个问题了。
至此,关于useState的基础用法写完了。
我发布完以后在这重新又检查了一遍,害怕那些React的高手来给小老弟上眼药水,我这只是最基础的使用以及详细讲解,希望各位大佬嘴下留情。
加油,共勉!