将事件处理程序绑定到文档并通过 useEffect 访问 firebase api 数据
Posted
技术标签:
【中文标题】将事件处理程序绑定到文档并通过 useEffect 访问 firebase api 数据【英文标题】:Bind event handler to document & have access to firebase api data via useEffect 【发布时间】:2020-01-05 23:09:20 【问题描述】:快速版:
我的最终目标是执行类似于下面链接的操作,但根据 useEffect
对 firebase 进行异步调用,其中列表数据由 firebase 对象内容组成。
https://codesandbox.io/s/usage-pxfy7
问题
在下面的代码中,useEffect
封装了 ping firebase 并获取一些称为“客户端”的数据的代码。数据被完美检索。
然后我使用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】:好的,您发布的代码几乎没有问题:
1) 绝对不要在 child_ added 监听器中添加键盘监听器(这意味着每次调用 child_added 监听器时,你都要创建一个新的监听器,导致意外结果和内存泄漏)
2) 您在 setState 更新函数(您提供的回调函数,setClientListState)中调用 setState,这是一种反模式,使您的代码难以遵循和理解,并且一旦组件增长会导致意想不到的影响。如果您想根据之前的状态更新状态,请使用 useEffect 回调
3) useEffect 函数接受第二个参数,称为依赖数组。当你为它提供了一个空数组时,这意味着你希望你的效果只运行一次,这是有问题的,因为我们看到该函数依赖于 clientsRef 变量。 (这实际上是你的问题,因为键盘监听器有你的 clientsList 的旧值,它是空数组,所以它总是返回 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的例子建立了一个模拟数据获取的工作代码框,我在那里添加了一些cmets来帮助解释我上面所说的。
【讨论】:
这使我的应用程序崩溃并创建了某种递归循环。我将很快发布更多我的应用程序代码,以便有更多上下文。 .... 这是一个视频,显示了我在使用您的代码时遇到的崩溃。 youtu.be/yDyYNgcceMs 这是我所有的原始组件代码转储到代码沙箱中,仅供查看,无需 Firebase 依赖等。codesandbox.io/s/usage-5qj4u @William 很难在视频中发布的代码中看到...您共享的代码框根本不工作...我需要一个显示问题的工作代码框,从那里我可以采取...无论如何,您可以向firebase添加一些虚拟数据并将其公开以供阅读,然后在您设置的示例中使用该数据 我用一个虚拟数据库创建了一个精简版的应用程序,并将其推送到 Github 并附上说明。这是我能做的最好的。看看有没有帮助:github.com/wktdev/wm_troubleshooting以上是关于将事件处理程序绑定到文档并通过 useEffect 访问 firebase api 数据的主要内容,如果未能解决你的问题,请参考以下文章