Firebase/React/Redux 组件有奇怪的更新行为,状态应该没问题

Firebase/React/Redux Component has weird updating behavior, state should be ok

我有一个连接到 firebase 的聊天网络应用程序。

当我刷新页面时,lastMessage 被加载(如 gif 所示),但是,由于某种原因,如果组件以其他方式安装,lastMessage 有时会闪烁并在之后消失,就像它被覆盖一样。当我将鼠标悬停在它上面并因此更新组件时, lastMessage 就在那里。 这是一种奇怪的行为,我现在花了几天时间尝试不同的事情。

我真的卡在这里了,如果有人能看一下,我将不胜感激。

数据库设置是在 firestore 上聊天集合有 sub-collection 条消息。

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>
  )
}

最终是异步设置问题。

在动作 'messages' 中是集合 'chats' 的子集合。 检索它们是一个异步操作。

当我为每个聊天的消息返回一个 Promise 并在我 运行 成功发送函数之前等待它时,消息按预期显示。