【问题标题】:React can't perform state update on unmounted component - checking isMounted not fixingReact 无法在未安装的组件上执行状态更新 - 检查 isMounted 未修复
【发布时间】:2022-10-04 22:24:14
【问题描述】:

我正在使用 axios 从 API 返回数据,并尝试将其呈现给我的 React 应用程序中的各种嵌套组件。

代码如下所示:

const Building = () => {
  const { bid } = useParams();
  const { userAccessToken } = useAuth();
  const [buildingData, setBuildingData] = useState([]);
  const bearerToken = `Bearer ${userAccessToken}`;
  React.useEffect(() => {
    let isMounted = true;
    const axiosConfig = {
      headers: { Authorization: bearerToken },
    };
    axios
      .get(
        "http://localhost:3001/building?requestedlid=2&requestedbid=" + bid,
        axiosConfig
      )
      .then(function (response) {
        if (isMounted) {
          setBuildingData(response.data[0]);
        }
      })
      .catch(function (error) {
        // handle error
        console.log(error);
      });
    return () => {
      isMounted = false;
    };
  }, [bearerToken, bid]);

  return (
    <React.Fragment>
      <Helmet title="Building Profile" />
      <Container fluid className="p-0">
        <Breadcrumb className="float-end mt-2">
          <Breadcrumb.Item href="/dashboard/default">Home</Breadcrumb.Item>
          <Breadcrumb.Item href="/buildings/portfolio">
            Portfolio
          </Breadcrumb.Item>
          <Breadcrumb.Item active>Building Profile</Breadcrumb.Item>
        </Breadcrumb>
        <h1 className="h3 mb-3">
          Building Profile
          <OffcanvasHelp
            id="buildingprofile"
            name="Building Profile"
            scroll
            backdrop
          />
        </h1>
        <div className="clearfix"></div>
        <Row>
          <Col xl="8">
            <BuildingProfile
              name={buildingData.building_name}
              status={buildingData.status}
              description={buildingData.description}
              keycontacts={buildingData.key_contacts}
              created={buildingData.stats.created_date}
              golive={buildingData.stats.golive_date}
            />
            <Rooms />
          </Col>
          <Col xl="4">
            <AccountManager />
            <Map location={buildingData.location} />
            <GetSupport type="commercial" />
          </Col>
        </Row>
      </Container>
    </React.Fragment>
  );
};

我的问题是我收到常见错误:

Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

使用许多 StackOverflow 答案中描述的方法来检查我上面代码中的 isMounted 布尔值,我无法解决问题。

奇怪的是,当我将这两个道具传递给 BuildingProfile 组件时,就会出现这个问题:

created={buildingData.stats.created_date}
golive={buildingData.stats.golive_date}

如果我不通过这两个道具,其他一切都可以正常工作。

我确定我错过了一些愚蠢的东西,但经过几个小时试图弄清楚我仍然被卡住了。任何可以提供指针或任何提示的人,我将不胜感激。

非常感谢

--- 更新 - 包括 BuildingProfile 组件:

