简体   繁体   中英

In a multichannel chat application how can I display messages pertaining only to a specific chat channel?

Environment : expo 36.0.0 / React Native 0.61 / react-native-gifted-chat 0.13.0 / firebase 7.6.0

My goal: Render only messages specific to a specific chat channel on the Gifted Chat UI.

Expected results: Only messages pertaining to a given channel should be displayed on the Gifted Chat UI.

Actual results :

  1. When I navigate from one chat channel to another the total messages cummulate.
  2. When I come back to an earlier channel, the messages for the same chat channel repeat more than once.
  3. Warning: Encountered two children with the same key, %s . Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.%s

What have I tried so far? 1. Relaunch subscription to new chat channel every time the user navigates from one channel to another using componentDidUpdate. 2. Set State of messages array to an empty array every time user changes chat channel. 3. Unsubscribe from the previous node in Firebase and subscribe to a new node in componentDidUpdate. Here node represents the chat channel identified by an ID in Firebase. Each node contains children that are all the messages pertaining to that specific chat channel.

  async componentDidMount() {
    await this.getSessionInfo();
    await this.getCurrentProfile();
    await this.getMessages();
  };

  async componentDidUpdate(prevProps) {
    /* sessionID represent a chat channel and corresponding node in Firebase */
    /* If user navigates from one channel to another we establish a connection with new node and get the 
    corresponding messages */
    if (this.props.navigation.state.params.sessionID !== prevProps.navigation.state.params.sessionID) {
      await this.getSessionInfo();
      await this.getCurrentProfile();
      /* Subscribe to new chat channel */
      await this.ref();
      await this.getMessages();
    }
  };

  async componentWillUnmount() {
    /* Unsubscribe from database */
    await this.off();
  }

  /* Get messages to display after component is mounted on screen */
  getMessages = () => {
    this.connect(message => {
      this.setState(previousState => ({
        messages: GiftedChat.append(previousState.messages, message),
      }))
    });
  }

  /* Each sessionID corresponds to a chat channel and node in Firebase  */
  ref = () => {
    return database.ref(`/sessions/${this.state.sessionID}`);
  }

  /* Fetch last 20 messages pertaining to given chat channel and lister to parse new incoming message */
  connect = (callback) => {
    this.ref()
      .limitToLast(20)
      .on('child_added', snapshot => callback(this.parse(snapshot)));
  }

    /* newly created message object in GiftedChat format */
    parse = snapshot => {
    const { timestamp: numberStamp, text, user } = snapshot.val();
    const { key: _id } = snapshot;
    const timestamp = new Date(numberStamp);

    const message = {
      _id,
      timestamp,
      text,
      user,
    };
    return message;
  };

  /* function that accepts an array of messages then loop through messages */
  send = (messages) => {
    for (let i = 0; i < messages.length; i++) {
      const { text, user } = messages[i];
      const message = {
        text,
        user
      };
      this.append(message);
    }
  };

  /* append function will save the message object with a unique ID */
  append = message => this.ref().push(message);

Following Brett Gregson's advice and Mike Kamerman's answer, I implemented the following solution.

  1. When the user navigates from one channel to another, I trigger a set of functions. this.ref() function subscribes to new channel (node in Firebase).
  async componentDidUpdate(prevProps) {
    if (this.props.navigation.state.params.sessionID !== prevProps.navigation.state.params.sessionID) {
      await this.getSessionInfo();
      await this.ref();
      await this.switchSession();
    }
  };
  1. I set the messages state which is an array of messages to en empty array along with async/await mechanism. The getMessages function fetches the messages of new channel. This way messages belonging to preceding group are cleared and there is no accumulation of messages in the UI.
  switchSession = async () => {
    await this.setState({ messages: [] });
    await this.getMessages();
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM