+4

ReactJs - Xây dựng ứng dụng chat và deploy lên web - Phần 2

Ở phần trước mình đã xây dựng demo được một ứng dụng chat cơ bản, các bạn có thể xem lại tại đây https://viblo.asia/p/reactjs-xay-dung-ung-dung-chat-va-deploy-len-web-phan-1-Az45bAvwlxY Phần này sẽ làm thêm 1 chức năng định danh người chat và hiển thị những ai đang online và deploy ứng dụng lên heroku

Xây dựng phòng chat

Về phía client react trong các bạn mở file src/App.js thay đổi nội dung thành như này, mình sẽ comment giải thích trực tiếp trong code cho các bạn dễ hiểu

import React from 'react';
import $ from 'jquery';
import Messages from './message-list';
import Input from './input';
import _map from 'lodash/map';
import io from 'socket.io-client';

import './App.css';

export default class App extends React.Component {
    constructor(props) {
        super(props);
        //Khởi tạo state,
        this.state = {
            messages: [], // danh sách tin nhắn
            user: {id: '', name: ''},// người dùng hiện tại, nếu rỗng sẽ hiển thị form login, có sẽ hiển thị phòng chat
            userOnline:[] // danh sách người dùng đang online
        }
        this.socket = null;
    }
    //Connetct với server nodejs, thông qua socket.io
    componentWillMount() {
        console.log(this.state.user)
        this.socket = io('localhost:6969');
        this.socket.on('newMessage', (response) => {this.newMessage(response)}); //lắng nghe khi có tin nhắn mới
        this.socket.on('loginFail', (response) => {alert('Tên đã có người sử dụng')}); //login fail
        this.socket.on('loginSuccess', (response) => {this.setState({user: {id: this.socket.id, name: response}})}); //đăng nhập thành công 
         this.socket.on('updateUesrList', (response) => {this.setState({userOnline: response})}); //update lại danh sách người dùng online khi có người đăng nhập hoặc đăng xuất

    }
    //Khi có tin nhắn mới, sẽ push tin nhắn vào state mesgages, và nó sẽ được render ra màn hình
    newMessage(m) {
        const messages = this.state.messages;
        let ids = _map(messages, 'id');
        let max = Math.max(...ids);
        messages.push({
            id: max+1,
            userId: m.user.id,
            message: m.data,
            userName: m.user.name
        });
        let objMessage = $('.messages');
        if (objMessage[0].scrollHeight - objMessage[0].scrollTop === objMessage[0].clientHeight ) {
            this.setState({messages});
            objMessage.animate({ scrollTop: objMessage.prop('scrollHeight') }, 300); //tạo hiệu ứng cuộn khi có tin nhắn mới

        } else {
            this.setState({messages});
            if (m.id === this.state.user) {
                objMessage.animate({ scrollTop: objMessage.prop('scrollHeight') }, 300);
            }
        }
    }
    //Gửi event socket newMessage với dữ liệu là nội dung tin nhắn và người gửi
    sendnewMessage(m) {
        if (m.value) {
            this.socket.emit("newMessage", {data:m.value, user: this.state.user}); //gửi event về server
            m.value = ""; 
        }
    }
    //login để định danh người dùng
    login() {
        this.socket.emit("login", this.refs.name.value); 
    }

    render () {
        return (
           <div className="app__content">
              <h1>chat box</h1>
          {/* kiểm tra xem user đã tồn tại hay chưa, nếu tồn tại thì render form chat, chưa thì render form login */}
              { this.state.user.id && this.state.user.name ? 
                <div className="chat_window">
                    {/* danh sách user online */}
                    <div className="menu">
                        <ul className="user">
                        <span className="user-name">{this.state.user.name}</span>
                            <p>Online</p>
                            {this.state.userOnline.map(item =>
                                <li key={item.id}><span>{item.name}</span></li>
                            )}
                        </ul>
                    </div>
                {/* danh sách message */}
                    <div className="content">
                        <Messages user={this.state.user} messages={this.state.messages} typing={this.state.typing}/>
                        <Input sendMessage={this.sendnewMessage.bind(this)}/>
                    </div>
                </div> 
                :
                  <div className="login_form">{/* form login */}
                      <input type="text" name="name" ref="name"></input>
                      <input type="button" name="" value="Login" onClick={this.login.bind(this)}></input>
                  </div>
              }
            </div>
        )
    }
}

Message cần hiển thị cả tên người gửi nên sẽ thay đổi 1 chút src/mesage-item.js

import React from 'react';

export default class messageItem extends React.Component {
    render () {
        return (
            <li className={this.props.user? "message right appeared": "message left appeared"}>
                <div className="avatar"></div>
                <div className="text_wrapper">
                    <div className="text"><b>{this.props.message.userName}</b><br></br>{this.props.message.message}</div>
                </div>
            </li>
        )
    }
}

Về phía server node js mở file index.js và thay đổi nội dung như sau

