React useState超详细讲解用法

 更新时间:2022年11月28日 13:57:45   作者:YinJie…  
我正在处理的组件是表单的时间输入。表单相对复杂,并且是动态生成的,根据嵌套在其他数据中的数据显示不同的字段。我正在用useReducer管理表单的状态,到目前为止效果很好

前言

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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Redux DevTools不能显示数据问题

    Redux DevTools不能显示数据问题

    这篇文章主要介绍了Redux DevTools不能显示数据问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • ForwardRef useImperativeHandle方法demo

    ForwardRef useImperativeHandle方法demo

    这篇文章主要为大家介绍了ForwardRef useImperativeHandle方法demo,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • react 中 mobx的使用案例详解

    react 中 mobx的使用案例详解

    这篇文章主要介绍了react 中 mobx的使用案例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-04-04
  • react项目升级报错,babel报错,.babelrc配置兼容等问题及解决

    react项目升级报错,babel报错,.babelrc配置兼容等问题及解决

    这篇文章主要介绍了react项目升级报错,babel报错,.babelrc配置兼容等问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • React中的Props类型校验和默认值详解

    React中的Props类型校验和默认值详解

    这篇文章主要为大家详细介绍了React中的Props类型校验和默认值,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • React如何动态控制伪元素样式

    React如何动态控制伪元素样式

    这篇文章主要介绍了React如何动态控制伪元素样式问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • 你知道怎么在 HTML 页面中使用 React吗

    你知道怎么在 HTML 页面中使用 React吗

    这篇文章主要为大家详细介绍了如何在HTML页面中使用 React,使用使用js模块化的方式开发,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • 详解React 的几种条件渲染以及选择

    详解React 的几种条件渲染以及选择

    这篇文章主要介绍了详解React 的几种条件渲染以及选择,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-10-10
  • React文件名和目录规范最佳实践记录(总结篇)

    React文件名和目录规范最佳实践记录(总结篇)

    React在使用时非常灵活,如果没有一个规范约束项目,在开发过程中会非常混乱,本文将介绍几个优秀的规范,介绍文件名和目录前,需要先简述一下几种通用的类型,用来区分文件的功能,感兴趣的朋友一起看看吧
    2022-05-05
  • 深入浅析React中diff算法

    深入浅析React中diff算法

    React 最为核心的就是 Virtual DOM 和 Diff 算法,diff算法的基础是Virtual DOM,接下来通过本文给大家介绍React中diff算法的相关知识,对React中diff算法感兴趣的朋友跟随小编一起学习下吧
    2021-05-05

最新评论