useState中遇到的问题

useState中遇到的问题
说道 useRef 那么我们先来看看 useState 存在的"问题"。
import React, { useState, useEffect } from ‘react’;

export default function Demo() {
const [like, setLike] = useState(0);

useEffect(() => {});

const handleClick = () => {
setLike(like + 1);
};

const getLikeValue = () => {
setTimeout(() => {
alert(like);
}, 2000);
};

return (

{like} 👍
获得like值

);
}
这是一个非常经典的例子,页面上渲染出来三个 button。当我点击 + 之后,页面重新渲染为1。
此时当我点击 获得like值 按钮,因为定时器的原因并不会立即执行alert,此时我再次点击 + 修改like。
当两秒过后,你会发现页面上展示的最新的like值,而 alert 弹出的 like 停留到了1。
先说结论:
当Demo函数每次运行我们都称他为每一次渲染,每一次渲染函数内部都拥有自己独立的props和state, 当在jsx中调用代码中的state进行渲染时,每一次渲染都会获得各自渲染作用域内的props和state。

对比vue更新原理的差异
实质上这里和vue实现响应式的原理是完全不同的,我们都知道在vue3中是通过proxy,当修改响应式值的时候会触发对应的set函数从而触发更新,并且运行对应收集的effect进行模版更新。
而在react这里state中的like仅仅是渲染函数中一个定义的数字而已。它并不是什么proxy,watcher,effect…它仅仅是表示一个数字而已。
当我们第一次调用函数,like赋予初始化值是0,当我们点击按钮调用setLike,react会再次渲染组件(运行Demo函数)。此时新函数内部的like是1,然后使用内部这个值重新调用Demo函数进行页面渲染。如此类推, 就好比下方这段代码:
const like = 2 // Final value
// …

{like}

// …
// During first render
function Counter () {
const like = 0
// …

{like}

// … }
// After a Click, our function is called again
function Counter () {
const like = 1
// …

{like}

// … }
// After another click, our function is called again
function Counter () {
const like = 2
// …

{like}

// … }
// …
结论分析
当我们每次更新状态的时候,(修改state值)。react会重新渲染组件,每一次渲染都可以拿到独立的like状态,这个状态值是独立于每次渲染函数中的一个常量, 它的作用仅仅只是渲染输出,插入jsx中的数字而已。
你可能会疑惑每次调用函数的like值是哪里来的,新的like值是由react提供,当我们调用setLike修改它的值的时候。react会带着新的值去重新运行函数进行再次渲染,保证渲染和输出一致。
这里有一个关键点,任意一次渲染周期(函数调用)中的state/prop(直观来说就是like值)都不会随着时间改变,因为每次调用渲染函数中的like值都是一个常量(在各自的渲染函数作用域内)。
渲染输出会变化是因为组件函数被一次次调用,而每一次调用引起的渲染函数中包含的like值都是函数内部互相独立的。
这就是为什么setTimeout中拿到的仍然是1而不是最新的like。因为闭包的原因,当我们点击getLikeValue的时候获取的是当次渲染函数内部的like值,谨记每次渲染state和prop都是相互独立的(因为是各自函数作用域内的变量),每次独立渲染函数中的state和prop都是保持不变的常量。
每次改变state/props造成函数组件重新执行,从而每次渲染函数中的state/props都是独立的,固定的。
注意这里的固定和独立这两个关键字。

固定表示函数在开始执行时已经确定了本次state/props的值。(特别注意上文setTimeout因为闭包的原因访问的是固定的,已经确定的state)。
独立表示每次运行函数的state/props都是各自独立作用域中的。

useRef
上边我们说到关于state和props在不同渲染中的独立性,这个时候就引出了我们的主角useRef。 useRef日常主要有两种作用,我们先来说说刚才关于state碰到的问题,使用 useRef 来如何解决。
useRef作用一:多次渲染之间的纽带
之前通过state我们了解了,react中每一次渲染它的state props都是相互独立的,于是自然而然我们想到如何在每一次渲染之间产生关系呢。这个时候useRef就展示了他的作用。
我们先来看看关于useRef在react中返回值的类型定义:
interface MutableRefObject {
current: T;
}
可以看到useRef返回值是一个包括属性current类型为范型的一个object。
它与直接在function compoent中定义一个 { current:xxx }的区别就是。
useRef会在所有的render中保持对返回值的唯一引用。因为所有对ref的赋值和取值拿到的都是最终的状态,并不会因为不同的render中存在不同的隔离。
简单来说,你可以将useRef的返回值,想象成为一个全局变量。
我们来改写一下这个Demo再来看看:
import React, { useState, useEffect, useRef } from ‘react’;

export default function Demo() {
const [like, setLike] = useState(0);
// 初始化likeRef 初始值为0
const likeRef = useRef(0);

useEffect(() => {});

const handleClick = () => {
setLike(like + 1);
// 看做是全局变量,直接使用current属性赋值
likeRef.current = likeRef.current + 1;
};

const getLikeValue = () => {
setTimeout(() => {
alert(likeRef.current);
}, 2000);
};

return (

{like} 👍
获得like值

);
}
按照之前的步骤操作,这个时候我们可以看到alert弹出的值就是最新的值。而并非作用域隔离的值。
当然需要额外注意的是,修改useRef返回的值并不会引起react进行重新渲染执行函数,demo中的页面渲染不是因为修改Ref的值,而是因为我们在修改likeRef.current时同时修改了state中的setLike造成了页面渲染。
useRef的值改变并不会造成页面重新渲染,这一点可以做很多事情。比如可以配合useEffect查询页面是首次渲染还是更新。
总结来说,useRef返回值的改变并不会造成页面更新。而且useRef类似于react中的全局变量, 并且不存在不同次render中的state/props的作用域隔离机制。这就是useRef和useState这两个hook的主要区别。
useRef作用二:获取DOM元素
vue3中获取DOM
当然这一点也是比较常用的useRef的用法,对比在vue3中获取DOM节点:
Hello

接下来只需要通过helloRef.value便可以获得div节点。
react中获取DOM
说到上边你可以已经了解了,useRef还有一种用法就是通过它来获取页面上的DOM元素。
import React, { useEffect, useRef } from ‘react’;

export default function Demo() {
const domRef = useRef(null);

useEffect(() => {
domRef.current?.focus();
console.log(domRef,‘domRef’)
});

return (

增加

);
}
1.通过useRef创建一个变量进行保存(domRef)。
2.在jsx中通过ref={domRef}给对应元素节点添加属性。
3.在页面挂载后通过domRef.current就可以获取对应节点的真实DOM元素了。

总结:
对于上边的Demo,我们可以总结出useRef的一些特性。
我们可以将useRef返回值看作一个组件内部全局共享变量,它会在渲染内部共享一个相同的值。相对state/props他们是独立于不同次render中的内部作用域值。
同时额外需要注意useRef返回值的改变并不会引起组件重新render,这也是和state/props不同的地方。
当然我们在React.functionComponent中想要获取对应jsx的真实Dom元素时候也可以通过useRef进行获取到对应的Dom元素。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JackieChan_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值