You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native+Firebase聊天问题:发送消息显示但接收消息不显示

React Native Firebase聊天:接收消息不显示问题排查与修复

问题场景

在React Native+Firebase实现的聊天功能中,自己发送的消息能正常显示,但接收的消息无法显示,以下是当前使用的代码:

const ChatScreen: React.FC<Props> = ({ navigation,route}) => {
  const [input,setInput] = useState("");
  const [messages, setMessages] = useState<Message[]>([]);

  const sendMessage = () => {
    Keyboard.dismiss();
    db.collection('chats')
      .doc(route?.params?.currentUserId)
      .collection('messages')
      .add({
        timestamp: firebase.firestore.FieldValue.serverTimestamp(),
        message: input,
        senderId: route?.params?.currentUserId,
        receiverId: route?.params?.otherUserId,
      })
      .then((docRef) => {
        console.log("Document written with ID: ", docRef.id);
        setTimeout(() => {
          setDocRef(docRef.id);
        }, 2000); // Add a delay of 2 seconds to allow document to fully write to the database
        setInput('');
      })
      .catch((error) => {
        console.error("Error adding document: ", error);
      });
  };
  
  useEffect(() => {
    const unsubscribe = db
      .collection("chats")
      .doc(route?.params?.currentUserId)
      .collection("messages")
      .orderBy("timestamp", "asc")
      .onSnapshot((querySnapshot) => {
        if (!querySnapshot.empty) {
          const newMessages = querySnapshot.docs.map((doc) => doc.data());
          const filteredMessages = newMessages.filter(
            (message) => message.senderId === route?.params?.currentUserId
          );
          setMessagesSent(filteredMessages);
        } else {
          console.log("No messages found");
        }
      });
    return () => unsubscribe(); // Clean up the listener when component unmounts
  }, []);
      
  return (
    <SafeAreaView style={{flex:1, backgroundColor:"white"}}>
      <StatusBar style="light"/>
      <KeyboardAvoidingView behavior={Platform.OS === "ios" ? "padding" : "height"} style={styles.container}
      keyboardVerticalOffset={90}>
        <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
          <>
        <ScrollView contentContainerStyle={{padding: 15}}>
        {messages.map((message,id) => (
          <View key={id} style={styles.sender}>
            <Avatar 
              position="absolute"
              bottom={-15}
              right={-5}
              rounded
              containerStyle={{
                position:"absolute",
                bottom: -15,
                right: -5,
              }}
              source={{
                // uri: data.photoURL,
              }}
              size={30}
            />
            <Text style={styles.receiverText}>{message?.message}</Text>
          </View>
        ))}
        </ScrollView>
        <View style={styles.footer}>
        <TextInput 
          value={input} 
          onChangeText={text => setInput(text)}
          onSubmitEditing={() => input.trim() !== "" ? sendMessage() : null}
          style={styles.textInput}
        />
        <TouchableOpacity onPress={() => input.trim() !== "" && sendMessage()} activeOpacity={0.5}>
          <Ionicons name="send" size={24} color="#2B68E6"/> 
        </TouchableOpacity>
      </View>
          </>
          </TouchableWithoutFeedback>
          </KeyboardAvoidingView>
    </SafeAreaView>
  )
}

问题根源

  1. 消息存储逻辑缺陷:发送消息时仅写入发送方(currentUserId)的chats文档下的messages集合,接收方(otherUserId)的文档中无此消息,导致接收方监听不到新消息。
  2. 监听过滤与状态不匹配:useEffect中过滤条件仅保留发送方自己的消息,接收消息直接被过滤;且更新的是未定义的messagesSent状态,而非渲染用的messages状态,就算有接收消息也无法显示。
  3. 渲染逻辑单一:所有消息都用发送方样式渲染,未区分接收方消息的布局。

修复方案

1. 统一聊天房间ID,同步消息存储

生成唯一聊天房间ID(按用户ID排序拼接,避免重复房间),将消息写入统一房间的messages集合,双方监听同一个房间即可获取所有消息:

// 生成唯一聊天房间ID
const getChatRoomId = (userId1: string, userId2: string) => {
  return userId1 < userId2 ? `${userId1}-${userId2}` : `${userId2}-${userId1}`;
};

const sendMessage = () => {
  Keyboard.dismiss();
  const chatRoomId = getChatRoomId(route.params.currentUserId, route.params.otherUserId);
  
  db.collection('chats')
    .doc(chatRoomId)
    .collection('messages')
    .add({
      timestamp: firebase.firestore.FieldValue.serverTimestamp(),
      message: input,
      senderId: route.params.currentUserId,
      receiverId: route.params.otherUserId,
    })
    .then(() => {
      setInput('');
    })
    .catch((error) => {
      console.error("Error adding document: ", error);
    });
};

2. 修正监听逻辑,更新正确状态

监听统一聊天房间的消息,直接更新渲染用的messages状态:

useEffect(() => {
  const chatRoomId = getChatRoomId(route.params.currentUserId, route.params.otherUserId);
  
  const unsubscribe = db
    .collection("chats")
    .doc(chatRoomId)
    .collection("messages")
    .orderBy("timestamp", "asc")
    .onSnapshot((querySnapshot) => {
      const newMessages = querySnapshot.docs.map(doc => ({
        ...doc.data(),
        id: doc.id // 用文档ID作为唯一key
      })) as Message[];
      setMessages(newMessages);
    });
  
  return () => unsubscribe();
}, [route.params.currentUserId, route.params.otherUserId]);

3. 区分发送/接收消息样式

根据senderId判断消息归属,调整布局和样式:

{messages.map((message) => (
  <View 
    key={message.id} 
    style={[
      styles.messageContainer,
      message.senderId === route.params.currentUserId 
        ? styles.senderMessage 
        : styles.receiverMessage
    ]}
  >
    <Avatar 
      position="absolute"
      bottom={-15}
      right={message.senderId === route.params.currentUserId ? -5 : undefined}
      left={message.senderId !== route.params.currentUserId ? -5 : undefined}
      rounded
      size={30}
      source={{
        uri: message.senderId === route.params.currentUserId 
          ? currentUserPhoto 
          : otherUserPhoto
      }}
    />
    <Text 
      style={[
        styles.messageText,
        message.senderId === route.params.currentUserId 
          ? styles.senderText 
          : styles.receiverText
      ]}
    >
      {message.message}
    </Text>
  </View>
))}

对应的样式示例:

const styles = StyleSheet.create({
  messageContainer: {
    marginVertical: 8,
    maxWidth: "80%",
    position: "relative"
  },
  senderMessage: {
    alignSelf: "flex-end",
    backgroundColor: "#2B68E6",
    borderRadius: 20,
    borderBottomRightRadius: 5
  },
  receiverMessage: {
    alignSelf: "flex-start",
    backgroundColor: "#E5E5EA",
    borderRadius: 20,
    borderBottomLeftRadius: 5
  },
  messageText: {
    padding: 12,
    fontSize: 16
  },
  senderText: {
    color: "white"
  },
  receiverText: {
    color: "black"
  }
});

替代实现方式

  1. Firebase Cloud Functions自动同步:若坚持用用户ID作为文档名,可写云函数,当新消息写入时自动复制到接收方的messages集合,减少前端重复逻辑。
  2. 切换到Firebase Realtime Database:树形结构更适合实时聊天,数据同步更直接,无需处理复杂的集合监听逻辑。
  3. 使用现成聊天UI库:比如react-native-gifted-chat,已封装消息渲染、输入框、自动滚动等功能,只需接入Firebase数据即可快速开发。

内容的提问来源于stack exchange,提问作者RandomDeveloper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 06:17:04