快速版本:
我的最终目标是做一些类似于以下链接的事情,但对 firebase 进行异步调用,其中列表数据由 firebase 对象内容组成useEffect
。
https://codesandbox.io/s/usage-pxfy7
问题
在下面的代码中,useEffect
封装了pingFirebase并获取一些称为"客户端"的数据的代码。数据被完美地检索。
然后,我使用useState
将该数据存储到两个不同的useState
实例中。数据存储在clientList
和clientListForRender
。
目前为止,一切都好。
现在问题开始了。
我有第三个需要数字的useState
实例。我想为文档设置一个按键事件,以便我可以使用向上/向下箭头切换计数器并访问clientListForRender
数组的每个值。
当我设置 eventListener 时,我无法访问数组(可能是由于异步调用的顺序不允许
(。我不确定如何以给我想要的结果的方式编写我的钩子。
谢谢。
const clientsRef = firebase.database().ref('clients');
const [clientList,setClientListState] = useState([]);
const [clientListForRender,setClientListStateForRender] = useState([]);
const [selectedIndex, updateSelectedIndex] = useState(0);
useEffect(() => {
function handleKeyPress(event,arr){
console.log(arr)
if(event.key === "ArrowDown"){
updateSelectedIndex((prev)=>{
return prev += 1
});
}
}
clientsRef.on('child_added', snapshot => {
const client = snapshot.val();
client.key = snapshot.key; // __________________________1. get firebase data
setClientListState(function(prev){
setClientListStateForRender(()=>[client,...prev]); //_______2 store data
// document.addEventListener('keydown', handleKeyPress); <---I am not sure where to put this. I have experimented and
// I decided to omit my cluttered "experiments" to protect your eyes
return[client,...prev]
});
});
},[]);
好的,您发布的代码几乎没有问题:
1(你绝对不应该在child_添加的侦听器中添加你的键盘监听器(这意味着每次调用child_added侦听器时,你都会创建一个新的侦听器,导致意外的结果和内存泄漏(
2(你在setState更新程序函数(你提供的回调函数setClientListState(中调用setState,这是一个反模式,使你的代码难以遵循和理解,一旦组件增长,就会造成意想不到的效果。如果要基于以前的状态更新状态,请使用 useEffect 回调
3( useEffect 函数采用第二个参数,称为依赖数组。当你为它提供了一个空数组时,这意味着你希望你的效果只运行一次,这是有问题的,因为我们看到函数依赖于 clientRef 变量。(这实际上带来了您的问题,因为键盘侦听器具有您的 clientList 的旧值,即空数组,因此它总是返回 0,当按下键时,我在代码沙箱中解释了更多(
4(您应该从 useEffect 函数返回一个回调函数来清理您创建的效果,关闭您附加的侦听器(否则您可能会有内存泄漏,具体取决于组件被装载/卸载的数量(
好的,这是代码应该如何工作:
const clientsRef = firebase.database().ref('clients');
const [clientList, setClientListState] = useState([]);
// I don't understand why you wanted another list, so for now i only use on list
// const [clientListForRender,setClientListStateForRender] = useState([]);
const [selectedIndex, updateSelectedIndex] = useState(0);
useEffect(() => {
function handleKeyPress(event, arr) {
if (event.key === 'ArrowDown') {
updateSelectedIndex(prev => {
if (prev >= clientList.length - 1) {
return (prev = 0);
} else {
return prev + 1;
}
});
}
}
clientsRef.on('child_added', snapshot => {
const client = snapshot.val();
client.key = snapshot.key; // __________________________1. get firebase data
setClientListState(function(prev) {
return [client, ...prev];
});
});
document.addEventListener('keydown', handleKeyPress);
// here you should return a callback to clear/clean your effects
return () => {
document.removeEventListener('keydown', handleKeyPress);
clientsRef.off();
};
// Its important to add these here, or else each time your keyboard listener runs it will have the initial value of
// clientsList ([]), and so clientsList.length = 0, and so you will always updateSelectedIndex(0)
}, [clientList, clientsRef]);
//here render based on selected list as you wish
最后,我设置了一个工作代码沙箱,根据您 https://codesandbox.io/s/usage-4sn92 给出的示例模拟数据获取,我在那里添加了一些注释以帮助解释我上面所说的内容。