处理有关未安装组件的错误的最佳方法是什么?尝试实现 CKEditor 但出现此错误

问题描述

将我的 CKeditor 实现为 React Modal 后,我收到以下错误

警告:无法对已卸载的组件执行 React 状态更新。 这是一个空操作,但它表明您的应用程序中存在内存泄漏。 修复,取消 useEffect 中的所有订阅和异步任务 清理功能

CKeditor 出现在第 132 行,下面是实现代码

import { Avatar } from "@material-ui/core";
import { MoreHorizOutlined,ShareOutlined } from "@material-ui/icons";
import ArrowUpwardOutlinedIcon from "@material-ui/icons/ArrowUpwardOutlined";
import ArrowDownwardOutlinedIcon from "@material-ui/icons/ArrowDownwardOutlined";
import RepeatOutlinedIcon from "@material-ui/icons/RepeatOutlined";
import ChatBubbleOutlineOutlinedIcon from "@material-ui/icons/ChatBubbleOutlineOutlined";
import React,{ useEffect,useState } from "react";
import "../style/Post.css";
import Modal from "react-modal";
import { usedispatch,useSelector } from "react-redux";
import {
  selectQuestionId,selectQuestionName,setQuestionInfo,} from "../features/questionSlice";
import db from "../firebase";
import { selectUser } from "../features/userSlice";
import firebase from "firebase";
import ClassicEditor from "@ckeditor/ckeditor5-react";
import { CKEditor } from "@ckeditor/ckeditor5-build-classic";

Modal.setAppElement("#root");

function Post({ Id,question,imageUrl,timestamp,buildFaastUser }) {
  const user = useSelector(selectUser);
  const [openModal,setopenModal] = useState(false);
  const dispatch = usedispatch();

  const questionId = useSelector(selectQuestionId);
  const questionName = useSelector(selectQuestionName);
  const [answer,setAnswer] = useState("");
  const [getAnswer,setGetAnswer] = useState([]);

  useEffect(() => {
    if (questionId) {
      db.collection("questions")
        .doc(questionId)
        .collection("answer")
        .orderBy("timestamp","desc")
        .onSnapshot((snapshot) =>
          setGetAnswer(
            snapshot.docs.map((doc) => ({ id: doc.id,answers: doc.data() }))
          )
        );
    }
  },[questionId]);

  const handleAnswer = (e) => {
    e.preventDefault();

    if (questionId) {
      db.collection("questions").doc(questionId).collection("answer").add({
        questionId: questionId,timestamp: firebase.firestore.FieldValue.serverTimestamp(),answer: answer,user: user,});

      console.log(questionId,questionName);
      setAnswer("");
      setopenModal(false);
    }
  };

  return (
    <div
      className="post"
      onClick={() =>
        dispatch(
          setQuestionInfo({
            questionId: Id,questionName: question,})
        )
      }
    >
      <div className="post__info">
        <Avatar src={buildFaastUser.photo} />
        <h5>
          {buildFaastUser.displayName
            ? buildFaastUser.displayName
            : buildFaastUser.email}
        </h5>
        <small>{new Date(timestamp?.toDate()).toLocaleString()}</small>
      </div>
      <div className="post__body">
        <div className="post__question">
          <p>{question}</p>
          <button
            onClick={() => setopenModal(true)}
            className="post__btnAnswer"
          >
            Answer
          </button>

          <Modal
            isOpen={openModal}
            onRequestClose={() => setopenModal(false)}
            shouldCloSEOnOverlayClick={false}
            style={{
              overlay: {
                width: 680,height: 550,backgroundColor: "transparent",BoxShadow:
                  "Box-shadow: 0 4px 8px 0 rgba(0,0.2),0 6px 20px 0 rgba(0,0.19);",zIndex: "1000",top: "50%",left: "50%",marginTop: "-250px",marginLeft: "-350px",},}}
          >
            <div className="modal__question">
              <h1>{question}</h1>
              <p>
                asked by{" "}
                <span className="name">
                  {buildFaastUser.displayName
                    ? buildFaastUser.displayName
                    : buildFaastUser.email}
                </span>{" "}
                {""}
                on{" "}
                <span className="name">
                  {new Date(timestamp?.toDate()).toLocaleString()}
                </span>
              </p>
            </div>
            <div className="modal__answer">
              <CKEditor
                required
                editor={ClassicEditor}
                data={answer}
                onChange={(e,editor) => {
                  const data = editor.getData();
                  setAnswer(e.target.data);
                }}
                placeholder="Enter your answer"
                type="text"
              />
            </div>
            <div className="modal__button">
              <button className="cancel" onClick={() => setopenModal(false)}>
                Cancel
              </button>
              <button onClick={handleAnswer} type="submit" className="add">
                Add Answer
              </button>
            </div>
          </Modal>
        </div>
        <div className="post__answer">
          {getAnswer.map(({ id,answers }) => (
            <p key={id} style={{ position: "relative",paddingBottom: "5px" }}>
              {Id === answers.questionId ? (
                <span>
                  {answers.answer}
                  <br />
                  <span
                    style={{
                      position: "absolute",color: "gray",fontSize: "small",display: "flex",right: "0px",}}
                  >
                    <span style={{ color: "lightblue" }}>
                      {answers.user.displayName
                        ? answers.user.displayName
                        : answers.user.email}{" "}
                      on{" "}
                      {new Date(answers.timestamp?.toDate()).toLocaleString()}
                    </span>
                  </span>
                </span>
              ) : (
                ""
              )}
            </p>
          ))}
        </div>
        <img src={imageUrl} alt="" />
      </div>
      <div className="post__footer">
        <div className="post__footeraction">
          <ArrowUpwardOutlinedIcon />
          <ArrowDownwardOutlinedIcon />
        </div>

        <RepeatOutlinedIcon />
        <ChatBubbleOutlineOutlinedIcon />
        <div className="post__footerLeft">
          <ShareOutlined />
          <MoreHorizOutlined />
        </div>
      </div>
    </div>
  );
}

export default Post;

我是新来的,所以可能在实施方面我遗漏了一些基础知识。我当然知道问题出在 CKeditor 实现上,但我不确定问题是什么,因为它是一个相当简单的组件。

解决方法

我能够通过将一个局部变量设置为 true 来消除未挂载的错误,我已经在效果的清理功能上将它设置为 false(就像 react 建议的那样)。然后,当且仅当该值为 true 时,我才更新状态,也就是说,如果组件未安装,这意味着我们的变量设置为 false,则它不会进入 if 块。

代码如下:

  useEffect(() => {
    let mounted = true;
    if (questionId) {
      db.collection("questions")
        .doc(questionId)
        .collection("answer")
        .orderBy("timestamp","desc")
        .onSnapshot((snapshot) => {
          if (mounted) {
            setGetAnswer(
              snapshot.docs.map((doc) => ({ id: doc.id,answers: doc.data() }))
            );
          }
        });
    }

    return () => (mounted = false);
  },[questionId]);