React Hook - useState

1. Hook 的概念

Hook 本身单词意思是“钩子”,作用就是“勾住”某些生命周期函数或某些数据状态,并进行某些关联触发调用。

useState 的作用是“勾住”函数组件中自定义的变量。


2. useState 源码

export function useState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}

需要注意:React 源码中使用的是 flow 语法,而不是 TypeScript 语法。虽然它们很相似,理解时可以当作 TS 语法来看待。

官方文档:React useState


3. useState 基本用法

useState(value) 函数返回一个数组,该数组包含两个元素:

  • 第 1 个元素:我们定义的变量
  • 第 2 个元素:修改该变量的函数

示例代码

const [variable, setVariable] = useState(value);
//....
setVariable(newValue); // 修改 variable 的值

4. 解决数据异步问题

const [count, setCount] = useState(0);
for (let i = 0; i < 3; i++) {
  setCount(count + 1);
}

你认为 count 会增加 3 吗?

答案:不会。无论 for 循环执行多少次,最终 count 仅仅执行了一次 +1

原因:

  • Hook 中的 setXxx 赋值是异步的
  • count 值的更新不会立即生效
  • 多次 setCount(count + 1) 使用的是同一个 count 旧值

解决办法

for (let i = 0; i < 3; i++) {
  setCount(prevData => {return prevData+1});
  //可以简化为 setCount(prev => prev + 1);
}

5. 数据类型为 Object 的修改方法

const [person, setPerson] = useState({ name: '学习不止境', age: 23 });

想要修改 age 的值为 18,正确做法如下:

let newData = {...person};
newData.age = 18;
setPerson(newData); //拷贝 `person`,然后修改 `age`,再赋值

setPerson({ ...person, age: 18 });//这种简写是解构赋值带来的,并不是React提供的

setState是执行的是 异步对比累加赋值,就是先对比之前数据属性中是否有age,如果有则修改age值,同时不会影响到其他属性的值。我猜测react是使用ES6中新增加的Object.assign()这个函数来实现这一步的。
React 不会自动合并对象,所以需要先拷贝 person,然后修改 age,再赋值。


6. 数据类型为 Array 的修改方法

和对象类似,数组也需要先拷贝一份再修改。

const [arr, setArr] = useState(['react', 'node']);
setArr([...arr, 'vue']); //添加'vue'

7. 性能优化

使用 setXxx 赋值时,React 会使用 Object.is() 进行比较,如果新值和旧值一致,则不会触发重新渲染

Object.is() 的行为

let str = 'a';
Object.is(str, 'a'); // true

let num = '18';
Object.is(num, 18); // false (String 和 Number 类型不同)

let obj1 = { name: 'a' };
Object.is(obj1, { name: 'a' }); // false (不同对象,占用不同内存)

let obj2 = { name: 'a' };
let ref1 = obj2;
let ref2 = obj2;
Object.is(ref1, ref2); // true (指向同一对象)

结论

  1. 简单类型(String、Number):相同值不会触发重新渲染。
  2. 复杂类型(Object、Array):即使内容相同,但引用不同,仍会触发重新渲染。
  3. 同一对象引用:不会触发重新渲染。

最佳实践

为了优化性能,如果可以,尽量避免使用复杂类型的状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值