我有一個組件,它顯示從 Firestore 獲取的表中的資料,即使獲取的資料是完整的,但是setting在 JSX 上訪問時屬性大部分時間都是空的。我有時會隨機看到正確的輸出。
據我所知,第一次渲染histories將為空,但在第二次渲染時不會。但似乎即使在第二次渲染settings中也是空的。我在這里遺漏了什么?
資料表.tsx
export default function DataTable() {
const [histories, setHistories] = useState<Array<Array<History>>>([]);
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
readHistories().then((item) => {
if (item) {
console.log(item);
setHistories(item);
setLoading(false);
}
});
}, [setHistories, setLoading]);
return (
<Table striped highlightOnHover verticalSpacing="md">
<thead>
<tr>
<th>UID</th>
<th>Test 1</th>
<th>Test 1</th>
<th>Test 3</th>
<th>Settings</th>
</tr>
</thead>
<tbody>
{!loading ? (
histories != null ? (
histories.map((item: Array<History>, index: number) => {
return (
<Link key={index} href={`profile/${item[0].uid}`} passHref>
<tr>
<td>
{trimUid(item[0].uid)}
</td>
<td>
<div>
<span>{item[0].score} </span>
<span>{item[0].questionnaireType}</span>
</div>
<div>{convertTimestampToDate(item[0].timestamp.seconds)}</div>
</td>
<td>
<div>
<span>{item[1].score}</span>
<span> {item[1].questionnaireType}</span>
</div>
<div>{convertTimestampToDate(item[1].timestamp.seconds)}</div>
</td>
<td>
<div>
<span>{item[2].score}</span>
<span> {item[2].questionnaireType}</span>
</div>
<div>
{convertTimestampToDate(item[2].timestamp.seconds)}
</div>
</td>
<td>
<div>
<span>Sms:</span>
<span> {displaySettings(item[0].settings, "sms")}</span>
</div>
<div>
<span>Call:</span>
<span> {displaySettings(item[0].settings, "call")}</span>
</div>
</td>
</tr>
</Link>
);
})
) : (
""
)
) : (
<tr>
<td>Loading...</td>
</tr>
)}
</tbody>
</Table>
);
}
...
function displaySettings(settings: any, key: string) {
if (settings != null && key == "sms") {
let onOff = settings.smsOn ? "On" : "Off";
console.log("Sms On : " onOff);
return onOff;
} else console.log("Settings Sms is null");
if (settings != null && key == "call") {
let onOff = settings.callOn ? "On" : "Off";
console.log("Call On : " onOff);
return onOff;
} else console.log("Settings Call is null");
}
頁面加載時的控制臺輸出
[Array(3)]
DataTable.tsx?a927:135 Settings Sms is null
DataTable.tsx?a927:141 Settings Call is null
DataTable.tsx?a927:135 Settings Sms is null
DataTable.tsx?a927:141 Settings Call is null
回應物件:控制臺輸出中的 [Array(3)]
[
[
{
"uid": "prW2afyS3WVMaUsnKoXBoEyrvLR2",
"timestamp": {
"seconds": 1647168151,
"nanoseconds": 124000000
},
"questionnaireType": "EPDS",
"score": 16,
"settings": {
"callOn": true,
"smsOn": true
}
},
{
"uid": "prW2afyS3WVMaUsnKoXBoEyrvLR2",
"timestamp": {
"seconds": 1647163878,
"nanoseconds": 998000000
},
"questionnaireType": "PHQ9",
"score": 18,
"settings": {
"callOn": true,
"smsOn": true
}
},
{
"uid": "prW2afyS3WVMaUsnKoXBoEyrvLR2",
"timestamp": {
"seconds": 1647162553,
"nanoseconds": 5000000
},
"questionnaireType": "PHQ9",
"score": 17,
"settings": {
"callOn": true,
"smsOn": true
}
}
]
]
歷史.ts
import { Timestamp } from "firebase/firestore";
import { QuestionnaireType } from "./QuestionnaireType";
import { Settings } from "./Settings";
export class History {
uid: string;
timestamp: Timestamp;
questionnaireType: QuestionnaireType; // enum
score: number;
settings: Settings;
constructor(
uid: string,
timestamp: Timestamp,
questionnaireType: QuestionnaireType,
score: number,
settings: Settings
) {
this.uid = uid;
this.timestamp = timestamp;
this.questionnaireType = questionnaireType;
this.score = score;
this.settings = settings;
}
}
設定.ts
export class Settings {
smsOn: boolean;
callOn: boolean;
constructor(smsOn: boolean, callOn: boolean) {
this.smsOn = smsOn;
this.callOn = callOn;
}
}
更新:添加readHistory功能
export async function readHistories() {
const q = query(
collectionGroup(db, "History"),
where("score", ">=", 10)
).withConverter(historyConverter);
const querySnapshot = await getDocs(q);
const histories = Array<History>();
querySnapshot.forEach((doc) => {
if (doc.exists()) {
const history: History = doc.data();
history.uid = doc.ref.parent.parent!.id;
histories.push(history);
} else console.log("No documents!");
});
return filterLastMonthHistories(histories);
}
function filterLastMonthHistories(histories: Array<History>) {
// ...
const groupedHistories = groupByUid(recentHistories, "uid");
Object.keys(groupedHistories).forEach(function (key) {
readUser(key).then((user) => {
if (user) {
groupedHistories[key].forEach(function (history: History) {
history.settings = user.settings;
});
}
});
});
return sortByTimestampLimit(groupedHistories);
}
function groupByUid(histories: Array<History>, key: string) {
// ...
}
function sortByTimestampLimit(arr: Object) {
// ...
return sortedRecentHistories;
}
export async function readUser(docId: string) {
const docRef = doc(db, "Users", docId).withConverter(userConverter);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
let user = docSnap.data();
return user;
} else console.log("No such document");
}
uj5u.com熱心網友回復:
問題出在下面的一段代碼中,我正在為每個物件發出請求async并設定物件屬性,而無需等待所有請求完成。keygroupedHistoriesHistorysettingsasync
Object.keys(groupedHistories).forEach(function (key) {
readUser(key).then((user) => {
if (user) {
groupedHistories[key].forEach(function (history: History) {
history.settings = user.settings;
});
}
});
});
解決方案是使用Promise.all()which wait until all promises getresolved并分配returned values給array.
async function filterLastMonthHistories(histories: Array<History>) {
// ...
const groupedHistories = groupByUid(recentHistories, "uid");
const uIds = Object.keys(groupedHistories);
const sortedRecentHistories = sortByTimestampLimit(groupedHistories);
const users: Array<User | undefined> = await Promise.all(
uIds.map(function (uid) {
return readUser(uid);
})
);
sortedRecentHistories.forEach(function (histories) {
histories.forEach(function (history) {
users.forEach(function (user) {
if (user != undefined) {
history.settings = user.settings;
}
});
});
});
return sortedRecentHistories;
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/448906.html
