我试图解决这个问题,但运气不好。。。
我正在使用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;
谢谢你抽出时间!
Models是一个常规的javascript函数,而不是一个函数组件。因此,这不是钩子的有效使用,也不会像预期的那样工作。请参阅有关挂钩规则的文档。
功能组件接收props并返回JSX或另一个React元素。
由于它没有,所以它基本上是重新启动并在每次被父对象调用时调用您的效果。
查看您的编辑,您可能只需要删除Models
函数,并将逻辑放在Gallery
组件中。
我阅读上面组件的方式让人觉得你已经定义了一个从firebase获取数据的自定义钩子。
因此,首先,我会将其重命名为useFbData,并将其视为一个自定义钩子,这样您就可以使用用于钩子的ESLint插件,并确保遵守钩子的规则。
按照上面的方法,如果它是组件中的一个函数,那么你的函数将在每次渲染时激发,所以你所描述的行为正是我所期望的。
根据您的请求的成本/组件呈现的频率,这可能是您想要的,因为您可能不想将过时的数据返回到组件。但是,如果您觉得DB的响应应该被缓存,并且您有一些逻辑可以使数据无效,那么您可以尝试这样的方法:
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,这将导致它无限循环(。
这将在每次调用之间保持数据库响应的结果,并防止多次调用,直到无效为止。不过,请记住,这意味着您使用的数据在失效之前是过时的。