Skip to content

下面函数组件的输出分别是什么?

Posted on:2024年7月23日 at 10:20

下面是一个简单的函数组件,有两个按钮:“alert”、“add”。

如果先点击“alert”按钮,再点击一次“add”按钮,那么弹窗框中的值和页面中展示value分别是什么?

const FunctionComponent = () => {
  const [value, setValue] = useState(1);

  const log = () => {
    setTimeout(() => {
      alert(value);
    }, 3000);
  };

  return (
    <div>
      <p>FunctionComponent</p>
      <div>value: {value}</div>
      <button onClick={log}>alert</button>
      <button onClick={() => setValue(value + 1)}>add</button>
    </div>
  );
};

弹出的值是 1,页面显示的值是 2

我们发现弹出的值和当前页面显示的值不相同。

换句话说:log 方法内的 value 和点击动作触发那一刻的 value 相同,value 的后续变化不会对 log 方法内的 value 造成影响

这种现象被称为“闭包陷阱”或者被叫做“Capture Value” :函数式组件每次render 都会生产一个新的 log 函数,这个新的 log 函数会产生一个在当前这个阶段 value 值的闭包。

上面例子 “闭包陷阱” 的分析:

  1. 初始次渲染,生成一个 log 函数(value = 1)
  2. value 为 1 时,点击 alert 按钮执行 log 函数(value = 1)
  3. 点击按钮增加 value,比如 value 增加到 6,组件 render ,生成一个新的 log 函数(value = 6)
  4. 计时器触发,log 函数(value = 1)弹出闭包内的 value 为 1

如何让弹窗中展示最新的value值呢?

使用 useRef 解决闭包陷阱的问题

const FunctionComponent = () => {
  const [value, setValue] = useState(1);
  const countRef = useRef(value);

  const log = () => {
    setTimeout(() => {
      alert(countRef.current);
    }, 3000);
  };

  useEffect(() => {
    countRef.current = value;
  }, [value]);

  return (
    <div>
      <p>FunctionComponent</p>
      <div>value: {value}</div>
      <button onClick={log}>alert</button>
      <button onClick={() => setValue(value + 1)}>add</button>
    </div>
  );
};

useRef 每次 render 时都会返回同一个引用类型的对象,我们设置值和读取值都在这个对象上处理,这样就能获取到最新的 value 值了。

原文转自:https://fe.ecool.fun/topic/207b117b-2e93-4760-91ed-8e618a85a50e