I. Project preview

React + React-router +redux+ node. js+socket. IO stack: React +react-router+ node. js+socket

1. In the first/loginYou can see the login and registration buttons below

2. Click the register button, and the route jumps to/register, register an account, the user name and password are LHH, select “cow person”, click register, after the route will jump to/geniusinfo, that is, the perfect information page, select a picture and perfect the information and click the save button

3. You can see that the content page with three TAB options is displayed. Click “I” to switch to route/meYou can see the personal center content, but there is still no content in the boss and message TAB page. You can follow the previous steps to register a boss account, just select the boss option when registering

4. Now you can see the list of LHH and LCE accounts respectively

5. Click to enter the chat room and enter the content

Second, the main content of the project will be explained

1. The directory of the project excluding node_modules
├ ─ build │ └ ─static│ │ ├ ─ CSS └ ─ js ├ ─ config │ └ ─ jest ├ ─ public ├ ─ scripts ├ ─ server └ ─ SRC ├ ─ component │ ├ ─ authroute │ ├ ─ avatars - the selector │ ├ ─ boss │ ├ ─ chat │ ├ ─ dashboard │ ├ ─ genius │ ├ ─ img │ ├ ─ logo │ ├ ─ MSG │ ├ ─ navlink │ │ └ ─ img │ ├ ─ user │ └ ─ usercard ├─ Container │ ├─ Bossinfo │ ├─ geniusInfo │ ├─login │ ├─ register ├─Copy the code

The contents of the build folder are the contents packaged by NPM Run Build, which can be accessed if the back-end interface is enabled in the project

2. Entrance page
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
// eslint-disable-next-line
import { BrowserRouter } from 'react-router-dom';
import App from './app'

import reducers from './reducer'
import './config'
import './index.css'

const store = createStore(reducers, compose(
    applyMiddleware(thunk),
    window.devToolsExtension?window.devToolsExtension():f= >f
))

// Boss Genius me MSG 4 pages
ReactDOM.render(
    (<Provider store={store}>
        <BrowserRouter>
            <App></App>
        </BrowserRouter>
    </Provider> ),
    document.getElementById('root'))Copy the code

Using the React-Redux Provider, you can implement global state storage. Sub-components can obtain the state stored globally by using props

const store = createStore(reducers, compose(
    applyMiddleware(thunk),
    window.devToolsExtension?window.devToolsExtension():f= >f
))
Copy the code

The main purpose of the above code is to configure the browser’s Redux plug-in, which allows you to view the data in State from the console. Take a look at the code in app.js

import React from 'react'
import Login from './container/login/login.js';
import Register from './container/register/register.js';
import AuthRoute from './component/authroute/authroute.js';
import BossInfo from './container/bossinfo/bossinfo.js';
import Geniusinfo from './container/geniusinfo/geniusinfo';
import Dashboard from './component/dashboard/dashboard';
import Chat from './component/chat/chat'
import {  Route,  Switch } from 'react-router-dom';

class App extends React.Component{
    render() {
        return (
            <div>
                <AuthRoute></AuthRoute>
                <Switch>
                    <Route path='/bossinfo' component={BossInfo}></Route>
                    <Route path='/geniusinfo' component={Geniusinfo}></Route>
                    <Route path='/login' component={Login}></Route>
                    <Route path='/register' component={Register}></Route>
                    <Route path='/chat/:user' component={Chat}></Route>
                    <Route component={Dashboard}></Route>
                </Switch>
                
            </div>)}}export default App
Copy the code

Here is mainly about the main page of the code to separate out. In Authroute.js, it is the logical judgment of route jump. UI component in the page is also used in ANTD-mobile plug-in client to receive and transmit data into socket. IO -client, the code is in chat.redux.js. A chatid is also required to create a unique chatid to represent the uniqueness of the chat room. The chatid can be used to concatenate from and to. The concatenation function is written in util.js.

3.Server

Node.js express framework is used for back-end interface, mongodb is used for database, files connected to database are stored in server folder, and Model.js is directly connected to mongodb database.

const mongoose = require('mongoose');
// Connect to mongo and use the collection my_app
const DB_URL = "mongodb://localhost:27017/chat_app";
mongoose.connect(DB_URL);

const models = {
    user: {
        'user': { 'type': String.'require': true },
        'pwd': { 'type': String.'require': true },
        'type': { 'type': String.'require': true },
        / / avatar
        'avatar': { 'type': String },
        // Personal profile or job description
        'desc': { 'type': String },
        / / position
        'title': { 'type': String },
        // If it is boss, there are two more fields
        'company': { 'type': String },
        'money': { 'type': String}},chat: {
        'chatid': { 'type': String.'require': true },
        'from': { 'type': String.'rewuire': true },
        'to': { 'type': String.'require': true },
        'read': { 'type': String.'require': true },
        'content': { 'type': String.'require': true.'default': ' ' },
        'create_time': { 'type': Number.'default': new Date().getTime() }
    }
}
for (let m in models) {
    mongoose.model(m, new mongoose.Schema(models[m]))
}
module.exports = {
    getModel: function(name) {
        return mongoose.model(name)
    }
}
Copy the code

