Как да създадете приложение в реално време с помощта на Socket.io, React, Node и MongoDB

Чудили ли сте се как се създават приложения в реално време? Виждали ли сте някога важността и случаите на използване на приложения в реално време?

Ако сте любопитни към горните въпроси и се нуждаете от отговор, тогава тази публикация в блога е за вас.

Първо, нека идентифицираме няколко случая на употреба, които се нуждаят от приложения в реално време:

  1. Получаване на актуализации за местоположението на вашата кабина на карта на заявление за резервация на такси.
  2. Получаване на нови съобщения незабавно в любимото ви приложение за чат.
  3. Информация за поръчка на храна за кухнята на любимия ви ресторант.

Всичко това са често срещаните сценарии от ежедневието ни, когато не можем да толерираме забавяне в актуализирането на информацията и следователно се нуждаем от комуникация в реално време.

Технологиите, които могат да се използват за комуникация в реално време, са:

  1. Кратко проучване : AJAX, създава интензивен трафик.
  2. Дълго анкетиране : Като AJAX, но сървърът задържа отговора, докато има актуализация. След като я получи, клиентът изпраща друга заявка и се нуждае от допълнителен заглавие, което да се премества напред-назад, което води до допълнителни режийни разходи.
  3. Web Sockets : правят възможно отварянето на интерактивна комуникация между клиента и сървъра. Човек може да изпрати заявка до сървъра и да получава отговори, управлявани от събития, без да анкетира сървъра за отговор, което прави уеб сокетите най -добрият избор за нашия случай на използване.

Повече подробна информация за горните три технологии можете да прочетете тук.

Ще се научим да създаваме приложение в реално време, като покрием следния сценарий.

Представете си, че седите в любимия си ресторант и имате цифрово меню. Поставяте поръчката и кухнята се актуализира относно вашата поръчка в реално време. Когато кухнята приключи с поръчката, те я актуализират и в реално време.

Характеристики в детайли:

  1. Поставяне на поръчка : Интерфейс за избор на количество и поставяне на поръчката за избрана хранителна стока в кухнята.
  2. Кухня : Интерфейс, който може да се отвори в множество кухни и да актуализира в реално време готвачите и готвачите по отношение на общия брой създадени поръчки и прогнозираното количество хранителни артикули, като им дава гъвкавостта да го актуализират. Също така има функционалност за изтегляне на отчета под формата на Excel лист.
  3. Промяна на прогнозата: Интерфейс за актуализиране на предвиденото количество храни.

Демонстрация на живо на този сценарий можете да намерите тук.

За по-добро разбиране го отворете едновременно в различни раздели / устройства, за да видите как данните се променят в реално време.

Изходният код е тук. Чувствайте се свободни да направите нещо иновативно / полезно отгоре.

Така че нека да започнем.

Технологичен стек:

Frontend : React.js, Reactstrap, Socket.io

Backend : Node.js (Express), MongoDB, Socket.io

Структура на папката:

/* Go to the root directory in the source code and find out the below-mentioned files. This architecture helps in creating a big modular App. */ backend-my-app/ /* Backend code of the app */ server.js /* Socket and backend code resides here*/ build/ /* Optional for deployment of Frontend Build */ package.json /* Backend dependency */ ... public/ src/ /* Frontend Sourcecode */ global/ /* Components getting used everywhere */ header.css header.js main/ Kitchen.js PlaceOrder.js UpdatePredicted.js App.js /* Routing logic and component assembly part */ package.json /* Frontend dependency */ ............

Обяснение на изходния код:

Предната част:

git clone //github.com/honey93/OrderKitchen.git cd OrderKitchen npm install npm start

Използвани пакети:

  1. Reactstrap: Лесни за използване компоненти на bootstrap4
  2. Socket.io: Socket.io е библиотека, която позволява в реално време двупосочна и базирана на събития комуникация между браузъра и сървъра.
  3. response-html-table-to-excel: Осигурява генериране на файл на Excel (.xls) от страна на клиента от елемент на таблица HTML.
  4. response-router-dom: DOM обвързвания за реакционен рутер. Състои се от много важни компоненти като BrowserRouter, използван, когато има сървър за обработка на динамична заявка, Switch, Route и т.н.

