首页 > 解决方案 > 为什么只有第一次 React 函数组件获取道具

问题描述

当我们尝试更新未安装组件的状态时,React 会引发错误。因此,当我为此测试反应组件时,我只会在第一次渲染时遇到错误。

我制作了一个基于点击启用子组件的组件。并且子组件具有按钮,该按钮在一些 settimeout 后会更新状态,这会引发反应警告

警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请在 useEffect 清理函数中取消所有订阅和异步任务。

这是完全有效的。但是为了克服这个问题,我从父组件传递启用道具,基于子组件中的 setTimeout 之前存在一个条件。那么为什么它只在第一次抛出错误呢?

重现

单击子组件按钮,false然后单击enable卸载子组件的父组件按钮。

**我的问题是为什么反应只在第一次抛出错误?以及为什么它第二次运行良好**

父组件

import { useState } from "react";
import "./styles.css";
import { Test } from "./Test";

export default function App() {
  const [state, setstate] = useState(true);

  const changeState = () => {
    setstate(!state);
  };
  return (
    <div className="App">
      <button onClick={changeState}>enable </button>
      {state && <Test enable={state} />}
    </div>
  );
}

子组件

import React, { useState } from "react";

export const Test = (props) => {
  const [state, setstate] = useState(false);

  const fetchData = () => {
    setstate(!state);
    if (props.enable) {
      setTimeout(() => {
        setstate(false);
      }, 1000);
    }
  };

  return (
    <>
      <button onClick={fetchData}> {`${state}`}</button>
    </>
  );
};

用于测试的 Codesandbox 链接

标签: javascriptreactjsreact-hooksfrontend

解决方案


不错的曲目,只是您遗漏了一个小点,当您编写一个需要一些时间来执行的状态时,同时我们可以根据任何操作一次又一次地访问流程,那么我们需要在转到新订阅之前清除旧订阅一...

例如,在您的代码中,您更新了状态流,但是每次我们访问具有有效道具并单击按钮的组件时,状态流都会注册一个新订阅,因此,当您触发新事件时,prev 执行可能仍然有效,很简单,我们需要做的是卸载旧订阅,我们可以根据您的情况这样做:

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

export const Test = (props) => {
  const [state, setstate] = useState(false);

  useEffect(() => {
    if (props.enable) {
      const timer = setTimeout(() => {
        setstate(false);
      }, 1000);

      return () => clearTimeout(timer);
    }
  }, [state, props.enable]);

  const fetchData = () => {
    setstate((prev) => !prev);
  };

  return (
    <>
      <button onClick={fetchData}> {`${state}`}</button>
    </>
  );
};

看上面的代码,我们只需添加代码需要清理效果,它看起来像我的状态和道具,现在当我点击按钮时,效果将触发,如果我们再次这样做,clearTime 将适用于 prev 订阅然后添加新的等等...

笔记:

  1. 在您的情况下,我们可以删除功能并直接在您的按钮上使用 setState。
  2. 喜欢在你的函数中使用 useCallBackconst fetchData = useCAllback...
  3. 您可以将setstate((prev) => !prev);其用作快照,当您依赖旧值时它很有用..但在某些情况下也可能不需要它,只是为了了解此功能。

更新1:

什么是订阅:

你可以说订阅代表了一次性资源,比如 Observable 的执行。订阅有一个重要的方法,取消订阅,它不带参数,只是处理订阅所持有的资源,换句话说,你可以说是,任何异步任务或任何作业都将被调用以响应生命周期状态及其所需观察变化,然后谈论订阅,如 API 或超时或时间间隔等,任何这些操作都需要清除 prev subscribe(停止观察者 - 取消订阅)以防止任何内存泄漏并清除内存以保持状态流安全并防止不必要的重新渲染。


推荐阅读