У компонента Firebase / React / Redux странное поведение при обновлении, состояние должно быть в порядке - PullRequest
0 голосов
/ 22 апреля 2019

У меня есть веб-приложение для чата, подключенное к firebase.

Flickering lastMessage

Когда я обновляю страницу, загружается lastMessage (как показывает gif), однако, по какой-то причине, если компонент монтируется иным образом, lastMessage иногда мерцает и впоследствии исчезает, как будто он переопределяется. Когда я наведите курсор мыши на него и, следовательно, обновите компонент, там появится lastMessage. Это странное поведение, и я проводил дни, пробуя разные вещи.

Я был бы очень признателен, если бы кто-нибудь взглянул, потому что я действительно застрял здесь.

Настройка БД заключается в том, что в пожарном хранилище коллекция чата содержит сообщения вложенной коллекции.

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

1019 * Переходник *

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

И, наконец, элемент, где на самом деле отображается последнее сообщение

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>
  )
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...