2
最近在做个需求,需求有个比较复杂的页面,用React hooks写的组件,由于对这种方式不熟悉,导致意想不到的结果,通常都是因为没有获取到最新值得问题;

有时候获取不到最新的state状态值,如下例子:

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

export default function Test() {
  const [name, setName] = useState('Kidy');
  const say = () => {
    console.log('name', name);
  };

  useEffect(() => {
    // 设置name的值为Joy
    setName('Joy');
    // 1s后打印出name的值,理论上name的最新的值为Joy,期望输出Joy
    setTimeout(() => {
      say();
    }, 1000);
  }, []);

  return null;
}

结果打印出来的是

name Kidy

所以为什么呢?
我们改一下这个示例,增加能正常获取name值的方式:

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

export default function Test() {
  const [name, setName] = useState('Kidy');
  const say = () => {
    console.log('name', name);
  };

  useEffect(() => {
    // 设置name的值为Joy
    setName('Joy');
    // 1s后打印出name的值,理论上name的最新的值为Joy,期望输出Joy
    setTimeout(() => {
      say();
    }, 1000);
  }, []);
  
  // 增加了一个按钮,点击触发say函数的执行
  return (
    <div>
      <button onClick={say}>
        按钮
      </button>
    </div>
  );
}

image.png

点击按钮输出:

name Joy

那么为什么延时函数里执行的say()输出的name不是最新的Joy呢?当调用setName给name重新赋值时,组件重新构建,相当于重新执行了一遍Test函数,里面的局部变量包括namesay会再一次创建并且被赋值,此时namesay已经是新的值,由于useEffect的第二个参数为空数组,所以第一入参的函数依然没有改变,其中的say变量指向的还是上一次创建的say函数(形成了一个闭包),里面保存着上一次的name值,所以输出还是上一次的值Kidy

为什么点击按钮输出的是最新的值JoyTest再次被创建时,构建并返回了最新的DomonClick指向的是当次创建的say函数,也即是包含最新name值的say函数作为button的点击执行函数。

再来看一个案例:

import React, { useState, useEffect, useRef } from 'react';

export default function Test() {
  const [name, setName] = useState('Kidy');
  const country = useRef('美国');
  const say = () => {
    console.log('name', name);
    console.log('country', country.current);
  };

  useEffect(() => {
    // 设置name的值为Joy
    setName('Joy');
    // 这里直接设置为'加拿大'
    country.current = '加拿大';
    // 1s后打印出name的值,理论上name的最新的值为Joy,期望输出Joy
    setTimeout(() => {
      say();
    }, 1000);
  }, []);

  return (
    <div>
      <button onClick={say}>
        按钮
      </button>
    </div>
  );
}

image.png

延时函数和点击事件输出的country.current的值都是最新的值加拿大,这个又是为什么呢?country用的是useRef函数返回值,其返回的是一个引用,赋值的时候是给country对象属性current赋值,也就是在Test函数重新执行的时候country的指向始终没变,所以无论什么时候什么情况下对同一个引用输出属性值,都是相同的。

函数组件在重新执行时,useState总是返回一个全新的值,useRef总是返回相同的引用


Juven
127 声望4 粉丝