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 组件有奇怪的更新行为,状态应该没问题的主要内容,如果未能解决你的问题,请参考以下文章

学习:图论----一般图

Codeforces 1220D

何为递归

codechef Far Graphs

二分图部分总结

Wannafly挑战赛18 B - 随机数