const BuildingProfile = ({
  name,
  status,
  description,
  created,
  golive,
  keycontacts,
}) => {
  // Modal config for "Deactivate Building"
  const initOpenModals = () => {
    let modals = {};
    colors.forEach((color, index) => {
      modals = Object.assign({}, modals, { [index]: false });
    });
    console.log(modals);
    return modals;
  };

  const [openModals, setOpenModals] = useState(() => initOpenModals());

  const toggle = (index) => {
    // Toggle selected element
    setOpenModals((openModals) =>
      Object.assign({}, openModals, { [index]: !openModals[index] })
    );
  };

  const notyf = useContext(NotyfContext);
  const [type] = useState("success");
  const [duration] = useState("5000");
  const [ripple] = useState(true);
  const [dismissible] = useState(false);
  const [positionX] = useState("right");
  const [positionY] = useState("top");

  const navigate = useNavigate();

  return (
    <Card>
      <Card.Header className="mb-0 pb-0">
        <Card.Title className="mb-0">
          <IsAllowed to="edit:buildings">
            <div className="card-actions float-end">
              <Dropdown align="end">
                <Dropdown.Toggle as="a" bsPrefix="-">
                  <MoreHorizontal />
                </Dropdown.Toggle>
                <Dropdown.Menu>
                  <Dropdown.Item onClick={() => navigate("/buildings/edit")}>
                    Edit Building
                  </Dropdown.Item>
                  <React.Fragment key="deactivateBuilding">
                    <Dropdown.Item onClick={() => toggle("deactivateBuilding")}>
                      Deactivate Building
                    </Dropdown.Item>
                    <Modal
                      show={openModals["deactivateBuilding"]}
                      onHide={() => toggle("deactivateBuilding")}
                      centered
                    >
                      <Modal.Header closeButton>
                        <b>Admin Function:</b>&nbsp; Deactivate Building
                      </Modal.Header>
                      <Modal.Body className="m-3">
                        <p className="text-left mb-0">
                          Are you sure you want to deactivate the
                          <b>Bus Works</b> building? This will prevent the
                          building from showing up in the platform completely.
                        </p>
                      </Modal.Body>
                      <Modal.Footer>
                        <Button
                          variant="secondary"
                          onClick={() => toggle("deactivateBuilding")}
                        >
                          Close
                        </Button>{" "}
                        <Button
                          variant="danger"
                          onClick={() => {
                            toggle("deactivateBuilding");
                            notyf.open({
                              type,
                              message: "The building has been deactivated.",
                              duration,
                              ripple,
                              dismissible,
                              position: {
                                x: positionX,
                                y: positionY,
                              },
                            });
                          }}
                        >
                          Deactivate Building
                        </Button>
                      </Modal.Footer>
                    </Modal>
                  </React.Fragment>
                </Dropdown.Menu>
              </Dropdown>
            </div>
          </IsAllowed>
          <h1 className="mb-0 pb-0">{name}</h1>
          <Badge
            className={
              status === "Live Building"
                ? "my-2 btn-gradient inline"
                : "my-2 inline"
            }
            bg="success"
          >
            {status}
          </Badge>
        </Card.Title>
      </Card.Header>
      <Card.Body>
        <h5>Building Overview:</h5>
        <p className="mb-4">{description}</p>
        <div className="row">
          <div className="col-md-4">
            <div className="mb-4">
              <h5>Created Date</h5>
              <p>{created}</p>
            </div>
          </div>
          <div className="col-md-4">
            <div className="mb-4">
              <h5>Go-Live Date</h5>
              <p>{golive}</p>
            </div>
          </div>
          <div className="col-md-4">
            <div className="mb-4">
              <h5>Key Contacts</h5>
              <div>
                <span className="me-1">
                  <OverlayTrigger
                    placement="right"
                    overlay={
                      <Tooltip id="tooltip-right">Joe Bloggs</Tooltip>
                    }
                  >
                    <img
                      src={avatar3}
                      width="28"
                      height="28"
                      className="rounded-circle me-2"
                      alt="Joe Bloggs"
                    />
                  </OverlayTrigger>
                </span>
                <span className="me-1">
                  <OverlayTrigger
                    placement="right"
                    overlay={
                      <Tooltip id="tooltip-right">Joe Bloggs</Tooltip>
                    }
                  >
                    <img
                      src={avatar2}
                      width="28"
                      height="28"
                      className="rounded-circle me-2"
                      alt="Joe Bloggs"
                    />
                  </OverlayTrigger>
                </span>
                <span className="me-1">
                  <OverlayTrigger
                    placement="right"
                    overlay={
                      <Tooltip id="tooltip-right">Joe Bloggs</Tooltip>
                    }
                  >
                    <img
                      src={avatar1}
                      width="28"
                      height="28"
                      className="rounded-circle me-2"
                      alt="Joe Bloggs"
                    />
                  </OverlayTrigger>
                </span>
                <Link to="#" className="d-inline-block text-muted fw-bold ms-2">
                  +2 more
                </Link>
              </div>
            </div>
          </div>
        </div>
      </Card.Body>
    </Card>
  );
};

【问题讨论】:

  • 嗨乔纳森,你在 BuildingProfile 组件中有什么? :) 这个组件中的代码看起来不错。
  • 但是,您不应该那样做。 useEffect 中的依赖项看起来像状态。这将使获取数据 3 次:在第一个组件渲染时,然后在每个依赖项被初始化时。
  • 嗨拉拉 - 感谢您的回复。我已经编辑了帖子以包含建筑配置文件组件。并感谢有关 useEffect 的提示-我将研究一种更好的方法。

标签: reactjs react-hooks


【解决方案1】:

我想通了这一点,并想分享我的答案,以防它对其他人有所帮助。

事实证明,因为我试图在 API 调用完成之前访问渲染组件中的嵌套对象属性,所以该属性(“stats”)不存在。当 API 调用完成后状态最终更新时,它无法更新组件,从而导致我看到的错误。

解决此问题的方法是执行以下操作:

  .... // useState should be set to an object, not an array
  const [buildingData, setBuildingData] = useState({});
  .... // skipping past intermediary code for brevity
  .then(function (response) {
        if (isMounted) {
          setBuildingData({
            name: response.data[0].building_name,
            status: response.data[0].status,
            description: response.data[0].description,
            keycontacts: response.data[0].key_contacts,
            created: response.data[0].stats.created_date,
            golive: response.data[0].stats.golive_date
           });
        }
      })
.... // then to access it in the component use:
    <BuildingProfile
      name={buildingData.name}
      status={buildingData.status}
      description={buildingData.description}
      keycontacts={buildingData.keycontacts}
      created={buildingData.created}
      golive={buildingData.golive}
    />

这样做意味着嵌套对象在 useEffect 挂钩内使用 API 数据进行更新,而不是在组件本身中。

希望这可以帮助某人。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 2021-11-30
    • 2020-09-04
    • 2020-08-03
    • 2021-07-01
    • 2020-11-21
    • 2019-11-09
    相关资源
    最近更新 更多