html5 文本 hook,茴字的四种写法——如何在React Hook中获得最新的state

今天的这个问题也源于生活(工作)😂。在我们刚开始使用React hook的时候,经常会遇到这样的情况:我需要在某个异步请求/事件监听中更新我的state的值,并拿着更新好的state去做什么事情。这个时候有可能就会遇到这样的情况,state的值并没有更新,我们拿到的总是旧的state。为什么会有这种情况?我们有哪些方法可以来解决它?本文将会带你解决这些问题。

1. 问题重现

首先来个demo,如下:

import React from "react";

import "./styles.css";

const { useState, useEffect } = React;

export default function App() {

const [scrollCount, setScrolledCount] = useState(0);

useEffect(() => {

console.log(`useEffect: ${scrollCount}`);

});

const handleScroll = () => {

console.log(`handleScroll: ${scrollCount}`);

setScrolledCount(scrollCount + 1);

};

useEffect(() => {

document.addEventListener("scroll", handleScroll);

return () => document.removeEventListener("scroll", handleScroll);

// ⚠️注意:这里是空数组,代表了只有在组件挂载时运行一次

}, []);

return (

React hook获得最新state Demo

);

}

在这个例子中,我们监听了滚动事件,然后持续的将scrollCount加1,同时持续输出scrollCount的值。毫无疑问,我们预想的结果应该是:

useEffect: 0

handleScroll: 0

useEffect: 1

handleScroll: 1

useEffect: 2

handleScroll: 2

...

但是实际的结果是:

useEffect: 0

handleScroll: 0

useEffect: 1

handleScroll: 0

// 因为state的值没有被更新,所以组件没有被重新渲染,useEffect也没有运行

handleScroll: 0

handleScroll: 0

...

那么为什么会出现这样的拿不到最新的state的情况呢?

因为useEffect执行时,会创建一个闭包,所以在运行的时候的scrollCount的值被保存在这个闭包中,且初始值为0,所以当滚动时,每次都输出0

❗️React hook相关原理可以参照:《React Hook原理》——这是我觉得讲的最清楚的一篇,没有之一。

那怎么样解决这个问题呢?

2. 四种解法

2.1 useEffect去掉依赖的空数组

这种解法是最简单的,去掉空数组即可。这样,每次useEffect都会运行,尽管还是个闭包,但是每次都拿了最新的scrollCount值,所以handleScroll的输出也会持续更新。

有些同学可能会问:事件被反复被加绑和解绑没有问题吗?

没有问题,由于useEffect会在浏览器完成布局与绘制之后调用,且加绑和解绑事件的性能开销很小,所以这并不是问题。

⚠️注意:记得解绑事件,持续的添加事件绑定肯定会有问题。

useEffect(() => {

document.addEventListener("scroll", handleScroll);

return () => document.removeEventListener("scroll", handleScroll);

// ⚠️改动了这里:去掉了useEffect的第二个参数,空数组

});

2.2 将函数移入到useEffect之内,并添加依赖的state

因为useEffect依赖了scrollCount,所以每次scrollCount的改动都会使得useEffect重新运行,从而获得了当前最新的scrollCount。

当然了,需要强调的是,函数移不移入useEffect之内其实并没有影响,只不过移入了之后,你会更容易的看到到底函数中依赖了哪个state。

useEffect(() => {

// ⚠️改动了这里:handleScroll被移入了useEffect内部

const handleScroll = () => {

console.log(`handleScroll: ${scrollCount}`);

setScrolledCount(scrollCount + 1);

};

document.addEventListener("scroll", handleScroll);

return () => document.removeEventListener("scroll", handleScroll);

// ⚠️改动了这里:空数组变为[scrollCount]

}, [scrollCount]);

2.3 使用setState的函数式更新

函数式更新:如果新的 state 需要通过使用先前的 state 计算得出,那么可以将函数传递给 setState。该函数将接收先前的 state,并返回一个更新后的值。来自React官方文档

const handleScroll = () => {

console.log(`handleScroll: ${scrollCount}`);

// ⚠️改动了这里:表达式变成了函数

setScrolledCount(scrollCount => scrollCount + 1);

};

通过使用这个特性,我们可以保证state每次都被更新了,但是handleScroll中获得的scrollCount值还是闭包中的0。我们实际得到的输出如下:

useEffect: 0

handleScroll: 0

useEffect: 1

handleScroll: 0

useEffect: 2

handleScroll: 0

...

2.4 使用全局变量

回顾这个问题,无非是某个状态不能获得最新的,我们使用全局变量就能解决这个问题,无论是把这个state挂载到window下,还是使用useRef,都能获得最新的值。

这种改动代码变化稍大,整体贴在下面:

import React from "react";

import "./styles.css";

const { useRef, useEffect } = React;

export default function App() {

const scrollCountRef = useRef(null);

useEffect(() => {

console.log(`useEffect: ${scrollCountRef.current}`);

});

const handleScroll = () => {

console.log(`handleScroll: ${scrollCountRef.current}`);

scrollCountRef.current++;

};

useEffect(() => {

scrollCountRef.current = 0;

document.addEventListener("scroll", handleScroll);

return () => document.removeEventListener("scroll", handleScroll);

}, []);

return (

React hook获得最新state Demo

);

}

当然,这种代码也不涉及到组件的重新渲染,所以它的输出是:

useEffect: null

handleScroll: 1

handleScroll: 2

handleScroll: 3

...

3. 总结

在本文中,我们探讨了问题发生的原因和4种解决React Hook种获得最新state的办法。问题本身并不复杂,但是深入了解其中的原理,并不断进行总结,才是我们持续不断进步的源泉。

b739ec46bb5c46d9c0aa4ce35ba1ea56.png

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[茴字的四种写法——如何在React Hook中获得最新的state]http://www.zyiz.net/tech/detail-140488.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值