反应-useState和Firebase的问题

时间:2019-11-01 19:33:19

标签: javascript reactjs firebase functional-programming react-hooks

我试图解决此问题,但是我没有运气...

我正在使用React和'react-bootstrap'。如下面的代码所示,使用useState从firebase获取数据。但我也将模态称为组件,该模态使用useState来显示和隐藏模态。

export const Models = () => {

    const [models, setModels] = useState(null);

    useEffect(() => {
        firebase.database().ref('Models').on('value', (snapshot) => {
            setModels(snapshot.val())
        });

    }, []);
    return models;
}

当我单击URL来访问模态时出现问题,结果显示出来,主要组件转到firebase并尝试再次获取数据。因此,如果在模式上单击3次,我将从Firebase中获取3次数据。

我该如何解决?不管打开模态窗口的时间如何,仅一次从Firebase获取我的数据?

代码的另一部分

const Gallery = () => {

    const [fireBaseDate, setFireBaseDate] = useState(null);
    axios.post('https://us-central1-models-gallery-puq.cloudfunctions.net/date',{format:'DD/MM/YYYY'})
    .then((response) => {
        setFireBaseDate(response.data)
    });

    let content = Models();
    let models = [];

    const [imageModalShow, setImageModalShow] = useState(false);
    const [selectedModel, setSelectedModel] = useState('');


    if(content){
        Object.keys(content).map((key, index) => 
        models[index] = content[key]
        );

        models = shuffleArray(models);
        console.log(models)

        return(
            <div className="appContentBody">
                <Jumbo />
                <Promotion models={models}/>
                <div className="Gallery">
                <h1>Galería - Under Patagonia</h1>

                <Filter />

                <div className="img-area">
                    {models.map((model, key) =>{
                        let myDate = new Date(model.creationDate);
                        let modelEndDate = new Date(myDate.setDate(myDate.getDate() + 30)).toLocaleDateString('en-GB')
                        if(fireBaseDate !== modelEndDate && model.active === true){
                            return (
                                <div className="img-card filterCard" key={key}>
                                    <div className="flip-img">
                                        <div className="flip-img-inner">
                                            <div className="flip-img-front">
                                                <img className="single-img card-img-top" src={model.thumbnail} alt="Model"/>
                                            </div>
                                            <div className="flip-img-back">
                                                <h2>{model.certified ? 'Verificada!' : 'No Verificada'}</h2>
                                                <p>Número: {model.contact_number}</p>
                                                <p>Ciudad: {model.city}</p>
                                                <p>Servicios: {model.services}</p>
                                            </div>
                                        </div>
                                    </div>
                                    <h5>{model.name}</h5>

                                    <Button variant="danger" onClick={() => {
                                                setImageModalShow(true)
                                                setSelectedModel(model)}
                                                }>
                                        Ver
                                    </Button>
                                </div>  
                            );
                        }
                    return 0})}                
                </div>
                    <Image
                        show={imageModalShow}
                        onHide={() => setImageModalShow(false)}
                        model={selectedModel}
                    />
                </div>
            <Footer />
            </div>
    )} else {
        return (
            <div className="loading">
                <h1>Loading...</h1>
            </div>
    )}
}


export default Gallery;

感谢您的时间!

2 个答案:

答案 0 :(得分:0)

模型是常规的javascript函数,而不是功能组件。因此,这不是对钩子的有效使用,并且无法按预期工作。请参阅rules of hooks上的文档。

一个功能组件接收道具并返回JSX或另一个React元素。

由于没有启用,因此基本上是重新启动并在每次由父级调用效果时调用效果。

查看所做的编辑,您可能应该只删除Models函数并将逻辑放在Gallery组件中。

答案 1 :(得分:0)

我阅读上述组件的方式使您似乎已经定义了一个自定义钩子,用于从Firebase获取数据。

因此,首先,我将其重命名为useFbData并将其视为自定义钩子,以便您可以使用ESLint Plugin for Hooks并确保遵循rules of hooks

上面的方法,如果它是组件中的一个函数,则该函数将在每个渲染器上触发,因此您所描述的行为是我所期望的。

取决于您的请求的成本/该组件呈现的频率,这可能就是您想要的,因为您可能不想将过时的数据返回到您的组件。但是,如果您认为应该缓存来自数据库的响应,并且有一些使数据无效的逻辑,则可以尝试如下操作:

import { useEffect, useRef } from 'react';

const useFbData = invalidationFlag => {
  const data = useRef(null);
  useEffect(() => {
    if (!data.current || invalidationFlag) {
      firebase.database().ref('Data').on('value', (snapshot) => {
        data.current = snapshot.val();
      });
    }
  }, [invalidationFlag]);
  return data.current;
};

export default useFbData;

这样,在初次运行时,每次更改invalidationFlag的值时,useFbData挂钩中的效果都会运行。只要您跟踪invalidationFlag并根据需要进行设置,就可以为您解决。

我在这里使用ref而不是state的原因是,效果钩子不会获取依赖项数组中的数据(如果使用state,它将无限期循环)。

这将在每个调用之间保留数据库响应的结果,并防止多次调用,直到您使之无效为止。不过请记住,这将意味着您使用的数据在保存无效之前都是过时的。