React useState超详细讲解用法

目录
  • 前言
  • 基本用法
    • initData为非函数的情况
    • initData为函数的情况
  • state变化监听
  • 过时状态问题
  • 更新引用数据类型
  • useState 实现原理

前言

React-hooks 正式发布以后, useState 可以使函数组件像类组件一样拥有 state,也就说明函数组件可以通过 useState 改变 UI 视图。那么 useState 到底应该如何使用,底层又是怎么运作的呢,首先一起看一下 useState

基本用法

[ state , dispatch ] = useState(initData)
  • state,目的提供给 UI ,作为渲染视图的数据源。
  • dispatch 改变 state 的函数,可以理解为推动函数组件渲染的渲染函数。
  • initData 有两种情况,第一种情况是非函数,将作为 state 初始化的值。 第二种情况是函数,函数的返回值作为 useState 初始化的值。

initData为非函数的情况

/* 此时将把 0 作为初使值 */const [ num , setNum ] = useState(0)

initData为函数的情况

每当 React 重新渲染组件时,都会执行useState(initData)。 如果初始状态是原始值(数字,布尔值等),则不会有性能问题。

当初始状态需要昂贵的性能方面的操作时,可以通过为useState(computeInitialState)提供一个函数来使用状态的延迟初始化,如下所示:

function MyComponent({ bigJsonData }) {
  const [value, setValue] = useState(function getInitialData() {
    const object = JSON.parse(bigJsonData);
    return object.initialValue;
  });
}

getInitialData()仅在初始渲染时执行一次,以获得初始状态。在以后的组件渲染中,不会再调用getInitialData(),从而跳过昂贵的操作。

state变化监听

类组件 setState 中,有第二个参数 callback 或者是生命周期 componentDidUpdat 可以检测监听到 state 改变或是组件更新。

那么在函数组件中,如何怎么监听 state 变化呢?这个时候就需要 useEffect 出场了,通常可以把 state 作为依赖项传入 useEffect 第二个参数 deps ,但是注意 useEffect 初始化会默认执行一次。

具体可以参考如下 Demo :

import { useState, useEffect } from "react";
export default function App() {
  const [num, setNum] = useState(0);
  /* 监听 num 变化 */
  useEffect(() => {
    console.log("监听num变化,此时的num是:  " + num);
  }, [num]);
  const handerClick = () => {
    setNum(1);
    setTimeout(() => {
      setNum(3);
    });
  };
  console.log(num);
  return (
    <div>
      <span> {num}</span>
      <button onClick={handerClick}>num++</button>
    </div>
  );
}

点击按钮后输出:

0
监听num变化,此时的num是:  0
1
监听num变化,此时的num是:  1
3
监听num变化,此时的num是:  3

我们再把上面的demo改成这样,看看会输出什么:

const [ num , setNum ] = React.useState(0)
const handleClick = ()=>{
    setNum(1)
    console.log(num)
    setTimeout(()=>{
        setNum(3)
        console.log(num)
    })
}

结果:0 0 0

原因很简单,函数组件更新就是函数的执行,在函数一次执行过程中,函数内部所有变量重新声明,所以改变的 state ,只有在下一次函数组件执行时才会被更新。所以在如上同一个函数执行上下文中,number 一直为0,无论怎么打印,都拿不到最新的 state 。

我们只需要记住:在本次函数执行上下文中,是获取不到最新的 state 值的就可以了

过时状态问题

闭包是一个从外部作用域捕获变量的函数。

闭包(例如事件处理程序,回调)可能会从函数组件作用域中捕获状态变量。 由于状态变量在渲染之间变化,因此闭包应捕获具有最新状态值的变量。否则,如果闭包捕获了过时的状态值,则可能会遇到过时的状态问题。

来看看一个过时的状态是如何表现出来的。组件<DelayedCount>延迟3秒计数按钮点击的次数

