Firebase/React/Redux 组件有奇怪的更新行为,状态应该没问题
Posted
技术标签:
【中文标题】Firebase/React/Redux 组件有奇怪的更新行为,状态应该没问题【英文标题】:Firebase/React/Redux Component has weird updating behavior, state should be ok 【发布时间】:2019-09-11 07:09:23 【问题描述】:我有一个连接到 Firebase 的聊天网络应用。
当我刷新页面时,lastMessage 被加载(如 gif 所示),但是,由于某种原因,如果组件以其他方式安装,lastMessage 有时会闪烁并在之后消失,就像它被覆盖一样。当我将鼠标悬停在它上面并因此更新组件时,lastMessage 就在那里。 这是一种奇怪的行为,我花了几天时间尝试不同的事情。
如果有人能看一看,我将非常感激,因为我真的被困在这里了。
数据库设置是在firestore上聊天集合有一个子集合消息。
App.js
// render property doesn't re-mount the MainContainer on navigation
const MainRoute = ( component: Component, ...rest ) => (
<Route
...rest
render=props => (
<MainContainer>
<Component ...props />
</MainContainer>
)
/>
);
render()
return (
...
<MainRoute
path="/chats/one_to_one"
exact
component=OneToOneChatContainer
/>
// on refresh the firebase user info is retrieved again
class MainContainer extends Component
componentDidMount()
const user, getUserInfo, firebaseAuthRefresh = this.props;
const isAuthenticated = user;
if (isAuthenticated)
getUserInfo(user.id);
firebaseAuthRefresh();
else
history.push("/sign_in");
render()
return (
<div>
<Navigation ...this.props />
<Main ...this.props />
</div>
);
动作
// if I set a timeout around fetchResidentsForChat this delay will make the lastMessage appear...so I must have screwed up the state / updating somewhere.
const firebaseAuthRefresh = () => dispatch =>
firebaseApp.auth().onAuthStateChanged(user =>
if (user)
localStorage.setItem("firebaseUid", user.uid);
dispatch(setFirebaseAuthUser(uid: user.uid, email: user.email))
dispatch(fetchAllFirebaseData(user.projectId));
);
;
export const fetchAllFirebaseData = projectId => dispatch =>
const userId = localStorage.getItem("firebaseId");
if (userId)
dispatch(fetchOneToOneChat(userId));
if (projectId)
// setTimeout(() =>
dispatch(fetchResidentsForChat(projectId));
// , 100);
...
export const fetchOneToOneChat = userId => dispatch =>
dispatch(requestOneToOneChat());
database
.collection("chat")
.where("userId", "==", userId)
.orderBy("updated_at", "desc")
.onSnapshot(querySnapshot =>
let oneToOne = [];
querySnapshot.forEach(doc =>
let messages = [];
doc.ref
.collection("messages")
.orderBy("created_at")
.onSnapshot(snapshot =>
snapshot.forEach(message =>
messages.push( id: message.id, ...message.data() );
);
);
oneToOne.push(Object.assign(, doc.data(), messages: messages ));
);
dispatch(fetchOneToOneSuccess(oneToOne));
);
;
减速器
const initialState =
residents: [],
oneToOne: []
;
function firebaseChat(state = initialState, action)
switch (action.type)
case FETCH_RESIDENT_SUCCESS:
return
...state,
residents: action.payload,
isLoading: false
;
case FETCH_ONE_TO_ONE_CHAT_SUCCESS:
return
...state,
oneToOne: action.payload,
isLoading: false
;
...
Main.js
// ...
render()
return (...
<div>React.cloneElement(children, this.props)</div>
)
OneToOne 聊天容器
// without firebaseAuthRefresh I don't get any chat displayed. Actually I thought having it inside MainContainer would be sufficient and subscribe here only to the chat data with fetchOneToOneChat.
// Maybe someone has a better idea or point me in another direction.
class OneToOneChatContainer extends Component
componentDidMount()
const firebaseAuthRefresh, firebaseData, fetchOneToOneChat = this.props;
const user = firebaseData;
firebaseAuthRefresh();
fetchOneToOneChat(user.id || localStorage.getItem("firebaseId"));
render()
return (
<OneToOneChat ...this.props />
);
export default class OneToOneChat extends Component
render()
<MessageNavigation
firebaseChat=firebaseChat
firebaseData=firebaseData
residents=firebaseChat.residents
onClick=this.selectUser
selectedUserId=selectedUser && selectedUser.residentId
/>
export default class MessageNavigation extends Component
render()
const
onClick,
selectedUserId,
firebaseChat,
firebaseData
= this.props;
<RenderResidentsChatNavigation
searchChat=this.searchChat
residents=residents
onClick=onClick
firebaseData=firebaseData
firebaseChat=firebaseChat
selectedUserId=selectedUserId
/>
const RenderResidentsChatNavigation = (
residents,
searchChat,
selectedUserId,
onClick,
firebaseData,
firebaseChat
) => (
<div>
firebaseChat.oneToOne.map(chat =>
const user = residents.find(
resident => chat.residentId === resident.residentId
);
const selected = selectedUserId == chat.residentId;
if (!!user)
return (
<MessageNavigationItem
id=chat.residentId
key=chat.residentId
chat=chat
onClick=onClick
selected=selected
user=user
firebaseData=firebaseData
/>
);
)
residents.map(user =>
const selected = selectedUserId == user.residentId;
const chat = firebaseChat.oneToOne.find(
chat => chat.residentId === user.residentId
);
if (_isEmpty(chat))
return (
<MessageNavigationItem
id=user.residentId
key=user.residentId
chat=chat
onClick=onClick
selected=selected
user=user
firebaseData=firebaseData
/>
);
)
</div>
最后是 lastMessage 实际显示的项目
export default class MessageNavigationItem extends Component
render()
const hovered = this.state;
const user, selected, chat, isGroupChat, group, id = this.props;
const messages = chat;
const item = isGroupChat ? group : user;
const lastMessage = _last(messages);
return (
<div>
`$user.firstName ($user.unit)`
lastMessage && lastMessage.content
</div>
)
【问题讨论】:
【参考方案1】:最后这是一个异步设置问题。
在动作“消息”中是集合“聊天”的子集合。 要检索它们,这是一个异步操作。
当我为每个聊天的消息返回一个 Promise 并在运行成功调度函数之前等待它时,消息按预期显示。
【讨论】:
以上是关于Firebase/React/Redux 组件有奇怪的更新行为,状态应该没问题的主要内容,如果未能解决你的问题,请参考以下文章