所以我的项目是一个 reactjs Web 应用程序,它将其数据存储在 Firestore 实时数据库中。最初所有组件都是基于类的,但我将其全部重构为功能组件,然后我遇到了标题中提到的错误。
Uncaught TypeError: Cannot read property 'displayName' of undefined
at Chat (Chat.js:61)
at renderWithHooks (react-dom.development.js:14985)
at updateFunctionComponent (react-dom.development.js:17356)
at beginWork (react-dom.development.js:19063)
at HTMLUnknownElement.callCallback (react-dom.development.js:3945)
at Object.invokeGuardedCallbackDev (react-dom.development.js:3994)
at invokeGuardedCallback (react-dom.development.js:4056)
at beginWork$1 (react-dom.development.js:23964)
at performUnitOfWork (react-dom.development.js:22776)
at workLoopSync (react-dom.development.js:22707)
at renderRootSync (react-dom.development.js:22670)
at performSyncWorkOnRoot (react-dom.development.js:22293)
at react-dom.development.js:11327
at unstable_runWithPriority (scheduler.development.js:468)
at runWithPriority$1 (react-dom.development.js:11276)
at flushSyncCallbackQueueImpl (react-dom.development.js:11322)
at flushSyncCallbackQueue (react-dom.development.js:11309)
at flushPassiveEffectsImpl (react-dom.development.js:23620)
at unstable_runWithPriority (scheduler.development.js:468)
at runWithPriority$1 (react-dom.development.js:11276)
at flushPassiveEffects (react-dom.development.js:23447)
at react-dom.development.js:23324
at workLoop (scheduler.development.js:417)
at flushWork (scheduler.development.js:390)
at MessagePort.performWorkUntilDeadline (scheduler.development.js:157)
该错误是针对我代码中的这一行:
Header />
59 | </div>
60 | <div className="user-info">
> 61 | Logged in as: <strong id="user-tag">{state.user.displayName}</strong>
| ^ 62 | </div>
63 | {console.log("the log: " + state.user.displayName)}
64 | <div className="chatbox">
我也收到此错误:
index.js:1 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.
at Chat (http://localhost:3000/static/js/main.chunk.js:804:83)
at Route (http://localhost:3000/static/js/vendors~main.chunk.js:114703:29)
at PrivateRoute (http://localhost:3000/static/js/main.chunk.js:38:14)
at Switch (http://localhost:3000/static/js/vendors~main.chunk.js:114905:29)
at Router (http://localhost:3000/static/js/vendors~main.chunk.js:114338:30)
at BrowserRouter (http://localhost:3000/static/js/vendors~main.chunk.js:113958:35)
at App (http://localhost:3000/static/js/main.chunk.js:99:83)
console.<computed> @ index.js:1
import React, { useState, useEffect } from "react";
import { auth } from "../services/firebase";
import { db } from "../services/firebase";
import Header from "../components/Header";
import { writeChats } from "../helpers/db";
function Chat() {
const [state, setState] = useState({
user: auth().currentUser,
chats: [],
content: "",
readError: null,
writeError: null,
});
useEffect(() => {
setState({ readError: null });
console.log("read error set to null now attempting to fetch data");
try {
db.ref("messages").on("value", (snapshot) => {
let chats = [];
snapshot.forEach((snap) => {
chats.push(snap.val());
});
setState({ chats });
});
} catch (err) {
this.setState({ readError: err.message });
}
console.log("data fetched");
}, []);
function handleChange(event) {
setState({
content: event.target.value,
});
}
async function handleSubmit(event) {
event.preventDefault();
setState({ writeError: null });
try {
await writeChats({
name: state.user.displayName,
content: state.content,
timestamp: Date.now(),
uid: state.user.uid,
});
setState({ content: "" });
} catch (error) {
setState({ writeError: error.message });
}
}
return (
<div className="chatter">
<div className="header">
<Header />
</div>
<div className="user-info">
Logged in as: <strong id="user-tag">{state.user.displayName}</strong>
</div>
{console.log("the log: " + state.user.displayName)}
<div className="chatbox">
<div className="chats">
{state.chats.map((chat) => {
return (
<p key={chat.timestamp} style={{ fontSize: "1.2rem" }}>
{chat.content}
<code style={{ fontSize: "0.5rem" }}> {chat.name}</code>
</p>
);
})}
</div>
<div className="input-group">
<form className="textbar" onSubmit={handleSubmit}>
<input
style={{ borderRadius: "10px" }}
size="50"
onChange={handleChange}
value={state.content}
></input>
{state.error ? <p>{state.writeError}</p> : null}
<button className="send-btn" type="submit">
Send
</button>
</form>
</div>
</div>
</div>
);
}
export default Chat;
这可能是一些基本错误,但我不知道该怎么做,请耐心等待。
我的一个猜测是,这行 state.user.displayName
可能指的是我的状态的旧版本,这是我使用 useState()
钩子创建的,因为我读到状态中的每个更新都会重新渲染整个组件,所以也许我需要使用上下文,但我不确定,所以请帮助我,如果您需要任何详细信息,请告诉我。
编辑说明:我已经发布了这个错误的完整代码,请让我知道我在这里犯了什么其他错误,比如我是否违反了任何编码习惯或任何其他严重错误。啊,我知道那些内联 css 语句很糟糕,抱歉。
答案 0 :(得分:1)
请注意,基于类的 setState()
的行为不同于函数式钩子替代方案 (useState
)。前者将您提供的对象与当前状态合并。在钩子的情况下 - 对象被完全替换,而不会从当前状态保留任何内容,因此您只剩下提供给 setX
setter 的内容。
这正是您问题的原因。
您没有正确修改状态。您正在替换整个 state
对象而不保留未修改的值。例如:
useEffect(() => {
setState({ readError: null });
console.log("read error set to null now attempting to fetch data");
try {
db.ref("messages").on("value", (snapshot) => {
let chats = [];
snapshot.forEach((snap) => {
chats.push(snap.val());
});
setState({ chats });
});
} catch (err) {
this.setState({ readError: err.message });
}
console.log("data fetched");
}, []);
更具体地说:
setState({ chats });
在这里,您正在替换您的
{
user: auth().currentUser,
chats: [],
content: "",
readError: null,
writeError: null,
}
对象:
{
chats: [...]
}
因此您不再有 state.user
因此错误。你应该这样做:
setState(state => ({ ...state, chats: chats }));
您应该为所有状态更改执行此操作(我猜)。
此外,例如,您还有尚未清除为 this.setState({ readError: err.message });
的类相关工件。在函数式组件中使用 this
是禁忌。
从这里开始并在必要时进行修改:
import React, { useState, useEffect } from "react";
import { auth } from "../services/firebase";
import { db } from "../services/firebase";
import Header from "../components/Header";
import { writeChats } from "../helpers/db";
function Chat() {
const [state, setState] = useState({
user: auth().currentUser,
chats: [],
content: "",
readError: null,
writeError: null,
});
useEffect(() => {
try {
db.ref("messages").on("value", (snapshot) => {
let chats = [];
snapshot.forEach((snap) => {
chats.push(snap.val());
});
setState(state => {
return {
...state,
chats: chats
};
});
});
}
catch (err) {
setState(state => {
return {
...state,
readError: err.message
};
});
}
console.log("data fetched");
}, []);
function handleChange(event) {
setState(state => {
return {
...state,
content: event.target.value
};
});
}
async function handleSubmit(event) {
event.preventDefault();
setState(state => {
return {
...state,
writeError: null
};
});
try {
await writeChats({
name: state.user.displayName,
content: state.content,
timestamp: Date.now(),
uid: state.user.uid,
});
setState(state => {
return {
...state,
content: ""
};
});
}
catch (error) {
setState(state => {
return {
...state,
writeError: error.message
};
});
}
}
return (
<div className="chatter">
<div className="header">
<Header />
</div>
<div className="user-info">
Logged in as: <strong id="user-tag">{state.user.displayName}</strong>
</div>
{console.log("the log: " + state.user.displayName)}
<div className="chatbox">
<div className="chats">
{state.chats.map((chat) => {
return (
<p key={chat.timestamp} style={{ fontSize: "1.2rem" }}>
{chat.content}
<code style={{ fontSize: "0.5rem" }}> {chat.name}</code>
</p>
);
})}
</div>
<div className="input-group">
<form className="textbar" onSubmit={handleSubmit}>
<input
style={{ borderRadius: "10px" }}
size="50"
onChange={handleChange}
value={state.content}
></input>
{state.error ? <p>{state.writeError}</p> : null}
<button className="send-btn" type="submit">
Send
</button>
</form>
</div>
</div>
</div>
);
}
export default Chat;
此外,我建议您将 state
分成不同的变量(不同的 useState
调用),因为其中一些看起来不相关,只会损害可维护性和清晰度。