App Component

Път : src / App.js

Този компонент съдържа основната логика на маршрутизиране на Frontend. Този файл се използва в src / index.js в модула на браузъра. Кодът по-долу демонстрира един от подходите да запазите модула на приложението си.

import React, { Component } from "react"; import "./App.css"; import { Header } from "./global/header"; import { Switch, Route } from "react-router-dom"; import PlaceOrder from "./main/PlaceOrder"; import UpdatePredicted from "./main/UpdatePredicted"; import Kitchen from "./main/Kitchen"; /*The  component is the main part of React Router. Anywhere that you want to only render content based on the location’s pathname, you should use a  element. */ /* The Route component expects a path prop, which is a string that describes the pathname that the route matches */ /* The  will iterate over routes and only render the first one that matches the current pathname */ class App extends Component { render() { return ( ); } } export default App;

Компонент на заглавката

Път : src / global / header.js

Този компонент ще бъде често срещан и ще се използва в раздели като Поставяне на поръчка, Прогнозирани промени, Кухня. Този подход помага да се избегне дублиране на код и поддържа приложението модулно.

import React, { Component } from "react"; import { NavLink } from "react-router-dom"; import socketIOClient from "socket.io-client"; import "./header.css"; // The Header creates links that can be used to navigate // between routes. var socket; class Header extends Component { /* Creating a Socket client and exporting it at the end to be used across the Place Order, Kitchen, etc components*/ constructor() { super(); this.state = { endpoint: '//localhost:3001/' }; socket = socketIOClient(this.state.endpoint); } render() { return (   
  • Place Order
  • Change Predicted
  • Kitchen
); } } export { Header, socket };

Кухненски компонент

Път : src / main / Kitchen.js

Логиката и потребителският интерфейс на кухненския екран се намират в този компонент:

import React, { Component } from "react"; import { Button, Table, Container } from "reactstrap"; import { socket } from "../global/header"; import ReactHTMLTableToExcel from "react-html-table-to-excel"; class Kitchen extends Component { constructor() { super(); this.state = { food_data: [] // this is where we are connecting to with sockets, }; } getData = foodItems => { console.log(foodItems); this.setState({ food_data: foodItems }); }; changeData = () => socket.emit("initial_data"); /*As soon as the component gets mounted ie in componentDidMount method, firing the initial_data event to get the data to initialize the Kitchen Dashboard */ /* Adding change_data listener for listening to any changes made by Place Order and Predicted Order components*/ componentDidMount() { var state_current = this; socket.emit("initial_data"); socket.on("get_data", this.getData); socket.on("change_data", this.changeData); } /* Removing the listener before unmounting the component in order to avoid addition of multiple listener at the time revisit*/ componentWillUnmount() { socket.off("get_data"); socket.off("change_data"); } /* When Done gets clicked, this function is called and mark_done event gets emitted which gets listened on the backend explained later on*/ markDone = id => { // console.log(predicted_details); socket.emit("mark_done", id); }; getFoodData() { return this.state.food_data.map(food => { return (  {food.name}  {food.ordQty}  {food.prodQty}  {food.predQty}   this.markDone(food._id)}>Done  ); }); } render() { return (  

Kitchen Area

{this.getFoodData()}
NameQuantityCreated Till NowPredictedStatus
); } } export default Kitchen;

Поставете компонент за поръчка

Path: src/main/PlaceOrder.js

import React, { Component } from "react"; import { Button, Table, Container } from "reactstrap"; import { socket } from "../global/header"; class PlaceOrder extends Component { constructor() { super(); this.state = { food_data: [] // this is where we are connecting to with sockets, }; } getData = foodItems => { console.log(foodItems); foodItems = foodItems.map(food => { food.order = 0; return food; }); this.setState({ food_data: foodItems }); }; componentDidMount() { socket.emit("initial_data"); var state_current = this; socket.on("get_data", state_current.getData); } componentWillUnmount() { socket.off("get_data", this.getData); } //Function to place the order. sendOrder = id => { var order_details; this.state.food_data.map(food => { if (food._id == id) { order_details = food; } return food; }); console.log(order_details); socket.emit("putOrder", order_details); var new_array = this.state.food_data.map(food => { food.order = 0; return food; }); this.setState({ food_data: new_array }); }; // Changing the quantity in the state which is emitted to the backend at the time of placing the order. changeQuantity = (event, foodid) => { if (parseInt(event.target.value)  { if (food._id == foodid) { food.order = parseInt(event.target.value); } return food; }); this.setState({ food_data: new_array }); }; // To get the initial data getFoodData() { return this.state.food_data.map(food => { return (  {food.name}   this.changeQuantity(e, food._id)} value={food.order} type="number" placeholder="Quantity" />  this.sendOrder(food._id)}>Order  ); }); } render() { return (  

Order Menu

{this.getFoodData()}
ProductQuantityOrder
); } } export default PlaceOrder;

