DEV Community

Cover image for Building a real-time bidding system with Socket.io and React Native đŸ€©
Nevo David Subscriber for novu

Posted on • Edited on • Originally published at novu.co

Building a real-time bidding system with Socket.io and React Native đŸ€©

What is this article about?

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 other bidders.
Today we are going to build a mobile app with React Native and Socket.io - eBay style!

Bidding

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.

Novu - the first open-source notification architecture

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

Image description

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

What is Socket.io?

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.

How to connect React Native to a Socket.io server

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.

Installing Expo

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-app
cd bidding-app
expo init app
Enter fullscreen mode Exit fullscreen mode

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: â€ș - Use arrow-keys. Return to submit.
    ----- Managed workflow -----
❯   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
Enter fullscreen mode Exit fullscreen mode

Install Socket.io Client API to the React Native app.

cd app
expo install socket.io-client
Enter fullscreen mode Exit fullscreen mode

Create a socket.js within a utils folder.

mkdir utils
touch socket.js
Enter fullscreen mode Exit fullscreen mode

Then copy the code below into the socket.js file.

import { io } from "socket.io-client";
const socket = io.connect("http://localhost:4000");
export default socket;
Enter fullscreen mode Exit fullscreen mode

The code snippet above creates a real-time connection to the server hosted at that URL. (We'll create the server in the upcoming section).

Create a styles.js file within the utils folder and copy the code below into the file. It contains all the styling for the chat application.

import { StyleSheet } from "react-native";

export const styles = StyleSheet.create({
    addProductContainer: {
        flex: 1,
    },
    productForm: {
        width: "100%",
        padding: 10,
    },
    formInput: {
        borderWidth: 1,
        padding: 15,
        marginTop: 5,
        marginBottom: 10,
    },
    addProductBtn: {
        backgroundColor: "green",
        padding: 15,
    },
    headerContainer: {
        padding: 15,
        flexDirection: "row",
        justifyContent: "space-between",
    },

    mainContainer: {
        flex: 1,
        padding: 20,
    },
    loginContainer: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center",
    },
    heading: {
        fontSize: 25,
        fontWeight: "bold",
        marginBottom: 20,
    },
    formContainer: {
        width: "100%",
        padding: 15,
    },
    input: {
        borderWidth: 1,
        paddingHorizontal: 10,
        paddingVertical: 15,
        marginBottom: 15,
        borderRadius: 3,
    },
    formLabel: {
        marginBottom: 3,
    },
    loginbutton: {
        backgroundColor: "green",
        width: 150,
        padding: 15,
        alignItems: "center",
        borderRadius: 5,
    },
    loginbuttonText: {
        color: "#fff",
    },
    modalContainer: {
        width: "100%",
        backgroundColor: "#FAF7F0",
        position: "fixed",
        bottom: 0,
        height: 400,
        padding: 20,
        flex: 1,
    },
    modalHeader: {
        fontSize: 24,
        fontWeight: "bold",
        textAlign: "center",
        marginBottom: 20,
    },
    modalPrice: {
        width: "100%",
        borderWidth: 1,
        padding: 12,
    },
    updateBidBtn: {
        width: 200,
        padding: 15,
        backgroundColor: "green",
        marginTop: 15,
        borderRadius: 3,
    },
    bidContainer: {
        flex: 1,
        backgroundColor: "#fff",
    },
    header: {
        fontSize: 24,
        fontWeight: "bold",
    },
    mainContainer: {
        flex: 1,
        padding: 20,
    },
    productContainer: {
        borderWidth: 1,
        borderColor: "#B2B2B2",
        padding: 20,
        height: 280,
        backgroundColor: "#fff",
        marginBottom: 10,
    },
    image: {
        width: "100%",
        height: "70%",
    },
    productDetails: {
        width: "100%",
        height: "30%",
        padding: 10,
        alignItems: "center",
    },
    productName: {
        fontSize: 16,
        fontWeight: "bold",
    },
});
Enter fullscreen mode Exit fullscreen mode

Install React Navigation and its dependencies. React Navigation allows us to navigate from one screen to another within a React Native application.

npm install @react-navigation/native
npx expo install react-native-screens react-native-safe-area-context
Enter fullscreen mode Exit fullscreen mode

Setting up the Node.js server

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-app
mkdir server
Enter fullscreen mode Exit fullscreen mode

Navigate into the server folder and create a package.json file.

cd server & npm init -y
Enter fullscreen mode Exit fullscreen mode

Install Express.js, CORS, Nodemon, and Socket.io Server API.

npm install express cors nodemon socket.io
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

