【问题标题】:How does a Firestore onSnapshot listens for changes in React-Native?Firestore onSnapshot 如何监听 React-Native 的变化?
【发布时间】:2020-04-20 03:19:25
【问题描述】:

我在多个地方看到人们将侦听器代码放在componentDidMount 中,但是,我想知道它是如何工作的,因为componentDidMount 只被调用一次,因此它应该只获取一次。当 Firestore 发生更改时,它如何再次获取?

示例代码:

componentDidMount() {
  if(this.state.screen === 7){
        var query = firestore().collection('Collection').doc().collection('subcollection');
            query = query.where('act', '==', 1);
            query = query.where('city', '==', this.state.selected_city);
            query = query.orderBy('update_time', 'desc');
            query = query.limit(10);

            query.onSnapshot({
                error: (e) => this.setState({ errorMessage: e, refreshingPatients: false }),
                next: (querySnapshot) => {
                    var dataSource = querySnapshot.docs.map(doc => { return { ...doc.data(), doc_id: doc.id } });                       
                    var lastVisiblePatient = dataSource[dataSource.length - 1].doc_id;
                    this.setState({
                        dataSource: dataSource,
                        lastVisiblePatient: lastVisiblePatient,
                        refreshingPatients: false,
                    });
                },
            });
    }
 }

编辑:添加示例代码。我想一直听 Firestore DB 的变化。这是正确的做法吗?如果是,它是如何工作的,因为componentDidMount 只被调用一次? 注意:我只在屏幕状态设置为 7 时才收听 Firestore。

【问题讨论】:

  • 也许您可以编辑问题以显示您感到困惑的代码示例?不要让我们猜测您在说什么。请务必查找可能取消注册侦听器的类似代码。
  • @DougStevenson 抱歉,我刚刚编辑并添加了示例代码。如果问题仍然不清楚,请告诉我。
  • 是的,componentDidUpdate 是个不错的选择
  • 你也用其他地方的一些值来设置你的初始状态吗?
  • @O.o 当应用程序启动时,我将状态设置为 0,稍后我将状态设置为 7,因此很明显 componentDidMount 中的代码永远不会从数据库中获取。

标签: javascript firebase react-native google-cloud-firestore


【解决方案1】:

您的componentDidMount 确实只调用了一次,但您在其中附加了一个永久 侦听器。因此,您在 onSnapshot.next 中指定的处理程序会立即使用数据库中的初始数据调用,之后每次匹配查询的数据发生更改。

【讨论】:

  • 啊哈!所以,我实际上可以从其他任何地方使用该侦听器代码调用一个函数,并且该侦听器将永久处于活动状态,对吗?
  • 我不确定我是否正确理解了您的评论。我想将附加侦听器 (query.onSnapshot({...})) 的代码移动到一个函数中,然后从 componentDidMount 调用它,那么它确实会完全一样。
  • 我的意思是,如果我不从componentDidMount 而是从另一个函数调用它会怎样?
  • 这取决于其他函数,以及该函数是否已被调用。我推荐阅读 reactjs.org/docs/state-and-lifecycle.html 来了解 React 中的组件生命周期,以及组件的哪些方法在什么时候被自动调用。
  • 是的。顺便说一句:如果您是新手,我建议您尝试一下。您已经掌握了大部分代码,它不仅能教给您更多知识,还能比我们在这里更快地为您提供答案。
【解决方案2】:

解决方案 1: 阅读代码中的注释。

componentDidUpdate(prevState, prevProps) {
  if(prevState !== this.state.screen && this.state.screen === 7) {

        var query = firestore().collection('Collection').doc().collection('subcollection');
            query = query.where('act', '==', 1);
            query = query.where('city', '==', this.state.selected_city);
            query = query.orderBy('update_time', 'desc');
            query = query.limit(10);

            query.onSnapshot({
                error: (e) => this.setState({ errorMessage: e, refreshingPatients: false }),
                next: (querySnapshot) => {
                    var dataSource = querySnapshot.docs.map(doc => { return { ...doc.data(), doc_id: doc.id } });                       
                    var lastVisiblePatient = dataSource[dataSource.length - 1].doc_id;
                    this.setState({

                    // you are not updating screen so it is always 7
                    // if you update other state variables, it will trigger ComponentDidUpdate again, 
                    // and again and again and again

                        dataSource: dataSource,
                        lastVisiblePatient: lastVisiblePatient,
                        refreshingPatients: false,
                    });
                },
            });
    }
 }

通过添加prevState !== this.state.screen,可以防止每次更新其他状态时触发componentDidUpdate

如 dataSource、lastVisiblePatient 和 refreshPatients。

解决方案2:由于使用的是类组件,所以可以使用setState回调。

this.setState({screen: newValue}, doSomethingNew);

doSomethingNew(){
     if (this.state.screen === 7) {
         // your code goes here
     }
}

回调函数(此处为doSomethingNew)在screen值更新为newValue后触发。

解决方案3:如果转换成函数组件,可以使用useEffect依赖。

const [screen, setScreen] = useState(initialValue);    
useEffect(() => {
    if (screen === 7) {
        //do something here
    }
}, [screen]);    // adding screen as a dependency means this  useEffect is triggered only when screen value is changed.

【讨论】:

  • 这些是一些不错的解决方案,谢谢!但是,请查看上面@Frank van Puffelen 所说的内容,他说听众是永久的,因此一旦被调用,它将永久收听。这意味着我什至根本不必使用任何componentDidMountcomponentDidUpdate。至少我是这么理解的。
  • 那你为什么加this.state.screen===7
  • 因为我不希望那个监听器被调用。用户只去过屏幕 1 或 2 或 3 等等。我希望它仅在用户进入屏幕 7 时才被调用,然后从那时起继续收听。
  • 那么你需要一个componentDidUpdate - 当screen === 7 时你将添加一个永久监听器,并且由于你不再需要监听器,你将不得不添加一个标志变量以便你应该以后不要再做听众了。假设用户一开始选择了 7,然后又选择了 3 和 7。
  • 不能再次点击按钮?如果用户再次点击,它将打开另一个监听器...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-17
  • 2021-03-30
  • 2019-11-01
相关资源
最近更新 更多