【问题标题】:How to convert a React class component to a function component with hooks to get firebase data如何将 React 类组件转换为带有钩子的函数组件以获取 Firebase 数据
【发布时间】:2019-08-22 02:11:00
【问题描述】:

我有一个可以工作的 React 类组件,我想将其转换为功能组件以使用钩子进行状态等。我正在学习 React 钩子。类组件版本运行良好,功能组件是我需要帮助的地方。

数据结构由一个包含三个“客户”的客户列表组成。它的图像在这里:

我要做的就是获取这些数据,对其进行迭代并向用户显示每个名称键的数据。很简单。

问题是从我的组件调用 firebase 会导致无法正确检索数据的不稳定行为。最后一个客户端名称被连续调用,它冻结了浏览器。 :)

这是结果的图像:

代码如下:



import React, {Component,useContext,useEffect, useState} from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
import ListItem from '@material-ui/core/ListItem';
import Button from '@material-ui/core/Button';
import firebase from 'firebase/app';
import {Consumer,Context} from '../../PageComponents/Context';


const styles = theme => ({
    root: {
        flexGrow: 1,
    },
    paper: {
        padding: theme.spacing.unit * 2,
        textAlign: 'center',
        color: theme.palette.text.secondary,
    },
});


const FetchData = (props) =>{
    const [state, setState] = useState(["hi there"]);
    const userID = useContext(Context).userID;

    useEffect(() => {
        let clientsRef = firebase.database().ref('clients');
        clientsRef.on('child_added', snapshot => {
            const client = snapshot.val();
            client.key = snapshot.key;
            setState([...state, client])
         });
    });

    //____________________________________________________BEGIN NOTE: I am emulating this code from my class component and trying to integrate it

    // this.clientsRef.on('child_added', snapshot => {
    //     const client = snapshot.val();
    //     client.key = snapshot.key;
    //     this.setState({ clients: [...this.state.clients, client]})
    //  });

    //___________________________________________________END NOTE


     console.log(state)

        return (

            <ul>

                {
                    state.map((val,index)=>{

                        return <a key={index} > <li>{val.name}</li>   </a>

                    }) 
                }

            </ul>
        )


}  


FetchData.propTypes = {
  classes: PropTypes.object.isRequired
}

export default withStyles(styles)(FetchData)

【问题讨论】:

    标签: javascript reactjs firebase firebase-realtime-database react-hooks


    【解决方案1】:

    默认情况下,useEffect 回调在每次完成渲染 (see docs) 后运行,并且您为每次此类调用设置一个新的 firebase 侦听器。因此,当 Firebase 发出事件时,每个此类侦听器都会收到数据快照,并且每个侦听器都会将收到的值添加到状态中。

    您需要在安装组件后设置一次侦听器,您可以通过提供一个空的依赖项数组 ([]) 作为 useEffect 的第二个参数来实现:

    useEffect(() => {
      // your code here
    }, []) // an empty array as a second argument
    

    这将告诉 React 这个效果没有任何依赖关系,因此不需要多次运行它。

    但是还有一个重要的时刻。由于您设置了一个侦听器,因此您需要在不再需要它时对其进行清理。这是通过另一个回调完成的,您应该在传递给useEffect 的函数中返回

    useEffect(() => {
      let clientsRef = firebase.database().ref('clients');
      clientsRef.on('child_added', snapshot => {
        const client = snapshot.val();
        client.key = snapshot.key;
        setState([...state, client])
      });
      return () => clientsRef.off('child_added') // unsubscribe on component unmount
    }, []);
    

    基本上,这个返回的清理函数将在每个新效果被调用之前和组件卸载之前被调用 (see docs) 所以只有这个清理函数应该自己解决你的解决方案,但是没有必要在每次之后调用你的效果无论如何渲染,因此将[] 作为第二个参数。

    【讨论】:

      【解决方案2】:

      您的问题是,默认情况下,useEffect() 将在您的组件每次呈现时运行。正在发生的事情是,您的效果触发了组件的更改,这将触发效果再次运行,您最终会得到近似于无限循环的东西。

      幸运的是,react 让我们可以控制何时以数组的形式运行效果挂钩,您可以将其作为附加参数传入。以您为例:

          useEffect(() => {
              let clientsRef = firebase.database().ref('clients');
              clientsRef.on('child_added', snapshot => {
                  const client = snapshot.val();
                  client.key = snapshot.key;
                  setState([...state, client])
               });
          }, []);//An empty array here means  this will run only once.
      

      数组告诉 react 哪些属性要观察。每当这些属性之一发生更改时,它将运行清理功能并重新运行效果。如果您提交一个空数组,那么它只会运行一次(因为没有要查看的属性)。例如,如果您要添加[userId],则每次userId 变量更改时都会运行效果。

      说到清理函数,你没有在你的效果钩子中返回一个。我对firebase不够熟悉,不知道在组件被销毁时是否需要清理任何东西(例如删除'child_added'事件绑定)。返回一个方法作为使用效果的最后一部分是一个好习惯。最终代码如下所示:

       useEffect(() => {
              let clientsRef = firebase.database().ref('clients');
              clientsRef.on('child_added', snapshot => {
                  const client = snapshot.val();
                  client.key = snapshot.key;
                  setState([...state, client])
               });
              return () => { /*  CLEANUP CODE HERE */ };
          }, []);//An empty array here means  this will run only once.
      

      【讨论】:

        【解决方案3】:

        默认情况下,效果在每次渲染后运行,设置状态会导致渲染。 Any effect that updates state needs to have a dependency array specified,否则你只会有一个无限的 update-render-update-render 循环。

        另外,remember to clean up any subscriptions that effects create. 在这里,您可以通过返回一个调用 .off(...) 并删除侦听器的函数来做到这一点。

        然后,make sure to use the function form of state update,确保下一个状态始终依赖于当前状态,而不是绑定事件时发生的任何闭包值。 Consider using useReducer if your component's state becomes more complex.

        const [clients, setClients] = useState([])
        
        useEffect(() => {
          const clientsRef = firebase.database().ref("clients")
        
          const handleChildAdded = (snapshot) => {
            const client = snapshot.val()
            client.key = snapshot.key
            setClients(clients => [...clients, client])
          }
        
          clientsRef.on("child_added", handleChildAdded)
          return () => clientsRef.off('child_added', handleChildAdded)
        }, [])
        

        另见:

        【讨论】:

          猜你喜欢
          • 2020-02-01
          • 1970-01-01
          • 2021-11-24
          • 2022-01-17
          • 2020-01-02
          • 2021-09-11
          • 2021-05-31
          相关资源
          最近更新 更多