import React, { useState } from 'react';
function DelayedCount() {
  const [count, setCount] = useState(0);
  const handleClickAsync = () => {
    setTimeout(function delay() {
      setCount(count + 1);
    }, 3000);
  }
  return (
    <div>
      {count}
      <button onClick={handleClickAsync}>Increase async</button>
    </div>
  );
}

快速多次点击按钮。count 变量不能正确记录实际点击次数,有些点击被吃掉。

delay() 是一个过时的闭包,它从初始渲染(使用0初始化时)中捕获了过时的count变量。

为了解决这个问题,使用函数方法来更新count状态:

import React, { useState } from 'react';
function DelayedCount() {
  const [count, setCount] = useState(0);
  const handleClickAsync = () => {
    setTimeout(function delay() {
      setCount(count => count + 1);
    }, 3000);
  }
  return (
    <div>
      {count}
      <button onClick={handleClickAsync}>Increase async</button>
    </div>
  );
}

现在setCount(count => count + 1)delay()中正确更新计数状态。React 确保将最新状态值作为参数提供给更新状态函数,过时闭包的问题解决了。

快速单击按钮。 延迟过去后,count 能正确表示点击次数。

更新引用数据类型

在使用 useStatedispatchAction 更新 state 的时候,记得不要传入相同的 state,这样会使视图不更新:

