如何使用 React 和 Firebase 数据库正确地 set/access 数据对象?
How to set/access data object correctly using React with a Firebase database?
从 Firebase 数据库检索数据后,我在 React 中使用 useState
挂钩存储和访问数据时遇到问题。问题似乎源于并非所有用户都拥有 playerData
数据包中的所有值 - 在这种情况下,如果玩家拥有“先驱”成就,他们可以创建定居点,否则,他们只能设置他们的配置文件名称(然后在 playerData
中有一个未定义的 settlementName
)。如果帐户具有所有相关数据,它工作正常,但如果没有,那么它会遇到一堆空值类型错误。
谁能告诉我我做错了什么?我想知道我是否应该使用 NoPioneer()
(如果用户被识别为没有先驱成就则执行)将空值填充到 playerData
对象,这样它至少不会命中空引用在对象中。
此外,我发现如果我删除 playerData.settlement.settlementName
渲染参考(根据玩家的先驱状态,该数据可能存在也可能不存在)组件会渲染。所以,这让我觉得我可能需要在渲染调用中添加一个条件来检查数据是否存在(尽管遗憾的是我不确定该怎么做)。
这是我的 React 组件供参考:
function Dashboard() {
const [ error, setError ] = useState('');
const history = useHistory();
const pioneer_emails = Firebase.database().ref("pioneer_emails");
const player_achievements = Firebase.database().ref("players/" + currentUser.uid + "/achievements");
const [ pioneer, setPioneer ] = useState(false);
const [ playerData, setPlayerData ] = useState({
player_data: {
playerName: "",
},
settlement: {
settlementName: "",
}
});
const playerName = playerData.player_data.playerName;
const settlementName = playerData.settlement.settlementName;
var dateVariable = Date().toLocaleString()
async function CheckForPioneer(){
//check if email is on pioneer list
await pioneer_emails.on('value', gotEmail, errData);
//if not, then check if the player has the achievement already
if(!pioneer) {
player_achievements.on('value', gotAchievement, noPioneer);
}
}
//check to see if the player's email is on the pioneer list
function gotEmail(data){
//console.log(data.val())
var emails = data.val();
var keys = Object.keys(emails);
for(var i = 0; i < keys.length; i++){
let k = keys[i];
if(emails[k] === currentUser.email){
//set bool
setPioneer(true);
//add achievement to database
SetPioneerAchievement();
//delete email from database
pioneer_emails.child(k).remove();
}
}
}
//check to see if the player has the pioneer achievement
function gotAchievement(data){
var achievements = data.val();
var achievementKeys = Object.keys(achievements);
for(var i = 0; i < achievementKeys.length; i++){
if(achievementKeys[i] === "MISC001"){
setPioneer(true);
console.log("We got a pioneer, baby!");
}
}
}
function noPioneer(){
console.log('Player is not a pioneer!');
}
function errData(err){
console.log('Error!');
console.log(err)
}
function GetPlayerData(){
Firebase.database().ref().child("players").child(currentUser.uid).get().then((snapshot) => {
if (snapshot.exists()) {
setPlayerData(snapshot.val());
console.log(snapshot.val());
console.log(playerData);
console.log(playerData.player_data.playerName);
console.log(playerData.settlement.settlementName);
} else {
console.log("No data available");
}
}).catch((error) => {
console.error(error);
});
}
function SetPioneerAchievement(){
const achievement = {
"MISC001": {
name: "Kickstarter Pioneer",
completed: dateVariable
}
}
player_achievements.set(achievement).catch(alert);
console.log('Achievement added: ' + achievement.name)
}
useEffect(() => {
GetPlayerData();
CheckForPioneer();
}, []);
return (
<div className="dashboard">
<div className="dashboard-box">
<div className="dashboard-content">
<h1>DASHBOARD</h1>
<br /><alert>{error}</alert>
<br />
<center><h3>Name:</h3></center>
<center><h2>Ancestor {playerData && playerData.player_data.playerName}</h2></center>
<center>{pioneer ? <h3 className="pioneer-tag">Pioneer from the 2021 Kickstarter Campaign</h3> : null }</center>
<br /><br />
{ pioneer ? (
<div><center><h3>Founder of the:</h3></center>
<center><h2>{playerData && playerData.settlement.settlementName} Settlement</h2></center></div>
)
: null }
<center><Link to="/update-dashboard" className="link"><input type="submit" id="ancestor-signup-submit" name="dashboard-update-details" value="Update Details" /></Link></center>
<br />
<div class="footer-switch-login">
<center><Link to="/" onClick={handleLogout} className="link">Log out.</Link></center>
</div>
</div>
</div>
</div>
);
}
export default Dashboard;
任何帮助将不胜感激,因为我不知所措。谢谢!
因此,如果我正确理解了您的 question/issue,则代码基本上可以按预期工作,但有时某些 users/data/state 是不完整的。为此,解决方案是使用 null-checks/guard-clauses 或 Optional Chaining operator.
您 sort 已经在几个地方这样做了:
playerData && playerData.player_data.playerName
和
playerData && playerData.settlement.settlementName
但您并未检查对象 属性 访问的每个深度。
使用null-checks/guard-clauses.
playerData &&
playerData.player_data &&
playerData.player_data.playerName
和
playerData &&
playerData.settlement &&
playerData.settlement.settlementName
使用可选链接运算符
playerData?.player_data?.playerName
和
playerData?.settlement?.settlementName
I am wondering if I should use NoPioneer()
(which executes if the user
is identified as not having the pioneer achievement) to populate empty
values to the playerData
object so that it at least doesn’t hit null
references in the object.
这听起来是个绝妙的主意,因为它有助于消除潜在访问 null 或未定义对象属性的可能性。
从 Firebase 数据库检索数据后,我在 React 中使用 useState
挂钩存储和访问数据时遇到问题。问题似乎源于并非所有用户都拥有 playerData
数据包中的所有值 - 在这种情况下,如果玩家拥有“先驱”成就,他们可以创建定居点,否则,他们只能设置他们的配置文件名称(然后在 playerData
中有一个未定义的 settlementName
)。如果帐户具有所有相关数据,它工作正常,但如果没有,那么它会遇到一堆空值类型错误。
谁能告诉我我做错了什么?我想知道我是否应该使用 NoPioneer()
(如果用户被识别为没有先驱成就则执行)将空值填充到 playerData
对象,这样它至少不会命中空引用在对象中。
此外,我发现如果我删除 playerData.settlement.settlementName
渲染参考(根据玩家的先驱状态,该数据可能存在也可能不存在)组件会渲染。所以,这让我觉得我可能需要在渲染调用中添加一个条件来检查数据是否存在(尽管遗憾的是我不确定该怎么做)。
这是我的 React 组件供参考:
function Dashboard() {
const [ error, setError ] = useState('');
const history = useHistory();
const pioneer_emails = Firebase.database().ref("pioneer_emails");
const player_achievements = Firebase.database().ref("players/" + currentUser.uid + "/achievements");
const [ pioneer, setPioneer ] = useState(false);
const [ playerData, setPlayerData ] = useState({
player_data: {
playerName: "",
},
settlement: {
settlementName: "",
}
});
const playerName = playerData.player_data.playerName;
const settlementName = playerData.settlement.settlementName;
var dateVariable = Date().toLocaleString()
async function CheckForPioneer(){
//check if email is on pioneer list
await pioneer_emails.on('value', gotEmail, errData);
//if not, then check if the player has the achievement already
if(!pioneer) {
player_achievements.on('value', gotAchievement, noPioneer);
}
}
//check to see if the player's email is on the pioneer list
function gotEmail(data){
//console.log(data.val())
var emails = data.val();
var keys = Object.keys(emails);
for(var i = 0; i < keys.length; i++){
let k = keys[i];
if(emails[k] === currentUser.email){
//set bool
setPioneer(true);
//add achievement to database
SetPioneerAchievement();
//delete email from database
pioneer_emails.child(k).remove();
}
}
}
//check to see if the player has the pioneer achievement
function gotAchievement(data){
var achievements = data.val();
var achievementKeys = Object.keys(achievements);
for(var i = 0; i < achievementKeys.length; i++){
if(achievementKeys[i] === "MISC001"){
setPioneer(true);
console.log("We got a pioneer, baby!");
}
}
}
function noPioneer(){
console.log('Player is not a pioneer!');
}
function errData(err){
console.log('Error!');
console.log(err)
}
function GetPlayerData(){
Firebase.database().ref().child("players").child(currentUser.uid).get().then((snapshot) => {
if (snapshot.exists()) {
setPlayerData(snapshot.val());
console.log(snapshot.val());
console.log(playerData);
console.log(playerData.player_data.playerName);
console.log(playerData.settlement.settlementName);
} else {
console.log("No data available");
}
}).catch((error) => {
console.error(error);
});
}
function SetPioneerAchievement(){
const achievement = {
"MISC001": {
name: "Kickstarter Pioneer",
completed: dateVariable
}
}
player_achievements.set(achievement).catch(alert);
console.log('Achievement added: ' + achievement.name)
}
useEffect(() => {
GetPlayerData();
CheckForPioneer();
}, []);
return (
<div className="dashboard">
<div className="dashboard-box">
<div className="dashboard-content">
<h1>DASHBOARD</h1>
<br /><alert>{error}</alert>
<br />
<center><h3>Name:</h3></center>
<center><h2>Ancestor {playerData && playerData.player_data.playerName}</h2></center>
<center>{pioneer ? <h3 className="pioneer-tag">Pioneer from the 2021 Kickstarter Campaign</h3> : null }</center>
<br /><br />
{ pioneer ? (
<div><center><h3>Founder of the:</h3></center>
<center><h2>{playerData && playerData.settlement.settlementName} Settlement</h2></center></div>
)
: null }
<center><Link to="/update-dashboard" className="link"><input type="submit" id="ancestor-signup-submit" name="dashboard-update-details" value="Update Details" /></Link></center>
<br />
<div class="footer-switch-login">
<center><Link to="/" onClick={handleLogout} className="link">Log out.</Link></center>
</div>
</div>
</div>
</div>
);
}
export default Dashboard;
任何帮助将不胜感激,因为我不知所措。谢谢!
因此,如果我正确理解了您的 question/issue,则代码基本上可以按预期工作,但有时某些 users/data/state 是不完整的。为此,解决方案是使用 null-checks/guard-clauses 或 Optional Chaining operator.
您 sort 已经在几个地方这样做了:
playerData && playerData.player_data.playerName
和
playerData && playerData.settlement.settlementName
但您并未检查对象 属性 访问的每个深度。
使用null-checks/guard-clauses.
playerData && playerData.player_data && playerData.player_data.playerName
和
playerData && playerData.settlement && playerData.settlement.settlementName
使用可选链接运算符
playerData?.player_data?.playerName
和
playerData?.settlement?.settlementName
I am wondering if I should use
NoPioneer()
(which executes if the user is identified as not having the pioneer achievement) to populate empty values to theplayerData
object so that it at least doesn’t hit null references in the object.
这听起来是个绝妙的主意,因为它有助于消除潜在访问 null 或未定义对象属性的可能性。