useEffect 之外的 Rect 异步调用 - 内存泄漏

时间:2021-06-14 04:44:01

标签: reactjs react-hooks

我的 useEffect 中有一个简单的 Task 钩子:

const TaskAD = ({ match }: TaskADProps) => {
  const { taskName } = match.params;
  const [task, setTask] = useState<TaskData | null>(null);
  const [loading, setLoading] = useState(true);

  const authCommunicator = authRequest();

  useEffect(() => {
    const getTask = async () => {
      const taskData = await authCommunicator
        .get(`/task/${taskName}`)
        .then((response) => response.data);
      setTask(taskData);
      setLoading(false);
    };
    getTask();
  }, []);

  if (loading || task == null) {
    return <Spinner centered />;
  }

  const updateDescription = async (content: string): Promise<boolean> => {
    const r = await authCommunicator
      .patch(`/task/${task.name}/`, {
        description: content,
      })
      .then((response) => {
        console.log("Setting Task data!");
        setTask(response.data);
        return true;
      })
      .catch(() => false);
    return r;
  };

  return (
    <ProjectEntity name={taskName}>
      <Space direction="vertical" size="small" style={{ width: "100%" }}>
        <StatusRow status="Open" />
        <TaskDetails task={task} />
        <Description content={task.description} onSubmit={updateDescription} />
        <Title level={2}>Subtasks:</Title>
        <Table dataSource={dataSource} columns={columns} />
      </Space>
    </ProjectEntity>
  );
};

Task 对象包含描述。描述是另一个带有文本区域的组件。这个想法是:当用户改变子组件中的描述时,子组件有一个函数(通过 props 传递)来更新描述。

所以我通过 props 将 updateDescription 传递给我的子组件 (Description)。 useEffectupdateDescription 都在我的 Task 组件中,Description 组件基本上是无状态的。会发生什么:

  • 用户更新描述
  • 子组件调用该函数,它更新我数据库中的记录
  • 它从 API 获取响应并调用 setTask
  • task 变量通过 DescriptionTask 中的 props 传递给 render,因此它们都会更新,因为父 Task 的状态已更改
  • 我看到更新的描述

唯一的问题是,虽然它可以工作,但是当我这样做时,我可以在控制台中看到:

Setting Task data!
Warning: 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.

(我添加了 console.log 只是为了看看它何时发生)。

所以我想问一下这是我在 useEffect 之外进行异步调用的问题还是其他原因?

@Edit Description 代码(我删除了所有不必要的垃圾):

interface DescriptionProps {
  content: string;
  onSubmit?: (content: string) => Promise<boolean>;
  title?: string;
  rows?: number;
}

const Description = (props: DescriptionProps) => {
  const { content, onSubmit, title, rows } = props;
  const [descriptionContent, setDescriptionContent] = useState(content);
  const [expanded, setExpanded] = useState(true);
  const [editMode, setEditMode] = useState(false);
  const [descriptionChanged, setDescriptionChanged] = useState(false);
  const editable = onSubmit !== undefined;

  const resetDescription = () => {
    setDescriptionContent(content);
    setDescriptionChanged(false);
  };

  const changeDescription = (value: string) => {
    setDescriptionContent(value);
    setDescriptionChanged(true);
  };

  const descriptionTitle = (
    <>
      <S.DescriptionTitle>{title}</S.DescriptionTitle>
    </>
  );

  return (
    <Collapse
      defaultActiveKey={["desc"]}
      expandIcon={S.ExpandIcon}
      onChange={() => setExpanded(!expanded)}
    >
      <S.DescriptionHeader header={descriptionTitle} key="desc">
        <S.DescriptionContent
          onChange={(event): void => changeDescription(event.target.value)}
        />
        {descriptionChanged && onSubmit !== undefined ? (
          <S.DescriptionEditActions>
            <Space size="middle">
              <S.SaveIcon
                onClick={async () => {
                  setDescriptionChanged(!(await onSubmit(descriptionContent)));
                }}
              />
              <S.CancelIcon onClick={() => resetDescription()} />
            </Space>
          </S.DescriptionEditActions>
        ) : null}
      </S.DescriptionHeader>
    </Collapse>
  );
};

@Edit2

有趣的是,将此添加到我的 Description 中可以解决问题:

  useEffect(
    () => () => {
      setDescriptionContent("");
    },
    [content]
  );

谁能解释一下为什么?

0 个答案:

没有答案