const textObj = {name:'yinjie'}
const [useState1, setUseState1] = useState(textObj)
setUseState1((oldUseState1) => {
	oldUseState1.name = 'xxx'
    return oldUseState1
}
useEffect(() => {
	console.log(useState1)
},[useState1])
//结果是没有任何反应

为什么会造成这个原因呢?

useStatedispatchAction 处理逻辑中,会浅比较两次 state ,发现 state 相同,不会开启更新调度任务;demo 中两次 state 指向了相同的内存空间,所以默认为 state 相等,就不会发生视图更新了。

解决方法:

const textObj = {name:'yinjie'}
const [useState1, setUseState1] = useState(textObj)
setUseState1((oldUseState1) => {
	oldUseState1.name = 'xxx'
	/** 返回一个新的对象,useEffectc才能检测得到 */
    return {...oldUseState1}
}
useEffect(() => {
	console.log(useState1)  // {name: "xxx"}
},[useState1])

在上面的 demo 中我们浅拷贝了对象,重新申请了一个内存空间。

useState 实现原理

下面简单写一下useState的实现原理:

function useState(init) {
	let state;
	// useState无法保存函数
	if(typeof init === 'function') {
		state = init()
	} else {
		state = init
	}
	const setState = (change) => {
		// 判断一下是否传递过来的是函数
		if(typeof change === 'function') {
			// 如果是函数,调用,并将之前的state传过去,接收到的返回值作为新的state并赋值
			state = change(state)
		} else {
			// 如果不是函数,直接赋值
			state = change;
		}
	}
	return [state, setState]
}

到此这篇关于React useState超详细讲解用法的文章就介绍到这了,更多相关React useState内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • React Hook中useState更新延迟问题及解决

    目录 React Hook中useState更新延迟 React Hook useState连续更新对象问题 React Hook中useState更新延迟 方法一:去掉useEffect的第二个参数 例如以下代码 错误实例 const[zoom, setZoom] = useState(0); useEffect(() = >{     document.getElementById('workspace-content').addEventListener('mousewheel', scr

  • React Hook中的useState函数的详细解析

    useState => 让函数组件具有维持状态的能力useState 这个 Hook 是用来管理 state 的,它可以让函数组件具有维持状态的能力.即在一个函数组件的多次渲染之间,这个 state 是共享的. Hooks 的最大的作用就是可以让你在不编写class的情况下使用state以及其他的 React 特性 useState的详细解析 在上一篇文章中, 我用到useState来让大家体验一下hooks函数 import { memo, useState } from "react&

  • 关于react useState更新异步问题

    目录 react useState更新异步 记useState异步更新小坑 问题点 react useState更新异步 当我们使用react中useState这个hook时候,如下 const [page, setPage] = useState(1); const handlerClick = (e)=>{     setPage(e.current);     console.log(page); } 当我打印page时候,此时page还是1,因为useState的更新是异步的,这和rea

  • React中useState的使用方法及注意事项

    目录 一.基本使用 第一个问题:setCount修改值时它是同步还是异步? 第二个问题:连续调用 setCount会发生什么? 二.注意事项 1.复杂变量的修改 2.异步操作获取更新的值 总结 一.基本使用 useState是 react 提供的一个定义响应式变量的 hook 函数,基本语法如下: const [count, setCount] = useState(initialCount) 它返回一个状态和一个修改状态的方法,状态需要通过这个方法来进行修改: initialCount 是我们

  • React中hook函数与useState及useEffect的使用

    目录 1. 简介 2. useState使用 3. useEffect使用 useEffect发起网络请求 1. 简介 在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数组件,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖于类组件来获取数据,处理数据,并向下传递参数给 UI 组件进行渲染.React在v16.8 的版本中推出了 React Hooks 新特性,Hook是一套工具函数的集合,它增强了函数

  • React18 useState何时执行更新及微任务理解

    目录 函数式组件中的useState 测试1 测试2 类组件中的setState setState在promise之前定义 setState在promise之后定义 结论 函数式组件中的useState 前言:众所周知useState是异步的,但网络上对于useState何时异步更新并没有一个共识,为了探究,做一个简单的实验,实验目的是探究useState与微任务.宏任务的先后关系 测试1 实验步骤:点击按钮触发事件,在事件中我们做三件事:修改state/定义一个宏任务setTimeout /定

  • React函数式组件Hook中的useState函数的详细解析

    目录 前言 一.什么是函数式组件 二.useState 前言 公司项目需要使用react,而函数式组件也是官方比较推荐的!!!所以学习hooks是很重要的. 一.什么是函数式组件 纯函数组件有以下特点: 没有状态 没有生命周期 没有 this 因存在如上特点,使得纯函数组件只能做UI展示的功能, 涉及到状态的管理与切换就不得不用到类组件或者redux. 但因为简单的页面也是用类组件,同时要继承一个React实例,使得代码会显得很重. 以前我们可以使用class来声明一个组件,其实使用functi

  • React超详细分析useState与useReducer源码

    目录 热身准备 为什么会有hooks hooks执行时机 两套hooks hooks存储 初始化 mount useState mountWorkInProgressHook 更新update updateState updateReducer updateWorkInProgressHook 总结 热身准备 在正式讲useState,我们先热热身,了解下必备知识. 为什么会有hooks 大家都知道hooks是在函数组件的产物.之前class组件为什么没有出现hooks这种东西呢? 答案很简单,

  • React useState超详细讲解用法

    目录 前言 基本用法 initData为非函数的情况 initData为函数的情况 state变化监听 过时状态问题 更新引用数据类型 useState 实现原理 前言 React-hooks 正式发布以后, useState 可以使函数组件像类组件一样拥有 state,也就说明函数组件可以通过 useState 改变 UI 视图.那么 useState 到底应该如何使用,底层又是怎么运作的呢,首先一起看一下 useState . 基本用法 [ state , dispatch ] = useS

  • React渲染机制超详细讲解

    目录 准备工作 render阶段 workloopSync beginWork completeWork commit阶段 commitWork mutation之前 mutation fiber树切换 layout layout之后 总结 准备工作 为了方便讲解,假设我们有下面这样一段代码: function App(){ const [count, setCount] = useState(0) useEffect(() => { setCount(1) }, []) const handl

  • Java超详细讲解接口的实现与用法

    目录 1.接口的定义 2.接口的实现 3.接口的引用 4.接口的继承 5.利用接口实现多重继承 1.接口的定义 接口是一种特殊的抽象类,是Java提供的一个重要的功能,与抽象类不同的是: 接口的所有数据成员都是静态的且必须初始化. 接口中的所有方法必须都是抽象方法,不能有一般的方法. [public] interface 接口名称 [extends  父接口名列表]{    [public] [static] [final]数据类型 成员变量名 = 常量;    ...    [public][

  • 超详细讲解SpringCloud Commons公共抽象的用法

    目录 Spring Cloud Commons公共抽象 @EnableDiscoveryClient 服务注册ServiceRegistry RestTemplate的负载均衡 RestTemplate的失败重试 本期主角——Spring Cloud Commons:公共抽象 Spring Cloud Commons公共抽象 Spring Cloud将服务发现.负载均衡和断路器等通用模型封装在一个公共抽象中,可以被所有的Spring Cloud客户端使用,不依赖于具体的实现(例如服务发现就有Eu

  • C++超详细讲解构造函数与析构函数的用法及实现

    目录 写在前面 构造函数和析构函数 语法 作用 代码实现 两大分类方式 三种调用方式 括号法 显示法 隐式转换法 正确调用拷贝构造函数 正常调用 值传递的方式给函数参数传值 值传递方式返回局部对象 构造函数的调用规则 总结 写在前面 上一节解决了类与对象封装的问题,这一节就是对象的初始化和清理的构造函数与析构函数的内容了:对象的初始化和清理也是两个非常重要的安全问题:一个对象或者变量没有初始状态,对其使用后果是未知,同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题:c++利用了构

  • Java超详细讲解ArrayList与顺序表的用法

    目录 简要介绍 Arraylist容器类的使用 Arraylist容器类的构造 ArrayList的常见方法 ArrayList的遍历 ArrayList中的扩容机制 简要介绍 顺序表是一段物理地址连续的储存空间,一般情况下用数组储存,并在数组上完成增删查改.而在java中我们有ArrayList这个容器类封装了顺序表的方法. 在集合框架中,ArrayList是一个普通的类,其实现了list接口.其源码类定义如图 可见,其实现了RandomAccess, Cloneable, 以及Seriali

  • Java超详细讲解抽象类的原理与用法

    目录 1. 抽象类是什么 2 抽象类的语法 3 抽象类都有什么特性 4 抽象类是干什么的 1. 抽象类是什么 ️给大家上一篇小作文,看完这个,你就理解了什么叫做抽象类 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的, 如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类. ️思考一下抽象类和类在功能上什么区别 因为抽象类是没有足够的信息去描绘一个具体的对象的,所以抽象类也就不能实例化对象 除此之外,抽象类的其它功能都是存在的,成员

  • C++超详细讲解强制类型转换的用法

    目录 static_cast dynamic_cast const_cast reinterpret_cast static_cast static_cast<type-id>(expression) 将 expression 转换为 type-id 类型.static_cast 是静态类型转换,发生在编译期.这种转换不会进行运行时的动态检查(RTTI),因而这种转换可能是不安全的.static_cast 典型应用场景如下: 1. 类的层级结构中,基类和子类之间指针或者引用的转换. 上行转换(

  • MySql超详细讲解表的用法

    目录 1. 建表的语法 2. mysql中的数据类型 3. 模拟表 4. 创建一个学生表 1. 创建表(create-DDL) 2. 插入数据(insert-DML) 3. 插入日期 4. date和datetime的区别 5. 更新(update-DML) 6. 删除(delete-DML) 5. 快速创建表(复制表) 6. 快速删除表中数据 1. 建表的语法 建表属于 DDL 语句,DDL 语句包括:create.drop.alter… create table 表名(字段1 数据类型, 字

  • React运行机制超详细讲解

    目录 适合人群 写源码之前的必备知识点 JSX 虚拟Dom 原理简介 手写react过程 基本架子的搭建 React的源码 ReactDom.render ReactDom.Component 简单源码 适合人群 本文适合0.5~3年的react开发人员的进阶. 讲讲废话: react的源码,的确是比vue的难度要深一些,本文也是针对初中级,本意了解整个react的执行过程. 写源码之前的必备知识点 JSX 首先我们需要了解什么是JSX. 网络大神的解释:React 使用 JSX 来替代常规的

随机推荐