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

时间:2019-04-22 02:31:22

标签: reactjs firebase redux react-router-v4 react-router-dom

我有一个连接到Firebase的聊天Web应用程序。

Flickering lastMessage

当我刷新页面时,将加载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 个答案:

答案 0 :(得分:0)

最后,这是一个异步设置问题。

在动作“消息”中是集合“聊天”的子集合。 要检索它们,这是一个异步操作。

当我为每次聊天的消息返回一个Promise并在运行成功调度功能之前等待它时,消息将按预期显示。