处理有关未安装组件的错误的最佳方法是什么?尝试实现 CKEditor 但出现此错误
What's the best way to deal with an error about an unmounted component? Trying to implement CKEditor but get this error
将我的 CKeditor 实现为 React Modal 后出现以下错误:
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.
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, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 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;
我是 React 新手,所以在实施方面我可能遗漏了一些基础知识。我肯定知道问题出在 CKeditor 实现上,但我不确定问题出在哪里,因为它是一个相当简单的组件。
我能够通过将局部变量 mounted 设置为 true 来消除未安装的错误,我在效果的清理功能上将其设置为 false(如 react 所建议的)。然后当且仅当该值为真时我才更新状态,也就是说,如果组件未安装意味着我们的变量设置为假,它不会进入 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]);
将我的 CKeditor 实现为 React Modal 后出现以下错误:
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.
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, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 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;
我是 React 新手,所以在实施方面我可能遗漏了一些基础知识。我肯定知道问题出在 CKeditor 实现上,但我不确定问题出在哪里,因为它是一个相当简单的组件。
我能够通过将局部变量 mounted 设置为 true 来消除未安装的错误,我在效果的清理功能上将其设置为 false(如 react 所建议的)。然后当且仅当该值为真时我才更新状态,也就是说,如果组件未安装意味着我们的变量设置为假,它不会进入 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]);