首页 > 解决方案 > 创建加载器

问题描述

因此,我正在尝试使用 react 和 firebase firestore 制作加载屏幕。基本上,每当我的应用程序从 firestore 获取数据时,我都想创建一个加载器。

编辑:例如,我尝试做类似的事情:

const [data, setData]=useState([])
const [loader, setLoader]=useState(false)


db.collection('conversations').doc(id).onSnapshot(snap=>{
setData(snap.data())
}).then(()=>{
setLoader(true)
})

但这会将加载程序设置为true,但我想在访问数据之前将加载程序设置为true,一旦检索到数据,我想将加载程序设置为false。

有什么建议么?

标签: reactjsgoogle-cloud-firestore

解决方案


.onSnapShot()不会做你认为它做的事——它附加一个监听器来监听对文档的更改,它总是至少触发一次,然后再进行任何进一步的更改——但不一定立即!它也不返回承诺;它返回一个取消订阅函数 - 当是 useEffect 钩子的一部分时很有用。

您的代码中发生的事情是附加了侦听器,然后立即setLoader(true)被调用 - 因为 Javascript 事件循环,远在调用侦听器函数之前。

如果您打算在返回数据setLoader(true)调用,则至少需要:

db.collection('conversations').doc(id).onSnapshot(snap=>{
setData(snap.data())
setLoader(true)
})

我会注意到这本身并不能处理快照中可能出现的错误。

它更有可能是 useEffect 钩子的一部分,这将有助于在卸载组件时卸载侦听器

useEffect(() => {
  return db
  .collection('conversations')
  .doc(id)
  .onSnapshot(snap=>{
    setData(snap.data());
    setLoader(true)
  })
}, []);

如果,otoh,您只想获取一次文档,您将需要使用 .get(),它确实返回带有 DocumentSnapshot 的承诺

db.collection('conversations').doc(id).get().then(snap=>{
setData(snap.data())
}).then(()=>{
setLoader(true)
})

并根据评论中的要求,进行一次提取:

useEffect(() => {
  // no need for a return for a single get
  setLoader(true)
  db
  .collection('conversations')
  .doc(id)
  .get()
  .then(snap=>{
    setData(snap.data());
    setLoader(false);
  })
}, [*whatever state variable will change to start the process*]);

...对于听众(上图),采取完全不同的方法:

const [data, setData]=useState([])
const [loader, setLoader]=useState(true) //DEFAULTS TO LOADING

useEffect(() => {
  return db
  .collection('conversations')
  .doc(id)
  .onSnapshot(snap=>{
    setData(snap.data());
    setLoader(false) //when the data is ready THE FIRST TIME
  })
}, []);

对于侦听器的任何更新,无需执行任何操作来将状态设置为 LOADING - 这将由状态管理和 setData() 处理。每次 Firestore 调用侦听器时,这些更改只会显示在后续渲染中


推荐阅读