react mongodb_如何使用Socket.io,React,Node和MongoDB创建实时应用

react mongodb

Ever wondered how real time apps are built? Ever noticed the importance and use cases of real time applications?

有没有想过如何构建实时应用程序? 是否曾经注意到实时应用程序的重要性和用例?

If you are curious about the above questions and need an answer, then this blog post is for you.

如果您对以上问题感到好奇并需要答案,那么此博客文章适合您。

First, let’s identify a few use cases needing real time applications:

首先,让我们确定一些需要实时应用的用例:

  1. Getting location updates for your cab on a map of a cab booking application.

    在出租车预订应用程序的地图上获取出租车的位置更新。
  2. Getting new messages instantly on your favourite chatting application.

    在您最喜欢的聊天应用程序上立即获取新消息。
  3. Food order info update to the kitchen of your favourite restaurant.

    食物订单信息将更新到您最喜欢的餐厅的厨房。

These all are the common scenarios of our day to day lives where we can’t tolerate a delay in the updating of information and hence need real time communication.

这些都是我们日常生活中的常见情况,在这些情况下我们不能容忍信息更新的延迟,因此需要实时通信。

Technologies which can be used for realtime communication are:

其可以被用于实时通信的 技术是:

  1. Short Polling: AJAX, creates heavy traffic.

    短轮询 :AJAX,会造成大量流量。

  2. Long Polling: Like AJAX, but the server holds on the response until it has an update. After receiving it, the client sends another request, and needs additional header to be traversed back and forth causing additional overhead.

    长轮询 :与AJAX相似,但是服务器保留响应,直到更新为止。 客户端收到请求后,会发送另一个请求,并且需要来回遍历其他标头,从而导致额外的开销。

  3. Web Sockets: make it possible to open interactive communication between the client and server. One can send a request to the server and receive event driven responses without Polling the server for a reply, making web sockets a best choice for our use case.

    Web套接字 :可以打开客户端和服务器之间的交互式通信。 无需轮询服务器即可获得答复,就可以向服务器发送请求并接收事件驱动的响应,这使Web套接字成为我们用例的最佳选择

More in-depth info about the above three technologies can be read here.

有关以上三种技术的更多详细信息,请参见此处

We will be learning to create a real time application by covering the following scenario.

我们将通过涵盖以下场景来学习创建实时应用程序。

Imagine you are sitting at your favourite restaurant and have a digital menu. You place the order and the kitchen gets updated regarding your order in real time. When the kitchen is done with the order, they update it in real time too.

想象一下,您坐在自己喜欢的餐厅里,并且有一个数字菜单。 您下订单后,厨房会实时更新您的订单。 当厨房处理完订单后,他们也会实时更新。

Features in detail:

详细功能:

  1. Place Order: Interface to select the quantity and place the order for a selected food item to the kitchen.

    下订单 :用于选择数量并将所选食物的订单下达到厨房的界面。

  2. Kitchen: Interface which can be opened across multiple kitchens and updates in real time the chefs and cooks regarding the total orders created and predicted quantity of food items, giving them the flexibility to update it. Also has a functionality to download the report in the form of an excel sheet.

    厨房 :可以在多个厨房中打开的界面,可以实时更新厨师和厨师关于已创建的总订单和预计食品数量的信息,从而使他们可以灵活地进行更新。 还具有以excel表格形式下载报告的功能。

  3. Change Predicted: Interface to update the predicted quantity of food items.

    更改预测值 :用于更新预测食品数量的界面。

A live demo of this scenario can be found here.

可以在此处找到这种情况的现场演示

For better understanding, open it in different tabs/devices at the same time to see the data change in real time.

为了更好地理解,请同时在不同的选项卡/设备中打开它,以实时查看数据更改。

The source code is here. Feel free to make something innovative/useful on top of it.

源代码这里 。 随时在其上进行创新/实用。

So let’s get started.

因此,让我们开始吧。

技术栈: (Technology Stack:)

Frontend: React.js, Reactstrap, Socket.io

前端 :React.js,Reactstrap,Socket.io

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

后端 :Node.js(Express),MongoDB,Socket.io

资料夹结构: (Folder Structure:)

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

源代码说明: (Explanation of source code:)

前端: (Frontend:)
git clone https://github.com/honey93/OrderKitchen.git
cd OrderKitchen
npm install
npm start

Packages used:

使用的软件包:

  1. Reactstrap: Easy to use bootstrap4 components

    Reactstrap :易于使用的bootstrap4组件

  2. Socket.io: Socket.io is a library that enables real-time, bidirectional and event-based communication between the browser and the server.

    Socket.io:Socket.io是一个库,可在浏览器与服务器之间进行实时,双向和基于事件的通信。

  3. react-html-table-to-excel: Provides a client side generation of Excel (.xls) file from HTML table element.

    react-html-table-to-excel :从HTML表格元素提供客户端生成的Excel(.xls)文件。

  4. react-router-dom: DOM bindings for react router. It consists of many important components like BrowserRouter used when there is a server to handle dynamic request, Switch, Route, etc.

    react-router-dom :React 路由器的 DOM绑定。 它由许多重要组件组成,例如在有服务器处理动态请求,交换机,路由等时使用的BrowserRouter。

应用组件 (App Component)

Path: src/App.js

路径 :src / App.js

This component contains the main routing logic of the Frontend. This file is used in src/index.js inside the Browser Router Module. The below code demonstrates one of the approaches to keep your app modular.

该组件包含前端的主要路由逻辑。 该文件在浏览器路由器模块中的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 <Route> 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 <Route> element. */
/* The Route component expects a path prop, which is a string that describes the pathname that the route matches */
/* The <Switch> will iterate over routes and only render the first one that matches the current pathname */
class App extends Component {
  render() {
    return (
      <div className="App">
        <Header />
        <Switch>
          <Route exact path="/" component={PlaceOrder} />
          <Route path="/updatepredicted" component={UpdatePredicted} />
          <Route path="/kitchen" component={Kitchen} />
        </Switch>
      </div>
    );
  }
}
export default App;
标头组件 (Header Component)

Path: src/global/header.js

路径 :src / global / header.js

This component will be common and used across the sections like Place Order, Change Predicted, Kitchen. This approach helps avoid code duplication and keeps the application modular.

该组件是通用组件,将在下订单,预测更改,厨房等部分中使用。 这种方法有助于避免代码重复,并使应用程序保持模块化。

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: 'http://localhost:3001/'
    };
socket = socketIOClient(this.state.endpoint);
  }
render() {
    return (
      <header>
        <nav>
          <ul className="NavClass">
            <li>
              <NavLink exact to="/">
                Place Order
              </NavLink>
            </li>
            <li>
              <NavLink to="/updatepredicted">Change Predicted </NavLink>
            </li>
            <li>
              <NavLink to="/kitchen"> Kitchen </NavLink>
            </li  >
          </ul>
        </nav>
      </header>
    );
  }
}
export { Header, socket };
厨房组件 (Kitchen Component)

Path: src/main/Kitchen.js

路径 :src / main / Kitchen.js

The Kitchen Screen UI logic and html code resides in this component:

Kitchen Screen UI逻辑和html代码位于此组件中:

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 (
        <tr key={food._id}>
          <td> {food.name} </td>
          <td> {food.ordQty} </td>
          <td> {food.prodQty} </td>
          <td> {food.predQty} </td>
          <td>
            <button onClick={() => this.markDone(food._id)}>Done</button>
          </td>
        </tr>
      );
    });
  }
render() {
    return (
      <Container>
        <h2 className="h2Class">Kitchen Area</h2>
        <ReactHTMLTableToExcel
          id="test-table-xls-button"
          className="download-table-xls-button"
          table="table-to-xls"
          filename="tablexls"
          sheet="tablexls"
          buttonText="Download as XLS"
        />
<Table striped id="table-to-xls">
          <thead>
            <tr>
              <th>Name</th>
              <th>Quantity</th>
              <th>Created Till Now</th>
              <th>Predicted</th>
              <th>Status</th>
            </tr>
          </thead>
          <tbody>{this.getFoodData()}</tbody>
        </Table>
      </Container>
    );
  }
}
export default Kitchen;
下订单组件 (Place Order Component)

Path: src/main/PlaceOrder.js

路径 :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) < 0) {
      event.target.value = 0;
    }
    var new_array = this.state.food_data.map(food => {
      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 (
        <tr key={food._id}>
          <td> {food.name} </td>
          <td>
            <input
              onChange={e => this.changeQuantity(e, food._id)}
              value={food.order}
              type="number"
              placeholder="Quantity"
            />
          </td>
          <td>
            <button onClick={() => this.sendOrder(food._id)}>Order</button>
          </td>
        </tr>
      );
    });
  }
render() {
    return (
      <Container>
        <h2 className="h2Class">Order Menu</h2>
        <Table striped>
          <thead>
            <tr>
              <th>Product</th>
              <th>Quantity</th>
              <th>Order</th>
            </tr>
          </thead>
          <tbody>{this.getFoodData()}</tbody>
        </Table>
      </Container>
    );
  }
}
export default PlaceOrder;

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

代码存储库中还有一个类似于“更新预测的路径”的部分:src / main / UpdatePredicted.js与上面的部分相似。

后端 (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.

    Monk :一个很小的层,它为Node.JS中的MongoDB使用提供了简单而实质性的可用性改进。

  2. Socket.io: Socket.io is a library that enables real-time, bidirectional and event-based communication between the browser and the server.

    Socket.io:Socket.io是一个库,可在浏览器和服务器之间进行实时,双向和基于事件的通信。

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

3. ExpressNode的快速,简约的Web框架。

主要代号 (Main Code)

Path: backend-my-app/server.js

路径 :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

数据库 :MongoDB

Mlab: Database as a service for MongoDB

Mlab :数据库作为MongoDB的服务

Collection Name: FoodItems

馆藏名称 :FoodItems

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

文件格式 :在FoodItems集合中,至少需要一个具有以下格式的文件。

{
"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.

希望您了解如何使用趋势不断的MERN堆栈创建模块化实时应用。 如果您发现以下有用的拍手 ,请为项目回购加注 星标 ,并与您的朋友分享。

翻译自: https://www.freecodecamp.org/news/how-to-create-a-realtime-app-using-socket-io-react-node-mongodb-a10c4a1ab676/

react mongodb

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值