comment corriger la boucle de rendu de limite dans react
Je suis en train de créer une application de chat en utilisant la base de données en temps réel React et Firebase.
J'ai un problème avec le rendu de limite maximale en raison du hook usecallback et de l'état des utilisateurs (les hooks sont addOnConnectLister addOnDisConnectListner).
c'est le code:
import React, { useEffect, useState, useCallback, useRef } from "react";
import { Icon, Menu } from "semantic-ui-react";
import firebase from "../../firebase";
const DirectMessages = (props) => {
const [usersRef] = useState(firebase.database().ref("users"));
const [connectedRef] = useState(firebase.database().ref(".info/connected"));
const [presenceRef] = useState(firebase.database().ref("presence"));
const [users, setUsers] = useState([]);
//add presence listner
//create a presence record for current user
const addPressenceListner = useCallback(
(currentUserID) => {
connectedRef.on("value", (snap) => {
console.log("connecting snap value", snap.val());
if (snap.val()) {
const ref = presenceRef.child(currentUserID);
ref.set(true);
ref.onDisconnect().remove((err) => {
if (err) console.log(err);
});
}
});
},
[connectedRef, presenceRef]
);
//init connected users
const initUsers = useCallback(
(currentUserID) => {
const loadedUsers = [];
usersRef.on("child_added", (snap) => {
if (currentUserID !== snap.key) {
let user = snap.val();
user["uid"] = snap.key;
user["status"] = "offline";
loadedUsers.push(user);
setUsers([...loadedUsers]);
}
});
},
[usersRef]
);
//update user status when go online
const addOnConnectListner = useCallback(() => {
presenceRef.on("child_added", (snap) => {
const updatedUsers = users.map((user) => {
if (user.uid === snap.key) {
console.log("user connected", user);
user["status"] = "online";
}
return user;
});
setUsers(...[updatedUsers]);
});
}, [presenceRef, users]);
//update user status when go offline
const addOnDisconnectListner = useCallback(() => {
presenceRef.on("child_removed", (snap) => {
const updatedUsers = users.map((user) => {
if (user.uid === snap.key) {
console.log("user disconnected", user);
user["status"] = "offline";
}
return user;
});
setUsers(updatedUsers);
});
}, [users, presenceRef]);
const addListners = useCallback(
(currentUserID) => {
addPressenceListner(currentUserID);
initUsers(currentUserID);
addOnConnectListner();
addOnDisconnectListner();
},
[
addOnConnectListner,
addOnDisconnectListner,
addPressenceListner,
initUsers,
]
);
useEffect(() => {
if (props.currentUser) {
addListners(props.currentUser.uid);
}
return () => {
presenceRef.off();
usersRef.off();
connectedRef.off();
};
}, [props.currentUser, addListners, connectedRef, presenceRef, usersRef]);
const isUserOnline = (user) => user.status === "online";
return (
<Menu.Menu className="menu" style={{ paddingTop: "2em" }}>
<Menu.Item>
<span>
<Icon name="mail" /> Direct Messages
</span>{" "}
({users.length})
</Menu.Item>
{users.map((user) => (
<Menu.Item key={user.uid}>
<Icon name="circle" color={isUserOnline(user) ? "green" : "red"} />@
{user.username}
</Menu.Item>
))}
</Menu.Menu>
);
};
export default DirectMessages;
après la recherche, j'ai trouvé une solution en utilisant useRef pour obtenir une référence de l'état des utilisateurs afin d'éviter le problème mais ne fonctionne pas correctement, le rappel ne s'exécute qu'une seule fois
import React, { useEffect, useState, useCallback, useRef } from "react";
import { Icon, Menu } from "semantic-ui-react";
import firebase from "../../firebase";
const DirectMessages = (props) => {
const [usersRef] = useState(firebase.database().ref("users"));
const [connectedRef] = useState(firebase.database().ref(".info/connected"));
const [presenceRef] = useState(firebase.database().ref("presence"));
const [users, setUsers] = useState([]);
//users state ref
const getUsersRef = useRef(users);
useEffect(() => {
getUsersRef.current = users;
}, [users]);
//add presence listner
//create a presence record for current user
const addPressenceListner = useCallback(
(currentUserID) => {
connectedRef.on("value", (snap) => {
console.log("connecting snap value", snap.val());
if (snap.val()) {
const ref = presenceRef.child(currentUserID);
ref.set(true);
ref.onDisconnect().remove((err) => {
if (err) console.log(err);
});
}
});
},
[connectedRef, presenceRef]
);
//init connected users
const initUsers = useCallback(
(currentUserID) => {
const loadedUsers = [];
usersRef.on("child_added", (snap) => {
if (currentUserID !== snap.key) {
let user = snap.val();
user["uid"] = snap.key;
user["status"] = "offline";
loadedUsers.push(user);
setUsers([...loadedUsers]);
}
});
},
[usersRef]
);
//update user status when go online
const addOnConnectListner = useCallback(() => {
presenceRef.on("child_added", (snap) => {
const updatedUsers = getUsersRef.current.map((user) => {
if (user.uid === snap.key) {
console.log("user connected", user);
user["status"] = "online";
}
return user;
});
setUsers(...[updatedUsers]);
});
}, [presenceRef, getUsersRef]);
//update user status when go offline
const addOnDisconnectListner = useCallback(() => {
presenceRef.on("child_removed", (snap) => {
const updatedUsers = getUsersRef.current.map((user) => {
if (user.uid === snap.key) {
console.log("user disconnected", user);
user["status"] = "offline";
}
return user;
});
setUsers(updatedUsers);
});
}, [getUsersRef, presenceRef]);
const addListners = useCallback(
(currentUserID) => {
addPressenceListner(currentUserID);
initUsers(currentUserID);
addOnConnectListner();
addOnDisconnectListner();
},
[
addOnConnectListner,
addOnDisconnectListner,
addPressenceListner,
initUsers,
]
);
useEffect(() => {
if (props.currentUser) {
addListners(props.currentUser.uid);
}
return () => {
presenceRef.off();
usersRef.off();
connectedRef.off();
};
}, [props.currentUser, addListners, connectedRef, presenceRef, usersRef]);
const isUserOnline = (user) => user.status === "online";
return (
<Menu.Menu className="menu" style={{ paddingTop: "2em" }}>
<Menu.Item>
<span>
<Icon name="mail" /> Direct Messages
</span>{" "}
({users.length})
</Menu.Item>
{users.map((user) => (
<Menu.Item key={user.uid}>
<Icon name="circle" color={isUserOnline(user) ? "green" : "red"} />@
{user.username}
</Menu.Item>
))}
</Menu.Menu>
);
};
export default DirectMessages;
Réponses
tout d'abord, vous n'en avez pas besoin comme état
const [usersRef] = useState(firebase.database().ref("users"));
const [connectedRef] = useState(firebase.database().ref(".info/connected"));
const [presenceRef] = useState(firebase.database().ref("presence"));
placez-les en dehors du composant.
const usersRef = firebase.database().ref("users")
const connectedRef = firebase.database().ref(".info/connected")
const presenceRef = firebase.database().ref("presence")
const DirectMessages = (props) => {
......
Deuxièmement, la bonne façon de supprimer les auditeurs est.
useEffect(() => {
if (props.currentUser) {
addListners(props.currentUser.uid);
return () => {
presenceRef.off('child_added');
presenceRef.off('child_removed');
usersRef.off('child_added');
connectedRef.off('value');
};
}
}, [props.currentUser, addListners, connectedRef, presenceRef, usersRef]);
Je vais changer ce qui suit
const [users, setUsers] = useState([]);
//users state ref
const getUsersRef = useRef(users);
useEffect(() => {
getUsersRef.current = users;
}, [users]);
à
const [users, _setUsers] = useState([]);
const getUsersRef = useRef(users);
const setUsers = (array) => {
getUsersRef.current = array;
_setUsers(array)
}
enfin , les usecallbacks sont inutiles, vous devez les créer comme des fonctions régulières.
const addOnDisconnectListner = () => {
presenceRef.on("child_removed", (snap) => {
const updatedUsers = getUsersRef.current.map((user) => {
if (user.uid === snap.key) {
console.log("user disconnected", user);
user["status"] = "offline";
}
return user;
});
setUsers(updatedUsers);
}
changer ceci:
const [users, setUsers] = useState([]);
//users state ref
const getUsersRef = useRef(users);
useEffect(() => {
getUsersRef.current = users;
}, [users]);
pour ça:
//users state ref
const getUsersRef = useRef(users);
useEffect(() => {
getUsersRef.current = users;
}, [users]);
après la mise à jour des dépendances de hooks:
import React, { useEffect, useState, useCallback, useRef } from "react";
import { Icon, Menu } from "semantic-ui-react";
import firebase from "../../firebase";
const usersRef = firebase.database().ref("users");
const connectedRef = firebase.database().ref(".info/connected");
const presenceRef = firebase.database().ref("presence");
const DirectMessages = (props) => {
const [users, _setUsers] = useState([]);
//get users ref
const getUsersRef = useRef(users);
const setUsers = (array) => {
getUsersRef.current = array;
_setUsers(array);
};
//add presence listner
//create a presence record for current user
const addPressenceListner = useCallback((currentUserID) => {
connectedRef.on("value",(snap) => {
console.log("connecting snap value", snap.val());
if (snap.val()) {
const ref = presenceRef.child(currentUserID);
ref.set(true);
ref.onDisconnect().remove((err) => {
if (err) console.log(err);
});
}
});
}, []);
//init connected users
const initUsers = useCallback((currentUserID) => {
const loadedUsers = [];
usersRef.on("child_added", (snap) => {
if (currentUserID !== snap.key) {
let user = snap.val();
user["uid"] = snap.key;
user["status"] = "offline";
loadedUsers.push(user);
setUsers([...loadedUsers]);
}
});
}, []);
//update user status when go online
const addOnConnectListner = useCallback(() => {
presenceRef.on("child_added", (snap) => {
const updatedUsers = getUsersRef.current.map((user) => {
if (user.uid === snap.key) {
user["status"] = "online";
}
console.log("user", user);
return user;
});
setUsers(...[updatedUsers]);
});
}, []);
//update user status when go offline
const addOnDisconnectListner = useCallback(() => {
presenceRef.on("child_removed", (snap) => {
const updatedUsers = getUsersRef.current.map((user) => {
if (user.uid === snap.key) {
console.log("user disconnected", user);
user["status"] = "offline";
}
return user;
});
setUsers(updatedUsers);
});
}, [getUsersRef]);
const addListners = useCallback(
(currentUserID) => {
addPressenceListner(currentUserID);
initUsers(currentUserID);
addOnConnectListner();
addOnDisconnectListner();
},
[
addOnConnectListner,
addOnDisconnectListner,
addPressenceListner,
initUsers,
]
);
useEffect(() => {
if (props.currentUser) {
addListners(props.currentUser.uid);
}
return () => {
presenceRef.off();
usersRef.off();
connectedRef.off();
};
}, [props.currentUser, addListners]);
const isUserOnline = (user) => user.status === "online";
return (
<Menu.Menu className="menu" style={{ paddingTop: "2em" }}>
<Menu.Item>
<span>
<Icon name="mail" /> Direct Messages
</span>{" "}
({users.length})
</Menu.Item>
{users.map((user) => (
<Menu.Item key={user.uid}>
<Icon name="circle" color={isUserOnline(user) ? "green" : "red"} />@
{user.username}
</Menu.Item>
))}
</Menu.Menu>
);
};
export default DirectMessages;