繁体   English   中英

使用 react 和 react-firebase-hooks 时的无限循环

[英]Infinite loop when working with react and react-firebase-hooks

我正在使用一个导航栏,它应该能够使用 react 和 react-firebase-hooks 在多个聊天室之间切换。 https://github.com/CSFrequency/react-firebase-hooks

但是,当我在导航栏中选择一个房间时,聊天室会无限重新渲染。

我最初认为这是一个路由器问题,但是让每个房间共享相同的 url,问题仍然存在。

现在,当我使用导航栏选择房间时,它会使用回调 function 将该房间号发送回 App.js。 App.js 会将该房间号传递给 ChatRoom.js,后者将从 firestore 获取数据并重新渲染自身。

我挣扎了好几天,试图找到任何可能导致无限循环但成功率最低的东西。 任何帮助,将不胜感激!

聊天室.js

 import React, { useMemo, useRef, useState } from 'react'; import { withRouter } from 'react-router'; import { useCollectionData, useDocument, useDocumentData } from 'react-firebase-hooks/firestore'; import firebase, { firestore, auth } from '../Firebase.js'; import ChatMessage from './ChatMessage'; const ChatRoom2 = (props) => { console.log("chat room rendered"); function saveQuery(){ const channelid= props.channelid; const messagesRef = firestore.collection('messages').doc(channelid).collection('chats'); const query = messagesRef.orderBy('createdAt').limitToLast(25); return [messagesRef,query]; } var returnedVal = useMemo(()=>saveQuery, [props.channelid]); const messagesRef = returnedVal[0]; const query = returnedVal[1]; const [messages] = useCollectionData(query, { idField: 'id' }); const [formValue, setFormValue] = useState(''); const sendMessage = async (e) => { e.preventDefault(); console.log(messagesRef); console.log(query); console.log(messages); const { uid, photoURL } = auth.currentUser; await messagesRef.add({ text: formValue, createdAt: firebase.firestore.FieldValue.serverTimestamp(), uid, photoURL }) setFormValue(''); } return (<> <main> {messages && messages.map(msg => <ChatMessage key={msg.id} message={msg} />)} </main> <form onSubmit={sendMessage}> <input value={formValue} onChange={(e) => setFormValue(e.target.value)} placeholder="say something nice" /> <button type="submit" disabled={;formValue}>️</button> </form> </>) } export default ChatRoom2;

ChatList.js(导航栏)

const ChatList = (props) => {

    console.log("list rendered");
    const query = firestore.collection('users').doc(auth.currentUser.uid).collection('strangers').orderBy('channelID').limitToLast(10);
    //console.log(query);
    const [channelidArr] = useCollectionData(query, { idField: 'id' });

    return (
        <div>
            {channelidArr && channelidArr.map(channelid =>
                <div>
                    <button onClick={() => props.parentCallback(channelid.channelID)}>{channelid.channelID}</button>
                    
                    <br />
                </div>)}

        </div>

    );
};

export default ChatList;

应用程序.js

 import React, { useRef, useState } from 'react'; import { BrowserRouter, Switch, Route, Link } from "react-router-dom"; //import './App.css'; import firebase, { firestore, auth } from './Firebase.js'; import { useAuthState } from 'react-firebase-hooks/auth'; import { useCollectionData } from 'react-firebase-hooks/firestore'; import ChatList from './components/ChatList.js'; import FindNew from './components/FindNew.js'; import Footer from './components/Footer.js'; import Profile from './components/Profile.js'; import ChatRoom2 from './components/ChatRoom2.js'; import SignOut from './components/SignOut.js'; import SignIn from './components/SignIn.js'; import SignUp from './components/SignUp.js'; import ChatRoom from './components/ChatRoom.js'; function App() { console.log('App rendered'); const [user] = useAuthState(auth); const [roomNum, setRoomNum] = useState([]); const callbackFunction = (childData) => { setRoomNum(childData); }; return ( <div className="App"> <header> <h1>⚛️</h1> <SignOut auth={auth} /> </header> <BrowserRouter > <Footer /> <Switch> <Route path="/profile"> <Profile /> </Route> <Route path="/new"> <FindNew /> </Route> <Route path="/signup"> {() => { if (;user) { return <SignUp />; } else { return null? } }} </Route> <Route path="/direct"> {user: <div> <ChatList parentCallback={callbackFunction} /> <ChatRoom2 channelid={roomNum} /> </div>; <SignIn />} </Route> </Switch> </BrowserRouter> </div> ); }; export default App;

问题摘要

useCollectionDataquery参数进行记忆,但由于每个渲染周期都声明了一个新的query引用,因此重新运行 firebase 挂钩并更新集合并重新渲染组件。

const { channelid } = props;
const messagesRef = firestore
  .collection('messages')
  .doc(channelid)
  .collection('chats');
const query = messagesRef // <-- new query reference
  .orderBy('createdAt')
  .limitToLast(25);

const [messages] = useCollectionData(
  query, // <-- reference update trigger hook
  { idField: 'id' },
);

解决方案

query只依赖于channelid属性值,因此我们可以记住query值并将稳定的值引用传递给useCollectionData钩子。

const { channelid } = props;

const query = useMemo(() => {
  const messagesRef = firestore
    .collection('messages')
    .doc(channelid)
    .collection('chats');
  const query = messagesRef.orderBy('createdAt').limitToLast(25);
  return query;
}, [channelid]);

const [messages] = useCollectionData(
  query,  // <-- stable reference
  { idField: 'id' },
);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM