Sådan oprettes en realtidsapp ved hjælp af Socket.io, React, Node & MongoDB

Har du nogensinde spekuleret på, hvordan apps i realtid er bygget? Har du nogensinde bemærket vigtigheden og brugen af ​​realtidsapplikationer?

Hvis du er nysgerrig efter ovenstående spørgsmål og har brug for et svar, så er dette blogindlæg noget for dig.

Lad os først identificere et par brugssager, der kræver applikationer i realtid:

  1. Sådan får du placeringsopdateringer til din taxa på et kort over en taxa-bookingansøgning.
  2. Få straks nye beskeder i din yndlingschatapplikation.
  3. Oplysninger om madordreopdatering til køkkenet på din yndlingsrestaurant.

Disse er alle de almindelige scenarier i vores daglige liv, hvor vi ikke kan tåle en forsinkelse i opdateringen af ​​information og derfor har brug for kommunikation i realtid.

Teknologier, der kan bruges til realtidskommunikation, er:

  1. Kort polling : AJAX, skaber tung trafik.
  2. Lang afstemning : Ligesom AJAX, men serveren holder svaret, indtil den har en opdatering. Efter modtagelse af det sender klienten en anden anmodning og har brug for yderligere overskrift, der skal krydses frem og tilbage, hvilket forårsager yderligere omkostninger.
  3. Web Sockets : gør det muligt at åbne interaktiv kommunikation mellem klienten og serveren. Man kan sende en anmodning til serveren og modtage hændelsesdrevne svar uden at polle serveren for et svar, hvilket gør websockets til det bedste valg i vores brugssag.

Mere detaljeret information om ovenstående tre teknologier kan læses her.

Vi lærer at oprette en applikation i realtid ved at dække følgende scenarie.

Forestil dig, at du sidder på din yndlingsrestaurant og har en digital menu. Du placerer ordren, og køkkenet bliver opdateret vedrørende din ordre i realtid. Når køkkenet er færdigt med ordren, opdaterer de det også i realtid.

Funktioner i detaljer:

  1. Bestil : Interface for at vælge mængden og placere ordren for en valgt madvare i køkkenet.
  2. Køkken : Interface, der kan åbnes på tværs af flere køkkener og opdateres i realtid kokke og kokke med hensyn til de samlede ordrer oprettet og forudsagt mængde madvarer, hvilket giver dem fleksibiliteten til at opdatere det. Har også en funktionalitet til at downloade rapporten i form af et excel-ark.
  3. Ændret forudsagt : Interface til opdatering af den forudsagte mængde madvarer.

En live demo af dette scenarie kan findes her.

For bedre forståelse skal du åbne den i forskellige faner / enheder på samme tid for at se dataændring i realtid.

Kildekoden er her. Du er velkommen til at lave noget innovativt / nyttigt oven på det.

Så lad os komme i gang.

Teknologi stak:

Frontend : React.js, Reactstrap, Socket.io

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

Mappestruktur:

/* 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 */ ............

Forklaring af kildekode:

Frontend:

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

Brugte pakker:

  1. Reactstrap: Let at bruge bootstrap4-komponenter
  2. Socket.io: Socket.io er et bibliotek, der muliggør realtids, tovejs og begivenhedsbaseret kommunikation mellem browseren og serveren.
  3. react-html-table-to-excel: Giver en klientsidegenerering af Excel (.xls) -fil fra HTML-tabelelement.
  4. react-router-dom: DOM-bindinger til reager router. Den består af mange vigtige komponenter som BrowserRouter, der bruges, når der er en server til at håndtere dynamisk anmodning, switch, rute osv.

Appkomponent

Sti : src / App.js

Denne komponent indeholder hovedrute-logikken i Frontend. Denne fil bruges i src / index.js inde i Browser Router Module. Koden nedenfor viser en af ​​fremgangsmåderne til at holde din app modulær.

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;

Sidehovedkomponent

Sti : src / global / header.js

Denne komponent vil være almindelig og bruges på tværs af sektionerne som Place Order, Change Predicted, Kitchen. Denne fremgangsmåde hjælper med at undgå duplikering af kode og holder applikationen modulær.

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 };

Køkkenkomponent

Sti : src / main / Kitchen.js

Kitchen Screen UI-logikken og html-koden findes i denne komponent:

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()}
Name Quantity Created Till Now Predicted Status
); } } export default Kitchen;

Bestil komponent

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()}
Product Quantity Order
); } } 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

Dokumentformat : Mindst ét ​​dokument er nødvendigt i FoodItems-samlingen med nedenstående format.

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

Håber du har forståelse for, hvordan du opretter en modulær realtidsapp ved hjælp af den trendende MERN-stak. Hvis du fandt det nyttigt at klappe nedenfor, skal du give stjerner til projektrepanoen og også dele med dine venner.