使用Mongodb

时间:2018-05-06 12:17:03

标签: javascript mongodb asynchronous react-native chat

我很反应原生,我正在开发一个聊天/论坛应用程序。最近,我在尝试为我的应用创建直接消息部分时遇到了一些麻烦。我不知道如何将我的数据库连接到我的前端。这是问题所在:

我使用的mongodb数据库包含两个集合:消息和对话。

每个会话都有一个唯一的ID,每条消息都有一个与其所属的会话对应的chatId。

在我的本机应用程序中,在Direct Message组件中,我有一个显示不同聊天的flatlist

当直接消息组件willMount()我调用异步函数getChats()时,它从当前用户所属的数据库中获取聊天。然后将获取的聊天设置为状态。

然后,在getChats()内,在聊天设置为状态后,我有for loop基本上遍历整个this.state.chats数组,然后调用函数{{1}它获取与聊天ID共享相同chatIds的所有消息。然后,已获取的消息将添加到getMessages(this.state.chats[i].id)

最后,带有道具的this.state.messages

flatlist

,进行聊天。

我希望能够在聊天keyExtractor={(item)=>item._id} data ={this.state.chats} renderItem={({item})=>this._renderChats(item)} extraData = {this.state}中显示最新消息内容和发件人,但是,错误消息说消息内容未定义。

我认为这可能是由于消息未设置为呈现聊天之前的状态,但我不确定。

你会如何解决这个问题?你会改变前端还是后端?你会改变两者吗?我应该分享我的代码,以便更容易理解问题吗?

提前致谢。

1 个答案:

答案 0 :(得分:0)

非常清楚地解释了这个问题!简而言之:

  

如何使用会话概述构建聊天页面(显示每个会话的最后一条消息) - 使用React Native,MongoDB,NodeJS和Express

一些说明:

  • 使用一致的命名,即。将chatId重命名为conversationId,将chats重命名为conversations
  • 尝试最小化互联网请求 - 因为它们资源密集且速度慢

    • 现在,您的算法每次打开页面时都会发出conversations.count+1个请求,可能只是1
    • 对于第一页,您只需要每次会话中的最后一条消息
    • 在页面打开时加载其余的消息以进行对话
    • 因此,您不需要extraData字段
    • (虽然缓存,请参阅其他说明)
    • 例如。使用GraphQL query('currentUser.conversations { text, messages(limit 1), ... }')
    • 例如。使用rest + mongoose:

      // controller
      // see https://stackoverflow.com/questions/32207457/node-js-mongoose-populate-limit
      // populate the first item in conversation.messages
      const conversations = ()=> db.Conversations
        .find(...)
        .populate({ path: 'messages', options: { limit: 1, sort: { created: -1} }}))
        .exec(...)
      
      // router
      router.get('/conversations', ({user}, res)=>
        getConversations({user})
          .then(data=> res.send({data}))
          .catch(error=> res.send({error}))
      )
      
    • (这假设消息是对话中的虚拟属性

      // schema
      const messageSchema = new Schema({
        text: String, ...
        conversationId: {type: Schema.ObjectId, ref: 'conversation'}
      })
      
      const conversationSchema = new Schema({
        participants: [{type: Schema.ObjectId, ref: 'user'}]
      })
      // add a "virtual" field to conversation called messages
      // https://stackoverflow.com/questions/43882577/mongoosejs-virtual-populate
      conversationSchema.virtual('messages', {
        ref: 'message',
        localField: '_id',
        foreignField: 'conversationId'
      })
      // make sure the "virtual" fields are included in conversion
      conversationSchema.set('toObject', { virtuals: true });
      conversationSchema.set('toJSON', { virtuals: true });
      
  • 假设所有数据都已损坏;例如。如果消息数据丢失,应用程序不应该崩溃。在访问之前检查该属性是否存在,并在继续之前将其转换为预期的数据类型。

    • 确保错误不被“吞噬”;例如。如果您有.catch,请确保始终拥有.then并记录错误,并将错误消息粘贴到问题中(如果有)。
    • Uncaught TypeError: Cannot read property 'c' of undefined,在执行a.b.c时,您知道a是一个对象,可以通过先检查来避免; if (a.b) use(a.b.c)或短信:a.b && use(a.b.c)
  • 正确的假设,发生的事情:
    • ConversationsPage已初始化,state.conversations为空
    • willMount调用 - > fetchAllConversations已启动,需要时间,异步
    • willMount结束 - >被render调用 - >空列表呈现
    • fetchAllConversations'首次请求完成 - > setState{conversations} - >再次调用render - >呈现完整列表(在您的情况下,它因为丢失了最后一个消息字段而崩溃)
    • fetchAllConversations调用fetchMessagesForConversations,它发出许多api请求,可能多次调用setState(无效),这反过来会导致重新呈现
  • 不要忘记加载状态

      state = {loading: false}
      render () {
        const {loading} = this.state
        return <FlatList renderHeader={()=> loading? <ActivityIndicator .../>: null} .../>
      }
    
  • 相反,一个简单的修复将在加载所有消息后调用setState:

    async fetchAllConversations () {
      const conversations = await api.conversations()
      await Promise.all(conversations.map(c=> c.messages = await api.messagesForConversationId(c._id)))
      // similar to for (let i=0; i<conversations.length; i++) {
      //  const c = conversations[i]; c.messages = await api.messagesForConversationId(c._id)}
      return conversations
    }
    async reload () {
      this.setState({loading: true})
      this.fetchAllConversations()
        .then(conversations=> this.setState({conversations}))
        .catch(showError)
        // stop loading both on error and success
        .then(()=> this.setState({loading: false}))
    }
    state = {loading: false, conversations: []}
    willMount () { this.reload() }
    
  • 虽然更好的解决方案将从上面替换fetchAllConversations,假设使用服务器端上面提到的虚拟属性和人口:

    async fetchAllConversationsIncludingLastMessage () {
      const conversations = await api.conversationsWithLastMessage()
      return conversations
    }
    
  • 这会减少流量:

    • ConversationsPage已初始化,state.conversations为空
    • willMount调用 - &gt; reload已启动,需要时间,异步
    • willMount结束 - &gt;被render调用 - &gt;加载指标呈现
    • reload唯一的请求完成 - &gt; setState{conversations} - &gt;再次调用render - &gt;完整列表呈现

附加说明:

  • 查看docker以简化服务器设置(即让mongodb + nodejs一起运行)
  • 我认为你有一个中间件可以进行身份​​验证,+查询中的正确授权逻辑(例如,只查找授权用户应该访问的对话/消息)
    • 即。 db.Conversation.find({participants: {$includes: req.user._id}}) // pseudocode
    • 即。在消息中,首先查看具有该ID的对话是否将用户作为参与者
  • 你如何处理分页? (例如,当有很多帖子时,防止数据提取速度慢和UI速度慢)(提示:使用“光标”代替“偏移” - 防止重复问题等)
  • 使用某些库在本地缓存数据,以改善感知和实际加载时间。
    • 中央国家管理(使用例如。mobxreduxapollo ...)解决其中的一部分
    • 如果你打算无论如何使用REST,请制作一个api包装助手+看看mobx
    • 否则,请查看graphql和apollo或类似的
祝你好运!