The connected database port number is 27017, which depends on the database port number of your computer. HTTP, Express and socket. IO plugins are introduced in server.js. The server uses port 9093.

const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const model = require('./model')
    // const User = model.getModel('user');
const Chat = model.getModel('chat');
const path = require('path')
const app = express();
//work with express
const server = require('http').Server(app);
const io = require('socket.io')(server);
io.on('connection'.function(socket) {
    // console.log('user login')
    socket.on('sendmsg'.function(data) {
        const { from, to, msg } = data;
        const chatid = [from, to].sort().join('_');
        Chat.create({ chatid, from, to, content: msg }, function(err, doc) {
                // console.log(doc._doc)
                io.emit('recvmsg'.Object.assign({}, doc._doc))
            })
            // console.log(data);
            // io.emit('recvmsg', data)})})const userRouter = require('./user');
app.use(cookieParser());
app.use(bodyParser.json())
app.use('/user', userRouter);
app.use(function(req, res, next) {
    if (req.url.startsWith('/user/') || req.url.startsWith('/static/')) {
        return next()
    }
    return res.sendFile(path.resolve('build/index.html'))
})
app.use('/', express.static(path.resolve('build')))
server.listen(9093.function() {
    console.log('Node app start at port 9093')});Copy the code

The interface used by the client is written in user.js

const express = require('express')
const Router = express.Router();
const model = require('./model')
const User = model.getModel('user');
const Chat = model.getModel('chat');
const _filter = { 'pwd': 0.'__v': 0 };

// Delete all chat logs
// Chat.remove({}, function(e, d) {})
/ / encryption
const utils = require('utility');
Router.get('/list'.function(req, res) {
    const { type } = req.query
    // Delete all users
    // User.remove({}, function(e, d) {})
    User.find({ type }, _filter, function(err, doc) {
        return res.json({ code: 0.data: doc })
    })
});
Router.get('/getmsglist'.function(req, res) {
    const user = req.cookies.userid;
    User.find({}, function(err, userdoc) {
        let users = {};
        userdoc.forEach(v= > {
            users[v._id] = { name: v.user, avatar: v.avatar }
        })
        Chat.find({ '$or': [{ from: user }, { to: user }] }, function(err, doc) {
            // console.log(doc)
            if(! err) {return res.json({ code: 0.msgs: doc, users: users })
            }
        })
    })
})
Router.post('/readmsg'.function(req, res) {
    const userid = req.cookies.userid;
    const { from } = req.body;
    // console.log(userid, from)
    Chat.update({ from.to: userid }, { '$set': { read: true}}, {'multi': true },

        function(err, doc) {
            if(! err) {return res.json({ code: 0.num: doc.nModified })
            }
            return res.json({ code: 1.msg: 'Modification failed' })
        })
})
Router.post('/update'.function(req, res) {
    const userid = req.cookies.userid;
    if(! userid) {return json.dumps({ code: 1 });
    }
    const body = req.body;
    User.findByIdAndUpdate(userid, body, function(err, doc) {
        const data = Object.assign({}, {
            user: doc.user,
            type: doc.type
        }, body)
        return res.json({ code: 0, data })
    })
});
Router.post('/login'.function(req, res) {
    const { user, pwd } = req.body;
    User.findOne({ user, pwd: md5Pwd(pwd) }, _filter, function(err, doc) {
        if(! doc) {return res.json({ code: 1.msg: 'Wrong username or password' });
        }
        res.cookie('userid', doc._id)
        return res.json({ code: 0.data: doc })
    })
});
Router.post('/register'.function(req, res) {
    console.log(req.body);
    const { user, pwd, type } = req.body;
    User.findOne({ user }, function(err, doc) {
        if (doc) {
            return res.json({ code: 1.msg: 'Username reset'})}const userModel = new User({ user, pwd: md5Pwd(pwd), type });
        userModel.save(function(e, d) {
            if (e) {
                return res.json({ code: 1.msg: 'Back end error'})}const { user, type, _id } = d;
            res.cookie('userid', _id)
            return res.json({ code: 0.data: { user, type, _id } })
        })
    })
})
Router.get('/info'.function(req, res) {
    const { userid } = req.cookies;
    if(! userid) {return res.json({ code: 1 })
    }
    User.findOne({ _id: userid }, _filter, function(err, doc) {
            if (err) {
                return res.json({ code: 1.msg: 'Back end error'})}if (doc) {
                return res.json({ code: 0.data: doc })
            }
        })
        // The user has no cookies

});
// Add salt to the password
function md5Pwd(pwd) {
    const salt = 'lhh_is_good_1310486! @ # % ^ 5 ~ * ';
    return utils.md5(utils.md5(pwd + salt))
}
module.exports = Router
Copy the code

Third, summary

This project has realized the separation of data acquisition and presentation code, which is a further improvement in learning React, Node and WebSocket. Of course, there are still many improvements to be made, such as async and await data acquisition asynchronously and so on. As a front-end rookie, I still hope that seniors can give some learning suggestions and Pointers. Finally, I attach the code link of this project to github link