fun joker
fun joker

Reputation: 1727

How to update state in reactjs?

I have created chat application using reactjs, nodejs, mongodb. I am storing the data in mongodb but while mapping over messages array it doesn't show messages in chat app why so ?

I am using message state variable to store single message and all these single messages are added inside messages array (state variable). I set message: "" and date : "" but what is happening in my case even before displaying message on screen it is set to empty string. How can I map over messages and display all messages in chat app ?

Code:

server.js:

const express = require('express');
const mongoose = require('mongoose');
const socket = require('socket.io');
const message = require('./model/message')

const app = express();

const mongoURI = require('./config/keys').mongoURI;

mongoose.connect(mongoURI, {useNewUrlParser: true}, function (err,res) {

    if(err){
      console.log("There is error: " + err);
    }else{
      console.log("Connected to mongo database") 
    }

})

let db = mongoose.connection;

db.once('open', function() {
  console.log("Database is now connected");

  let io =  socket(server);

io.on("connection", function(socket){
  console.log("Socket Connection Established with ID :"+ socket.id)

  socket.on('disconnect', function(){
    console.log('User Disconnected');
  });

  let chat = db.collection('chat');

      socket.on('SEND_MESSAGE', function(data){
        let message = data.message;
        let date = data.date;

        // Check for name and message
        if(message !== '' || date !== ''){
            // Insert message
            chat.insert({message: message, date:date}, function(){
                socket.emit('RECEIVE_MESSAGE', [data]);
            });
        }
    });

    chat.find().sort({_id:1}).toArray(function(err, res){
      if(err){
          throw err;
      }
      // Emit the messages
      socket.emit('RECEIVE_MESSAGE', res);
    });

}) 

});


const port = 5000;

let server = app.listen(5000, function(){
  console.log('server is running on port 5000')
});

messages.js:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

// Create Schema
const MessageSchema = new Schema({
  message: {
    type: String,
    required: true
  },
  date: {
    type: Date,
    default: Date.now
  }
});

module.exports = mongoose.model('Message', MessageSchema);

chat.js:

import React, { Component } from 'react'
import './chat.css'
import io from "socket.io-client";

export default class Chat extends Component {

    constructor(props){
        super(props);

        this.state = {
            message: '',
            date: '',
            messages: []
        };
        this.sendMessage = this.sendMessage.bind(this);
        this.addMessage = this.addMessage.bind(this);

        this.socket = io('localhost:5000');
    }

    componentDidMount() {
      this.socket.on('RECEIVE_MESSAGE', data => {
          this.addMessage(data);
      });
    }

    sendMessage(event) {
      event.preventDefault();

      if(this.state.message !== ''){
        this.socket.emit('SEND_MESSAGE', {
            message: this.state.message,
            date: Date.now()
        });
      }
    };

    addMessage(data) {
      this.setState({
        messages: [...this.state.messages, data],
        message: '', 
        date: ''
      });
      console.log(this.state.messages);
    };

    render() {
        return (
        <div>
                <div id="status"></div>
                <div id="chat">
                    <div className="card">
                        <div id="messages" className="card-block">
                            {this.state.messages.map((message, index) => {
                                    return (
                                        <div key={index} className="msgBox"><p className="msgText">{message.message}</p></div>
                                    )
                            })}
                        </div>
                    </div>
                    <div className="row">
                        <div className="column">
                            <input id="inputmsg" type="text" placeholder="Enter Message...."
                            value={this.state.message} onChange={ev => this.setState({message: ev.target.value})}/>
                        </div>
                        <div className="column2">
                            <button id="send" className="button" onClick={this.sendMessage}>Send</button>
                        </div>
                    </div>
                </div>
        </div>
        )
    }
}

Screenshot of console.log(this.state.messages) :

enter image description here

Upvotes: 0

Views: 338

Answers (1)

Vytautas Lozickas
Vytautas Lozickas

Reputation: 226

Judging from your server side code it looks like you do emit RECEIVE_MESSAGE as an array and so doing [ ...state.messages, data ] results in multidimensional array being created in react. You can either emit a single message via RECEIVE_MESSAGE or change your addMessage to do [ ...state.messages, ...data ].

To explain this more: So at first your messages state is set to an empty array. Then all your old messages comes from socket as an array (data) and addMessage gets called and tries to spread elements of state.messages (which is still empty at this point) and then adds a data (which is an array) in the end of that new array being set to state. The result is that you get an array with one element: all your previous messages (array in array). When new message comes in addMessage does the same spreading and so state.messages gets spread into a new array and data (which is again - an array) gets added to the end of it. The result is array with two elements: the first array (data) that got added in the beginning and the new one. By doing ...data in addMessage function you're spreading the actual array of data and putting its elements into the state but not the array itself so [ ...state.messages, ...data ] acts like a concatenation of elements in two arrays: state.messages and data.

Upvotes: 1

Related Questions