Building a real-time bidding system with Socket.io and React Native
Goin’ Once, Goin’ Twice, Sold to the lady with the red dress 💃🏻 You have probably heard that many times in movies or public auctions. We can also find some online platforms, such as eBay, where you can bid on a product and get counterbids from…
To use online bidding, We must stick to the same principles. We must give our bidder information as soon as a new bid comes.In this article, you’ll learn how to build a bidding application that allows users to add auction items and update the bid prices in real time using React Native and Socket.io.
Just a quick background about us. Novu is the first open-source notification infrastructure. We basically help to manage all the product notifications. It can be In-App (the bell icon like you have in Facebook – Websockets), Emails, SMSs and so on.I would be super happy if you could give us a star! And let me also know in the comments ❤️ https://github.com/novuhq/novu
*React Native is an open-source React framework that enables us to create native applications for both IOS and Android with JavaScript code.Basic knowledge of React Native is required to understand this tutorial.*
Socket.io is a popular JavaScript library that allows us to create real-time, bi-directional communication between web browsers and a Node.js server. It is a highly performant and reliable library designed to process a large volume of data with minimal delay. It follows the WebSocket protocol and provides better functionalities, such as fallback to HTTP long-polling or automatic reconnection, which enables us to build efficient real-time applications.
Here, you’ll learn how to connect the bidding application to a Socket.io server. In this guide, I’ll use Expo – the tool that provides an easier way of building React Native applications.
Expo saves us from the complex configurations required to create a native application with the React Native CLI, making it the easiest and fastest way to build and publish React Native apps.
Ensure you have the Expo CLI, Node.js, and Git installed on your computer. Then, create the project folder and an Expo React Native app by running the code below.
mkdir bidding-appcd bidding-appexpo init app
Expo allows us to create native applications using the Managed or Bare Workflow. We’ll use the blank Managed Workflow in this tutorial.
? Choose a template: \u203a - Use arrow-keys. Return to submit. ----- Managed workflow -----\u276f blank a minimal app as clean as an empty canvas blank (TypeScript) same as blank but with TypeScript configuration tabs (TypeScript) several example screens and tabs using react-navigation and TypeScript ----- Bare workflow ----- minimal bare and minimal, just the essentials to get you started
Install Socket.io Client API to the React Native app.
Here, I will guide you through creating the Socket.io Node.js server for real-time communication with the React Native application.
Create a server folder within the project folder.
cd bidding-appmkdir server
Navigate into the server folder and create a package.json file.
cd server & npm init -y
Install Express.js, CORS, Nodemon, and Socket.io Server API.
npm install express cors nodemon socket.io
Express.js is a fast, minimalist framework that provides several features for building web applications in Node.js. CORS is a Node.js package that allows communication between different domains.
Nodemon is a Node.js tool that automatically restarts the server after detecting file changes, and Socket.io allows us to configure a real-time connection on the server.
Create an index.js file – the entry point to the Node.js server.
touch index.js
Set up a simple Node.js server using Express.js. The code snippet below returns a JSON object when you visit the http://localhost:4000/api in your browser.
Next, add Socket.io to the project to create a real-time connection. Before the app.get() block, copy the code below.
//\ud83d\udc47\ud83c\udffb New imports.....const socketIO = require('socket.io')(http, { cors: { origin: "http://localhost:3000" }});//\ud83d\udc47\ud83c\udffb Add this before the app.get() blocksocketIO.on('connection', (socket) => { console.log(`\u26a1: ${socket.id} user just connected!`); socket.on('disconnect', () => { socket.disconnect() console.log('\ud83d\udd25: A user disconnected'); });});
From the code snippet above, the socket.io("connection") function establishes a connection with the React app, creates a unique ID for each socket, and logs the ID to the console whenever you refresh the app.
When you refresh or close the app, the socket fires the disconnect event showing that a user has disconnected from the socket.
Configure Nodemon by adding the start command to the list of scripts in the package.json file. The code snippet below starts the server using Nodemon.
//\ud83d\udc47\ud83c\udffb In server/package.json"scripts": { "test": "echo \\"Error: no test specified\\" && exit 1", "start": "nodemon index.js" },
You can now run the server with Nodemon by using the command below.
Here, we’ll create the user interface for the bidding application to enable users to sign in, put items up for auction, and bid for products. The user interface is divided into three screens – the Login, the Bid Page, and the Add Product screens.GIF
First, let’s set up React Navigation.
Create a screens folder within the app folder, add the Login, BidPage, and AddProduct components, and render a “Hello World” text within them.
The code snippet accepts the username from the user and logs it on the console.
Next, update the code and save the username using Async Storage for easy identification.
*Async Storage is a React Native package used to store string data in native applications. It is similar to the local storage on the web and can be used to store tokens and data in string format.*
Here, we’ll update the user interface to display the product list to the users and allow them to bid for any products of their choice.
Copy the code below into the BidPage.js file:
import { View, Text, SafeAreaView, Image, StyleSheet, Button,} from "react-native";import React, { useState } from "react";import Modal from "./Modal";import { Entypo } from "@expo/vector-icons";const BidPage = ({ navigation }) => { const [visible, setVisible] = useState(false); const toggleModal = () => setVisible(!visible); return ( Place Bids</Text> navigation.navigate("AddProduct")} /> </View> Tesla Model S</Text> Current Price: $40000</Text> </View> </View> </View> {visible ? : ""} </SafeAreaView> );};export default BidPage;
The code snippet above displays the auction items and a plus button that navigates users to the AddProduct page. The Place Bid button toggles a custom modal component that allows users to update the bid price of each product.GIF
Create the custom Modal component in the screens folder and copy the code below into the file.
Add these functions within the AddProduct component.
//\ud83d\udc47\ud83c\udffb Fetch the saved username from AsyncStorageconst getUsername = async () => { try { const value = await AsyncStorage.getItem("username"); if (value !== null) { setUser(value); } } catch (e) { console.error("Error while loading username!"); }};//\ud83d\udc47\ud83c\udffb Fetch the username when the screen loadsuseLayoutEffect(() => { getUsername();}, []);//\ud83d\udc47\ud83c\udffb This function runs when you click the submitconst addProduct = () => { if (name.trim() && price.trim() && url.trim()) { console.log({ name, price, url, user }); navigation.navigate("BidPage"); }};
Congratulations! 🥂 You have completed the user interface for the application. Next, let’s connect the application to the Socket.io server.
Import socket from the socket.js file into the AddProduct.js file.
import socket from "../utils/socket";
Update the addProduct function to send the product’s details to the Node.js server via Socket.io.
const addProduct = () => { //\ud83d\udc47\ud83c\udffb checks if the input fields are not empty if (name.trim() && price.trim() && url.trim()) { //\ud83d\udc47\ud83c\udffb sends the product's details to the Node.js server socket.emit("addProduct", { name, price, url, user }); navigation.navigate("BidPage"); }};
Create a listener to the addProduct event that adds the item to a product list on the server.
//\ud83d\udc47\ud83c\udffb Generates a random string as IDconst generateID = () => Math.random().toString(36).substring(2, 10);//\ud83d\udc47\ud83c\udffb Array containing all the productslet productList = [];socketIO.on("connection", (socket) => { console.log(`\u26a1: ${socket.id} user just connected!`); //\ud83d\udc47\ud83c\udffb Listens to the addProduct event and stores the new product socket.on("addProduct", (product) => { productList.unshift({ id: generateID(), name: product.name, price: product.price, image_url: product.url, owner: product.user, }); //\ud83d\udc47\ud83c\udffb sends a new event containing all the products socket.emit("getProducts", productList); }); socket.on("disconnect", () => { socket.disconnect(); console.log("\ud83d\udd25: A user disconnected"); });});
The getProducts event is triggered only when you add a new item to the list. Next, let’s fetch the products from the server when we navigate to the BidPage.
Update the index.js on the server to send the products list via an API route as below.
From the code snippet above, ProductUI is a dummy component that displays the layout for each product on the page. Create the component and copy the code below:
import { View, Text, Image, Button } from "react-native";import React from "react";import { styles } from "../utils/styles";const ProductUI = ({ toggleModal, name, image_url, price, id }) => { return ( {name}</Text> Current Price: ${price}</Text> </View> </View> </View> );};export default ProductUI;
const Modal = ({ setVisible, selectedProduct }) => { //\ud83d\udc47\ud83c\udffb sets the default price of the input field // to the price of the selected product const [newPrice, setNewPrice] = useState(selectedProduct.price); const [user, setUser] = useState(""); //\ud83d\udc47\ud83c\udffb Runs when you press Place Bid function const updateBidFunction = () => { //\ud83d\udc47\ud83c\udffb checks if the new price is more than the previous price if (Number(newPrice) > Number(selectedProduct.price)) { //\ud83d\udc47\ud83c\udffb sends an event containing the product's previous and current details socket.emit("updatePrice", { newPrice, user, selectedProduct }); setVisible(false); } else { Alert.alert("Error!", "New price must be more than the bidding price"); } }; //..other functions (getUsername & useLayoutEffect) return ( Update Bid</Text> Name: {selectedProduct.name}</Text> Price</Text> setNewPrice(value)} /> PLACE BID </Text> </View> </Pressable> </View> </View> );};
Create a listener to the updatePrice event on the server and update the item’s price.
socket.on("updatePrice", (data) => { //\ud83d\udc47\ud83c\udffb filters the product's list by ID let result = productList.filter( (product) => product.id === data.selectedProduct.id ); //\ud83d\udc47\ud83c\udffb updates the product's price and owner // with the new price and proposed owner result[0].price = data.newPrice; result[0].owner = data.user; //\ud83d\udc47\ud83c\udffb emits the getProducts event to update the product list on the UI socket.emit("getProducts", productList);});
Congratulations! 🥂 You’ve completed the project for this article.
So far, you’ve learnt how to set up Socket.io in a React Native and Node.js application, save data with Async Storage, and communicate between a server and the Expo app via Socket.io.
This project is a demo of what you can build using React Native and Socket.io. Feel free to improve the project by:
using an authentication library
sending push notifications via the expo notification package when the bid price for a product changes
adding a database that supports real-time storage.
If you feel like this article helped you understand WebSockets and React Native better! I would be super happy if you could give us a star! And let me also know in the comments ❤️ https://github.com/novuhq/novu GIF