绑定事件处理程序以记录文档并可以通过useEffect访问Firebase API数据

Wil*_*iam 6 javascript reactjs firebase-realtime-database react-hooks

快速版本:

我的最终目标是做类似下面的链接的操作,但每个异步调用Firebase时useEffect ,列表数据都是由Firebase对象内容组成的。

https://codesandbox.io/s/usage-pxfy7

问题

在下面的代码中,useEffect封装了可对Firebase进行ping操作并返回一些称为“客户端”的数据的代码。数据可以完美检索。

然后,我使用useState的两个不同实例存储该数据useState。数据存储在clientListclientListForRender

到目前为止,一切都很好。

现在问题开始了。

我有第三个实例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]
      });

   });


},[]);
Run Code Online (Sandbox Code Playgroud)

eha*_*hab 3

好的,您发布的代码存在一些问题:

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
Run Code Online (Sandbox Code Playgroud)

最后,我设置了一个工作的codesandbox,它根据您提供的示例模拟数据获取https://codesandbox.io/s/usage-4sn92,我在那里添加了一些注释来帮助解释我上面所说的内容。