Реагируйте на собственный чат используя Mongodb - PullRequest
0 голосов
/ 06 мая 2018

Я довольно новичок, чтобы реагировать на нативные, и сейчас я разрабатываю приложение для чата / форума Недавно у меня возникли проблемы с попыткой создать раздел прямого сообщения для моего приложения. Я не знаю, как подключить мою базу данных к моему интерфейсу. Вот вопрос:

Я использую базу данных mongodb, которая состоит из двух коллекций: сообщений и разговоров.

Каждый диалог имеет уникальный Id, а каждое сообщение имеет chatId, соответствующий разговору, которому он принадлежит.

В моем собственном приложении реакции, внутри компонента Direct Message, у меня есть flatlist, который отображает различные чаты.

Когда компонент Direct Message willMount(), я вызываю асинхронную функцию getChats(), которая выбирает чаты из базы данных, частью которой является текущий пользователь. Полученные чаты затем устанавливаются в состояние.

Затем, внутри getChats(), после того, как чаты установлены в состояние, у меня есть for loop, который в основном перебирает весь массив this.state.chats, а затем вызывает функцию 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}

, отображает чаты.

Я хочу иметь возможность показывать содержимое последних сообщений и отправителя в чате View, однако ошибка, указывающая, что содержимое сообщений не определено.

Я думаю, это может быть связано с тем фактом, что сообщения не устанавливаются в состояние до отображения чатов, но я не уверен.

Как бы вы решили это? Вы бы поменяли интерфейс или бэкэнд? Вы бы поменяли оба? Должен ли я поделиться своим кодом, чтобы было легче понять проблему?

Заранее спасибо.

1 Ответ

0 голосов
/ 06 мая 2018

Очень четкое объяснение проблемы! Короче говоря:

Как создать страницу чата с обзором разговоров (показывая последнее сообщение для каждого разговора) - используя React Native, MongoDB, NodeJS и Express

.

Некоторые заметки:

  • использовать согласованное именование , т.е. переименуйте chatId в conversationId и chats в conversations
  • попытаться минимизировать интернет-запросы - потому что они ресурсоемкие и медленные

    • прямо сейчас ваш алгоритм делает conversations.count+1 запросов при каждом открытии страницы, может быть просто 1
    • для первой страницы, вам нужно только последнее сообщение в каждом разговоре
    • загрузить остальные сообщения для разговора при открытии его страницы
    • тем самым вам не нужно поле extraData
    • (хотя кеширование, см. Дополнительные заметки)
    • например. используя GraphQL query('currentUser.conversations { text, messages(limit 1), ... }')
    • например. используя отдых + мангуст:

      // 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 вызвано -> reload запущено, занимает время, асинхронно
    • willMount заканчивается -> render вызывается -> отображается индикатор загрузки
    • reload заканчивается только запрос -> setState{conversations} -> render вызывается снова -> отображается полный список

.

Дополнительные примечания:

  • посмотрите в docker для упрощенной настройки сервера (т. Е. Чтобы mongodb + nodejs работали вместе)
  • Полагаю, у вас есть промежуточное ПО, которое выполняет аутентификацию, + исправьте логику авторизации в запросе (например, найдите только те разговоры / сообщения, к которым должен иметь доступ авторизованный пользователь)
    • т. db.Conversation.find({participants: {$includes: req.user._id}}) // pseudocode
    • т. в сообщениях сначала проверьте, есть ли в разговоре с этим идентификатором пользователь
  • как вы справляетесь с нумерацией страниц? (например, чтобы предотвратить медленную выборку данных и медленный пользовательский интерфейс при большом количестве записей) (советы: используйте «курсор» вместо «смещение» - предотвращает дублирование и т. д.)
  • Используйте некоторую библиотеку для локального кэширования данных, чтобы улучшить воспринимаемое и фактическое время загрузки.
    • Центральное государственное управление (используя, например, mobx, redux, apollo ...) решает некоторые из этих проблем
    • если вы собираетесь использовать REST так или иначе, сделайте помощник обертки API + посмотрите на mobx
    • в противном случае проверьте graphql и apollo или аналогичный

Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...