//đŸ‘‡đŸ» index.js
const express = require("express");
const app = express();
const PORT = 4000;

app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.get("/api", (req, res) => {
    res.json({
        message: "Hello world",
    });
});

app.listen(PORT, () => {
    console.log(`Server listening on ${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

Next, add Socket.io to the project to create a real-time connection. Before the app.get() block, copy the code below.

//đŸ‘‡đŸ» New imports
.....
const socketIO = require('socket.io')(http, {
    cors: {
        origin: "http://localhost:3000"
    }
});

//đŸ‘‡đŸ» Add this before the app.get() block
socketIO.on('connection', (socket) => {
    console.log(`⚡: ${socket.id} user just connected!`);

    socket.on('disconnect', () => {
      socket.disconnect()
      console.log('đŸ”„: A user disconnected');
    });
});
Enter fullscreen mode Exit fullscreen mode

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.

//đŸ‘‡đŸ» In server/package.json

"scripts": {
    "test": "echo \\"Error: no test specified\\" && exit 1",
    "start": "nodemon index.js"
  },
Enter fullscreen mode Exit fullscreen mode

You can now run the server with Nodemon by using the command below.

npm start
Enter fullscreen mode Exit fullscreen mode

Building the app user interface

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.

firststep

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.

mkdir screens
cd screens
touch Login.js BidPage.js AddProduct.js
Enter fullscreen mode Exit fullscreen mode

Copy the code below into the App.js file within the app folder.

import React from "react";

//đŸ‘‡đŸ» app screens
import Login from "./screens/Login";
import BidPage from "./screens/BidPage";
import AddProduct from "./screens/AddProduct";

//đŸ‘‡đŸ» React Navigation configurations
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";

const Stack = createNativeStackNavigator();

export default function App() {
    return (
        <NavigationContainer>
            <Stack.Navigator>
                <Stack.Screen
                    name='Login'
                    component={Login}
                    options={{ headerShown: false }}
                />

                <Stack.Screen
                    name='BidPage'
                    component={BidPage}
                    options={{
                        headerShown: false,
                    }}
                />

                <Stack.Screen
                    name='AddProduct'
                    component={AddProduct}
                    options={{
                        title: "Add Product",
                    }}
                />
            </Stack.Navigator>
        </NavigationContainer>
    );
}
Enter fullscreen mode Exit fullscreen mode

The Login screen

Copy the code below into the Login.js file.

import React, { useState } from "react";
import {
    View,
    Text,
    SafeAreaView,
    TextInput,
    Pressable,
    Alert,
} from "react-native";
import { styles } from "../utils/styles";

const Login = ({ navigation }) => {
    const [username, setUsername] = useState("");

    const handleLogin = () => {
        if (username.trim()) {
            console.log({ username });
        } else {
            Alert.alert("Username is required.");
        }
    };
    return (
        <SafeAreaView style={styles.loginContainer}>
            <Text style={styles.heading}>Login</Text>
            <View style={styles.formContainer}>
                <Text style={styles.formLabel}>Username</Text>
                <TextInput
                    placeholder='Enter your name'
                    style={styles.input}
                    autoCorrect={false}
                    onChangeText={(value) => setUsername(value)}
                />

                <Pressable style={styles.loginbutton} onPress={handleLogin}>
                    <View>
                        <Text style={styles.loginbuttonText}>Get Started</Text>
                    </View>
                </Pressable>
            </View>
        </SafeAreaView>
    );
};

export default Login;
Enter fullscreen mode Exit fullscreen mode

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

Run the code below to install Async Storage.

expo install @react-native-async-storage/async-storage
Enter fullscreen mode Exit fullscreen mode

Update the handleLogin function to save the username via AsyncStorage.

import AsyncStorage from "@react-native-async-storage/async-storage";

const storeUsername = async () => {
    try {
        await AsyncStorage.setItem("username", username);
        navigation.navigate("BidPage");
    } catch (e) {
        Alert.alert("Error! While saving username");
    }
};

const handleLogin = () => {
    if (username.trim()) {
        //đŸ‘‡đŸ» calls AsyncStorage function
        storeUsername();
    } else {
        Alert.alert("Username is required.");
    }
};
Enter fullscreen mode Exit fullscreen mode

The BidPage screen

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.

BigPage

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 (
        <SafeAreaView style={styles.bidContainer}>
            <View style={styles.headerContainer}>
                <Text style={styles.header}>Place Bids</Text>
                <Entypo
                    name='circle-with-plus'
                    size={30}
                    color='green'
                    onPress={() => navigation.navigate("AddProduct")}
                />
            </View>

            <View style={styles.mainContainer}>
                <View style={styles.productContainer}>
                    <Image
                        style={styles.image}
                        resizeMode='contain'
                        source={{
                            uri: "https://stimg.cardekho.com/images/carexteriorimages/930x620/Tesla/Model-S/5252/1611840999494/front-left-side-47.jpg?tr=w-375",
                        }}
                    />
                    <View style={styles.productDetails}>
                        <Text style={styles.productName}>Tesla Model S</Text>
                        <View>
                            <Text style={styles.productPrice}>Current Price: $40000</Text>
                        </View>

                        <Button title='Place Bid' onPress={toggleModal} />
                    </View>
                </View>

            {visible ? <Modal visible={visible} setVisible={setVisible} /> : ""}
        </SafeAreaView>
    );
};

export default BidPage;
Enter fullscreen mode Exit fullscreen mode

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.

AddProduct

Create the custom Modal component in the screens folder and copy the code below into the file.

//đŸ‘‡đŸ» Within screens/Modal.js
import { View, Text, StyleSheet, TextInput, Pressable } from "react-native";
import React, { useState } from "react";

const Modal = ({ visible, setVisible }) => {
    const [newPrice, setNewPrice] = useState(0);

    return (
        <View style={styles.modalContainer}>
            <Text style={styles.modalHeader}>Update Bid</Text>
            <Text style={{ marginBottom: 10 }}>Price</Text>
            <TextInput
                keyboardType='number-pad'
                style={styles.modalPrice}
                onChangeText={(value) => setNewPrice(value)}
            />
            <View style={{ width: "100%", alignItems: "center" }}>
                <Pressable
                    style={styles.updateBidBtn}
                    onPress={() => {
                        console.log({ newPrice });
                        setVisible(!visible);
                    }}
                >
                    <View>
                        <Text style={{ color: "#fff", fontSize: 16, textAlign: "center" }}>
                            PLACE BID
                        </Text>
                    </View>
                </Pressable>
            </View>
        </View>
    );
};

export default Modal;
Enter fullscreen mode Exit fullscreen mode

The code snippet above displays an input field for the new bid price, and a submit button that logs the new price to the console.

The Add Product screen

Copy the code below into the AddProduct.js file.

import { View, Text, SafeAreaView, TextInput, Pressable } from "react-native";
import React, { useState, useLayoutEffect } from "react";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { styles } from "../utils/styles";

const AddProduct = ({ navigation }) => {
    const [name, setName] = useState("");
    const [price, setPrice] = useState(0);
    const [url, setURL] = useState("");
    const [user, setUser] = useState("");

    return (
        <SafeAreaView style={styles.addProductContainer}>
            <View style={styles.productForm}>
                <Text>Product Name</Text>
                <TextInput
                    style={styles.formInput}
                    onChangeText={(value) => setName(value)}
                />

                <Text>Product Price</Text>
                <TextInput
                    style={styles.formInput}
                    onChangeText={(value) => setPrice(value)}
                />

                <Text>Product Image URL</Text>
                <TextInput
                    style={styles.formInput}
                    keyboardType='url'
                    onChangeText={(value) => setURL(value)}
                    autoCapitalize='none'
                    autoCorrect={false}
                />

                <Pressable style={styles.addProductBtn} onPress={addProduct}>
                    <View>
                        <Text style={{ color: "#fff", fontSize: 16, textAlign: "center" }}>
                            ADD PRODUCT
                        </Text>
                    </View>
                </Pressable>
            </View>
        </SafeAreaView>
    );
};

export default AddProduct;
Enter fullscreen mode Exit fullscreen mode

Add these functions within the AddProduct component.

//đŸ‘‡đŸ» Fetch the saved username from AsyncStorage
const getUsername = async () => {
    try {
        const value = await AsyncStorage.getItem("username");
        if (value !== null) {
            setUser(value);
        }
    } catch (e) {
        console.error("Error while loading username!");
    }
};

//đŸ‘‡đŸ» Fetch the username when the screen loads
useLayoutEffect(() => {
    getUsername();
}, []);

//đŸ‘‡đŸ» This function runs when you click the submit
const addProduct = () => {
    if (name.trim() && price.trim() && url.trim()) {
        console.log({ name, price, url, user });
        navigation.navigate("BidPage");
    }
};
Enter fullscreen mode Exit fullscreen mode

Congratulations! đŸ„‚ You have completed the user interface for the application. Next, let's connect the application to the Socket.io server.

Sending data between React Native and a Socket.io server

In this section, you'll learn how to send data between the React Native application and the Socket.io server.

How to add auction items

Import socket from the socket.js file into the AddProduct.js file.

import socket from "../utils/socket";
Enter fullscreen mode Exit fullscreen mode

Update the addProduct function to send the product's details to the Node.js server via Socket.io.

const addProduct = () => {
    //đŸ‘‡đŸ» checks if the input fields are not empty
    if (name.trim() && price.trim() && url.trim()) {
        //đŸ‘‡đŸ» sends the product's details to the Node.js server
        socket.emit("addProduct", { name, price, url, user });
        navigation.navigate("BidPage");
    }
};
Enter fullscreen mode Exit fullscreen mode

Create a listener to the addProduct event that adds the item to a product list on the server.

//đŸ‘‡đŸ» Generates a random string as ID
const generateID = () => Math.random().toString(36).substring(2, 10);

//đŸ‘‡đŸ» Array containing all the products
let productList = [];

socketIO.on("connection", (socket) => {
    console.log(`⚡: ${socket.id} user just connected!`);

    //đŸ‘‡đŸ» 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,
        });
        //đŸ‘‡đŸ» sends a new event containing all the products
        socket.emit("getProducts", productList);
    });

    socket.on("disconnect", () => {
        socket.disconnect();
        console.log("đŸ”„: A user disconnected");
    });
});
Enter fullscreen mode Exit fullscreen mode

Displaying the auction items

Import socket from the socket.js file into the BidPage.js file.

import socket from "../utils/socket";
Enter fullscreen mode Exit fullscreen mode

Update the BidPage.js to listen to the getProducts event and display the products to the user.

const [products, setProducts] = useState([]);

useEffect(() => {
    socket.on("getProducts", (data) => setProducts(data));
}, [socket]);
Enter fullscreen mode Exit fullscreen mode

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.

app.get("/products", (req, res) => {
    res.json(productList);
});
Enter fullscreen mode Exit fullscreen mode

Update the BidPage component to fetch the products list from the server when you load the component.

useLayoutEffect(() => {
    function fetchProducts() {
        fetch("http://localhost:4000/products")
            .then((res) => res.json())
            .then((data) => setProducts(data))
            .catch((err) => console.error(err));
    }
    fetchProducts();
}, []);
Enter fullscreen mode Exit fullscreen mode

Render the products on the page via a FlatList.

return (
    <SafeAreaView style={styles.bidContainer}>
        <View style={styles.headerContainer}>
            <Text style={styles.header}>Place Bids</Text>
            <Entypo
                name='circle-with-plus'
                size={30}
                color='green'
                onPress={() => navigation.navigate("AddProduct")}
            />
        </View>
        <View style={styles.mainContainer}>
            <FlatList
                data={products}
                key={(item) => item.id}
                renderItem={({ item }) => (
                    <ProductUI
                        name={item.name}
                        image_url={item.image_url}
                        price={item.price}
                        toggleModal={toggleModal}
                        id={item.id}
                    />
                )}
            />
        </View>
        {visible ? <Modal visible={visible} setVisible={setVisible} /> : ""}
    </SafeAreaView>
);
Enter fullscreen mode Exit fullscreen mode

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 (
        <View style={styles.productContainer}>
            <Image
                style={styles.image}
                resizeMode='contain'
                source={{
                    uri: image_url,
                }}
            />
            <View style={styles.productDetails}>
                <Text style={styles.productName}>{name}</Text>
                <View>
                    <Text style={styles.productPrice}>Current Price: ${price}</Text>
                </View>

                <Button title='Place Bid' onPress={toggleModal} />
            </View>
        </View>
    );
};

export default ProductUI;
Enter fullscreen mode Exit fullscreen mode

Updating the bid prices

Here, you'll learn how to update the bid price for each product when you press the Place Bid button.

PlaceBid

Within the BidPage.js file, create a state that holds the selected item a user wants to bid.

const [selectedProduct, setSelectedProduct] = useState({});
Enter fullscreen mode Exit fullscreen mode

Update the toggleModal function to accept the item's details

const toggleModal = (name, price, id) => {
    setVisible(true);
    setSelectedProduct({ name, price, id });
};
Enter fullscreen mode Exit fullscreen mode

Pass the product's details into the toggleModal function and update the selectedProduct state.

const ProductUI = ({ toggleModal, name, image_url, price, id }) => {
    return (
        <View style={styles.productContainer}>
            <Image
                style={styles.image}
                resizeMode='contain'
                source={{
                    uri: image_url,
                }}
            />
            <View style={styles.productDetails}>
                <Text style={styles.productName}>{name}</Text>
                <View>
                    <Text style={styles.productPrice}>Current Price: ${price}</Text>
                </View>
                {/*đŸ‘‡đŸ» The toggleModal function accepts the product's details */}
                <Button
                    title='Place Bid'
                    onPress={() => toggleModal(name, price, id)}
                />
            </View>
        </View>
    );
};
Enter fullscreen mode Exit fullscreen mode

Ensure the selectedProduct state is passed into the Modal component within the BidPage.js file.

const BidPage = () => {
    return (
        <SafeAreaView>
            {/* other JSX elements...*/}
            {visible ? (
                <Modal
                    visible={visible}
                    setVisible={setVisible}
                    selectedProduct={selectedProduct}
                />
            ) : (
                ""
            )}
        </SafeAreaView>
    );
};
Enter fullscreen mode Exit fullscreen mode

Update the Modal.js file as below:

const Modal = ({ setVisible, selectedProduct }) => {
    //đŸ‘‡đŸ» 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("");

    //đŸ‘‡đŸ» Runs when you press Place Bid function
    const updateBidFunction = () => {
        //đŸ‘‡đŸ» checks if the new price is more than the previous price
        if (Number(newPrice) > Number(selectedProduct.price)) {
            //đŸ‘‡đŸ» 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 (
        <View style={styles.modalContainer}>
            <Text style={styles.modalHeader}>Update Bid</Text>
            <Text style={{ marginBottom: 10 }}>Name: {selectedProduct.name}</Text>
            <Text style={{ marginBottom: 10 }}>Price</Text>
            <TextInput
                keyboardType='number-pad'
                style={styles.modalPrice}
                defaultValue={selectedProduct.price} // đŸ‘‰đŸ» the product's default value
                onChangeText={(value) => setNewPrice(value)}
            />
            <View style={{ width: "100%", alignItems: "center" }}>
                <Pressable style={styles.updateBidBtn} onPress={updateBidFunction}>
                    <View>
                        <Text style={{ color: "#fff", fontSize: 16, textAlign: "center" }}>
                            PLACE BID
                        </Text>
                    </View>
                </Pressable>
            </View>
        </View>
    );
};
Enter fullscreen mode Exit fullscreen mode

Create a listener to the updatePrice event on the server and update the item’s price.

socket.on("updatePrice", (data) => {
    //đŸ‘‡đŸ» filters the product's list by ID
    let result = productList.filter(
        (product) => product.id === data.selectedProduct.id
    );
    //đŸ‘‡đŸ» updates the product's price and owner 
  //   with the new price and proposed owner
    result[0].price = data.newPrice;
    result[0].owner = data.user;

    //đŸ‘‡đŸ» emits the getProducts event to update the product list on the UI
    socket.emit("getProducts", productList);
});
Enter fullscreen mode Exit fullscreen mode

Congratulations! đŸ„‚ You've completed the project for this article.

Conclusion

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.

The source code for this application is available here: https://github.com/novuhq/blog/tree/main/bidding-app-with-reactnative

Thank you for reading!

Help me out!

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
Image description

Top comments (13)

Collapse
 
nevodavid profile image
Nevo David

Image description

Collapse
 
nevodavid profile image
Nevo David

How are you today?

Collapse
 
nevodavid profile image
Nevo David

Have you worked with React Native before?

Collapse
 
jcmaciel profile image
José Carlos Maciel

Hi, I got "Network request failed", can you help me?

Collapse
 
user1111333 profile image
Sacred (void*)

Some browsers/platforms reject non-secure websocket(ws://) connections/handshakes.
Fix is easy tho, as adopting https, and the same story for WS, using wss://...

Collapse
 
jcmaciel profile image
José Carlos Maciel

Thanks!!

Collapse
 
r4e profile image
Richie Permana

How would you handke the concurrent event when users is bidding at the exact same time and price? 😆

Collapse
 
imsuvesh profile image
Suvesh K

On server check the price before updating, incoming bid must be greater that current price.
If both users bid at same time who's request comes first will update the price and second will see error to increase bid.

Collapse
 
r4e profile image
Richie Permana

Thanks for the reply. Really love the discussion!

But anyway, I get your point but actually....it's not that simple. Well, your point technically, yes, but actually no. Sometimes, the implementation is much harder than you think.

For me, my approach is using Redis to solve this problem instead of DB transactions.

Collapse
 
asubodh profile image
Subodh Aryal

🚀

Collapse
 
nevodavid profile image
Nevo David

đŸ€©

Collapse
 
wilmela profile image
Wilmela

Nice

Collapse
 
nevodavid profile image
Nevo David

Thank you Wilmela! đŸ€©