One more section called Update Predicted Path: src/main/UpdatePredicted.js similar to above section is there in the code repository.

Backend

Starting the Backend:

cd backend-my-app npm install node server.js

Packages used:

  1. Monk: A tiny layer that provides simple yet substantial usability improvements for MongoDB usage within Node.JS.
  2. Socket.io: Socket.io is a library that enables real-time, bidirectional and event-based communication between the browser and the server.

3. Express: Fast, minimalist web framework for node.

Main Code

Path: backend-my-app/server.js

const express = require("express"); const http = require("http"); const socketIO = require("socket.io"); // Connection string of MongoDb database hosted on Mlab or locally var connection_string = "**********"; // Collection name should be "FoodItems", only one collection as of now. // Document format should be as mentioned below, at least one such document: // { // "_id": { // "$oid": "5c0a1bdfe7179a6ca0844567" // }, // "name": "Veg Roll", // "predQty": 100, // "prodQty": 295, // "ordQty": 1 // } const db = require("monk")(connection_string); const collection_foodItems = db.get("FoodItems"); // our localhost port const port = process.env.PORT || 3000; const app = express(); // our server instance const server = http.createServer(app); // This creates our socket using the instance of the server const io = socketIO(server); io.on("connection", socket => { // console.log("New client connected" + socket.id); //console.log(socket); // Returning the initial data of food menu from FoodItems collection socket.on("initial_data", () => { collection_foodItems.find({}).then(docs => { io.sockets.emit("get_data", docs); }); }); // Placing the order, gets called from /src/main/PlaceOrder.js of Frontend socket.on("putOrder", order => { collection_foodItems .update({ _id: order._id }, { $inc: { ordQty: order.order } }) .then(updatedDoc => { // Emitting event to update the Kitchen opened across the devices with the realtime order values io.sockets.emit("change_data"); }); }); // Order completion, gets called from /src/main/Kitchen.js socket.on("mark_done", id => { collection_foodItems .update({ _id: id }, { $inc: { ordQty: -1, prodQty: 1 } }) .then(updatedDoc => { //Updating the different Kitchen area with the current Status. io.sockets.emit("change_data"); }); }); // Functionality to change the predicted quantity value, called from /src/main/UpdatePredicted.js socket.on("ChangePred", predicted_data => { collection_foodItems .update( { _id: predicted_data._id }, { $set: { predQty: predicted_data.predQty } } ) .then(updatedDoc => { // Socket event to update the Predicted quantity across the Kitchen io.sockets.emit("change_data"); }); }); // disconnect is fired when a client leaves the server socket.on("disconnect", () => { console.log("user disconnected"); }); }); /* Below mentioned steps are performed to return the Frontend build of create-react-app from build folder of backend Comment it out if running locally*/ app.use(express.static("build")); app.use("/kitchen", express.static("build")); app.use("/updatepredicted", express.static("build")); server.listen(port, () => console.log(`Listening on port ${port}`));

Database: MongoDB

Mlab: Database as a service for MongoDB

Collection Name: FoodItems

Document format: At least one document is needed in the FoodItems collection with the below mentioned format.

{ "name": "Veg Roll", // Food Name "predQty": 100, // Predicted Quantity "prodQty": 295, // Produced Quantity "ordQty": 1 // Total Order Quantity }

Hope you got the understanding of how to create a modular real time app using the trending MERN stack. If you found it helpful clap below, give stars to the project repo and share with your friends too.