setState和useState使用比较

useState和setState使用比较

首先最明显的区别:useState是在函数组件中使用,setState在类组件中使用

二者参数对比
setState( updater,callback )
updater:object/function - 用于更新数据
callback:function - 用于获取更新后最新的 state 值
useState(initState)
const [ state , setState ] = useState(initState)
state:value
setState(updater) :改变value的方法
updater:object/function - 用于更新数据
initState:状态的初始值

setState:
首先看一下setState的用法,构造中给state中定义了三个变量count, status,countArray,同时启动定时器,3s后改变status的值,render并绘制变量结果

import React, { Component } from "react";

export default class Home extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
      status: 0,
      countArray: [0, 1, 2, 3, 4],
    };
    setTimeout(() => {
      this.setState({
        status: 1,
      });
      console.log(this.state.status);
    }, 3000);
  }

  onClick() {
    this.setState(
      {
        count: this.state.count + 3,
      },
      () => {
        console.log("callback this.state.age = ", this.state.count);
      }
    );
    console.log("this.state.age = ", this.state.count);
  }

  changeArray() {
    let array = this.state.countArray;
    array[0] = 10;
    this.setState({
      countArray: array,
    });
  }

  render() {
    console.log("render Home ", new Date().getTime());
    return (
      <div className="container">
        <div>{`count is ${this.state.count}-${this.state.status} -${this.state.countArray[0]}`}</div>
        <button
          onClick={() => {
            this.onClick();
          }}
          className="btn btn-primary"
        >
          Click me1
        </button>
        <button
          onClick={() => {
            this.changeArray();
          }}
          className="btn btn-primary"
        >
          Click me2
        </button>
      </div>
    );
  }
}

浏览器中运行,渲染了两次,并且可以看到status值同步变化了,先render,再打印出结果值1,见下图
在这里插入图片描述

setState是同步的么?接着往下看,点击第一个按钮,调用this.setState,使count+3,输出结果如下:
在这里插入图片描述

点击后,显示为3,但log却同步打印出count值为0,然后render,callback回调中打印出count值为3
这是异步?
对比调试,这里的executionContext,在setTimeout时,context = NoContext,直接执行,对应官方解释
// Flush the synchronous work now, unless we’re already working or inside
// a batch. This is intentionally inside scheduleUpdateOnFiber instead of
// scheduleCallbackForFiber to preserve the ability to schedule a callback
// without immediately flushing it. We only do this for user-initiated
// updates, to preserve historical behavior of legacy mode.
在这里插入图片描述
executionContext 代表了目前 react 所处的阶段,而 NoContext 我们可以理解为是 react 已经没要执行的内容。而 flushSyncCallbackQueue 里面就会去同步调用我们的 this.setState ,也就是说会同步更新我们的 state 。所以,当 executionContext 为 NoContext 的时候,我们的 setState 就是同步的。那什么地方会改变 executionContext 的值呢?

在这里插入图片描述

当 react 进入它自己的调度步骤时,会给这个 executionContext 赋予不同的值,表示不同的操作以及当前所处的状态,而 executionContext 的初始值就是 NoContext ,所以只要不进入 react 的调度流程,这个值就是 NoContext ,也就是说在setTimeout 、原生事件内调用 setState,那 setState 就是理解成同步的。

useState

import React, { useState, useEffect } from "react";

function Home2() {
  const [count, setCount] = useState(0);
  const [countArray, setCountArray] = useState([0, 1, 2, 3]);

  const onClick = () => {
    setCount(count + 1);
    console.log("onClick count ", count, "time = ", new Date().getTime());
    setTimeout(() => {
    setCount(count + 1);
      console.log("onClickTimeout count = ",count,"time = ",new Date().getTime());
    }, 3000);
  };

  const onClick2 = () => {
    let array = countArray;
    array[0] = 10;
    setCountArray(array);
    // let array = [10,1,2,3]
    // setCountArray(array)
  };

  useEffect(() => {
    console.log("useEffect = ", count); // 这里是监控到的最新值
  }, [count]);

  console.log("render Home2");
  return (
    <div>
      <div> {`now count = ${count}- array = ${countArray[0]}`}</div>
      <button className="btn btn-primary" onClick={onClick}>
        Click me1
      </button>
      <button className="btn btn-primary" onClick={onClick2}>
        Click me2
      </button>
    </div>
  );
}

export default Home2;

onClick后,先打印count为0,然后render,绘制时count的值就是+1后的值了。3s后的定时器,跟setState一致,先调用了render,再打印结果,但是这时候打印count的值却是点击前的0,这是因为必包问题。根据这个打印顺序,我们其实也可以推理出跟setstate一样的结论。
在这里插入图片描述
闭包问题,我们可以通过useRef来验证一下,看如下代码

import React, { useState, useEffect } from "react";

function Home2() {
  const [count, setCount] = useState(0);
  const [countArray, setCountArray] = useState([0, 1, 2, 3]);
  const countRef = useRef();
  countRef.current = count;

  const onClick = () => {
    setCount(count + 1);
    console.log("onClick count ", count, "time = ", new Date().getTime());
    setTimeout(() => {
      console.log(
        "onClickTimeout before count countRef.current = " + countRef.current
      );
      setCount(countRef.current + 1);
      console.log(
        "onClickTimeout after count countRef.current = " + countRef.current
      );
    }, 3000);
  };
  ....
}

export default Home2;

验证结果如下:

在这里插入图片描述
闭包问题陷阱参考:

https://blog.csdn.net/weixin_38080573/article/details/115178502

当setState和useState使用数组和对象时,有什么区别么?
看上面第一段代码中的changeArray,array[0] = 10,然后调用setState,看下结果,重新刷新,结果也变为10
在这里插入图片描述

再看看第二段代码:
同样是赋值后,再去setState,看下结果,却没有更新
在这里插入图片描述

调试可以看到:这里判断了新setstate的数组和之前的数组比较,相同则return,不进行render
在这里插入图片描述
JavaScript存储对象都是存地址的,所以浅拷贝会导致 obj1 和obj2 指向同一块内存地址。改变了其中一方的内容,都是在原来的内存上做修改会导致拷贝对象和源对象都发生改变,而深拷贝是开辟一块新的内存地址,将原对象的各个属性逐个复制进去。
那我们在使用setState修改数组时,需要做深拷贝。
let array = countArray.slice(0, countArray.length);
array[0] = 10;
setCountArray(array);

这样结果有变化并更新。对object的处理同样如此。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值