var express = require('express');
var app = express();
var _findIndex = require('lodash/findIndex') // npm install lodash --save
var server = require('http').Server(app);
var port = (process.env.OPENSHIFT_NODEJS_PORT || process.env.PORT || 6969);
var io = require('socket.io')(server);
server.listen(port, () => console.log('Server running in port ' + port));

var userOnline = []; //danh sách user dang online
io.on('connection', function(socket) {
    console.log(socket.id + ': connected');
    //lắng nghe khi người dùng thoát
    socket.on('disconnect', function() {
        console.log(socket.id + ': disconnected')
        $index = _findIndex(userOnline, ['id', socket.id]);
        userOnline.splice($index, 1);
        io.sockets.emit('updateUesrList', userOnline);
    })
    //lắng nghe khi có người gửi tin nhắn
    socket.on('newMessage', data => {
        //gửi lại tin nhắn cho tất cả các user dang online
        io.sockets.emit('newMessage', {
            data: data.data,
            user: data.user
        });
    })
    //lắng nghe khi có người login
    socket.on('login', data => {
        // kiểm tra xem tên đã tồn tại hay chưa
        if (userOnline.indexOf(data) >= 0) {
            socket.emit('loginFail'); //nếu tồn tại rồi thì gửi socket fail
        } else {
            // nếu chưa tồn tại thì gửi socket login thành công
            socket.emit('loginSuccess', data);
            userOnline.push({
                id: socket.id,
                name: data
            })
            io.sockets.emit('updateUesrList', userOnline);// gửi danh sách user dang online
        }
    })

});

app.get('/', (req, res) => {![](https://images.viblo.asia/388d121e-72d0-4fc0-b946-bd4f3e125f94.png)
    res.send("Home page. Server running okay.");
})

Deploy lên web

Để có thể deploy ứng dụng, bạn cần có 1 tài khoản github và 1 tài khoản heroku nhé

Deploy server chat

Các bạn thêm file Procfile vào thư mục gốc của project với nội dung web: node index.js, đây là file cấu hình cho heroku biết nó sẽ phải chạy project của bạn thế nào Tiếp theo các bạn vào trang heroku tạo 1 ứng dụng mới chọn new->create new app, sau đó điền tên ứng dụng của bạn tù thích, điền tên xong thì Create app Tạo xong các bạn được chuển hướng đến màn hình quan lý của ứng dụng, kéo xuống chọn liên kết với github Kéo xuống dưới chọn deploy branch mà bạn vừa push lên github, đợi quá trình deploy xong, các bạn lên đầu trang và click vào Opend app, các bạn được chuyển hướng đến trang với dòng chữ Home page. Server running okay. là đã deploy đươc server chat thành công Các bạn nhớ chú ý tên miền của ứng dụng, ta sẽ dùng nó vào việc kết nối từ client chát với server chat

Deploy client chat

Về bản chất client chat chỉ là 1 trang tĩnh nên ta sẽ dùng tính năng GitHub Pages của github Các bạn đăng nhập vào github, tạo 1 repository tên gì cũng được, mình đặt là client-chat trong file /src/App.js, phần khởi tạo kết nối socket, ta không sử sủng địa chỉ localhost:6969 nữa mà dùng địa chỉ của server node js mà chúng ta vừa deploy ở trên, địa chỉ của mình là https://chattaap.herokuapp.com/

this.socket = io('https://chattaap.herokuapp.com/');

Sau khi tạo repository rồi thì push code từ local lên thôi, nhưng trước khi push, các bạn kiểm tra file package.json, trường homepage các bạn hay thay tên repository và tên tài khoản git của bạn vào

{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "homepage": "https://ththth0303.github.io/chat-client",
  "author": "Thang",
  "dependencies": {
    "an-array-of-english-words": "^1.3.1",
    "gh-pages": "^1.0.0",
    "jquery": "^3.2.1",
    "lodash": "^4.17.4",
    "react": "^16.0.0",
    "react-dom": "^16.0.0",
    "react-scripts": "1.0.14",
    "socket.io-client": "^2.0.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject",
    "deploy": "react-scripts build && gh-pages -d build"
  }
}

Tiếp theo là push lên remote reposirory, push xong các bạn chạy lệng npm run deploy, đợi khi hoàn thành Giờ các bạn có thể vào trang https://{tài khoản git}/github.io/{repository}, của mình là https://ththth0303.github.io/chat-client/ Các bạn nhập tên và click login xem có vào phòng chat được không nhé Như này là bạn đã thành công

Kết

Việc deploy lên web cũng k quá phức tạp, phần tiếp theo chúng ta sẽ lưu database người dùng, tin nhắn và một chức năng nhỏ nữa là typing(hiển thi ai đang nhập tin nhắn) Chat client https://github.com/ththth0303/chat-client/tree/part2 Chat server https://github.com/ththth0303/chat-server Demo https://ththth0303.github.io/chat-client/


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí