2

I'm working on a web app to track fruits vendor stock using React.js, MongoDB, Node.js, and Express. I called my database endpoints to render the data in the table. Now I try to increment and decrement the stock amount using a button, but when I try to set the new state, it doesn't work. I try to change the state by clicking, then update the new state in the database. Any advice?

  • FruitTable component:

    import Reac, { Component } from 'react'; import * as ReactBootstrap from 'react-bootstrap'; import axios from 'axios'; import Button from 'react-bootstrap/Button';

    class FruitTable extends Component {

    constructor(props) {
      super(props)
    
      this.state = {
        fruits: []
      }
      this.handleClick = this.handleClick.bind(this)
    }
    
    
    componentDidMount() {
      axios.get('http://localhost:5000/fruits/')
      .then(res => {
        this.setState({
          fruits: res.data
        });
       })
      .catch((error) => {
        console.log(error)
      })
    
    
    
    }
    
    handleClick =  () => {
      const fruits = [...this.state.fruits]
      this.setState({fruits: this.state.fruits[1] +1})
    
      }
    
    
    render() {
    
      return(
        <div>
         <h1>Fruit Vendor Stock</h1>
        <ReactBootstrap.Table striped bordered hover size="sm">
      <thead>
        <tr>
          <th>#</th>
          <th>Name</th>
          <th>Stock</th>
          <th>Price</th>
          <th>Add/Reomve</th>
        </tr>
      </thead>
      <tbody> 
        {
          this.state.fruits.map((fruit, index) => (
            <tr>
            <th scope="row">{index + 1}</th>
            <td>{fruit.name}</td>
            <td>{fruit.stock}</td>
            <td>{fruit.price}</td>
            <td>
              <div className="mb2">
              <Button className="btn btn-primary btn-sm"
                onClick={this.handleClick}
              >Add</Button>{' '}
              <Button className="btn btn-danger btn-sm"
    
              >Remove</Button> 
              </div>
            </td>
            </tr>
            ))
        }
      </tbody>
       </ReactBootstrap.Table>
      </div>
      )
    };
    

    }

    export default FruitTable;

  • FruitsData.model

    const mongoose = require('mongoose');

    const Schema = mongoose.Schema;

    const fruits = new Schema({ name: { type: String, required: true }, stock: { type: Number, required: true }, price: { type: Number, required: true }, }, {

    });

    const Fruits = mongoose.model('Fruits', fruits);

    module.exports = Fruits;

-Routes:

const router = require('express').Router();
let Fruit = require('../models/fruit_data');

router.route('/').get((req, res) => {
    Fruit.find()
    .then(fruits => res.json(fruits))
    .catch(err => res.status(400).json('Error: ' + err));
});

router.route('/add').post((req, res) => {
    const name = req.body.name;
    const stock = Number(req.body.stock);
    const price = Number(req.body.price);

    const newFruit = new Fruit({
        name,
        stock,
        price,
    });

    newFruit.save()
    .then(() => res.json('Fruit has been added!'))
    .catch(err => res.status(400).json('Error: ' + err));
});

router.route('/:id').get((req, res) => {
    Fruit.findById(req.params.id)
    .then(Fruit => res.json(Fruit))
    .catch(err => res.status(400).json('Error: ' + err));
});

router.route('/:id').delete((req, res) => {
    Fruit.findByIdAndDelete(req.params.id)
    .then(() => res.json('Fruit has deleted.'))
    .catch(err => res.status(400).json("Error: " + err));
});

router.route('/update/:id').put((req, res, next) => {
    Fruit.findByIdAndUpdate(req.params.id, {
        $set: req.body
    }, (error, data) => {
        if(error) {
            return next(error);
            console.log(error)
        } else {
            res.json(data)
            console.log('Stock has been updated')
        }
    })   
    })

module.exports = router;

-Server.js:

const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');

require('dotenv').config();

const app = express();
const port = process.env.PORT || 5000;

app.use(cors());
app.use(express.json());


const uri = process.env.ATLAS_URI;
mongoose.connect(uri, { useNewUrlParser: true });
const connection = mongoose.connection;
connection.once('open', () => {
console.log("MongoDb database connection established successfully!!")

})

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

const fruitsRouter = require('./routes/fruit');

app.use('/fruits', fruitsRouter);




app.listen(port, () => {
console.log(`Server is running on port: ${port}`);

})

-App.js:

import "./App.css";
import React, { Component, useState, useEffect } from "react";
import FruitTable from "./Components/fruitTable";
import "bootstrap/dist/css/bootstrap.min.css";
import { sendEmail } from "./service/emailService";
import axios from 'axios';

function App() {


  return (
    <div className="App">
     <FruitTable  />
    </div>
   );
  }

export default App;

3
  • #1 What means "doesn't work"? Do you have any error? #2 If you don't have any error on browser console side or express server side, add several logs like console.log("before foo") ,console.log("after click") , console.log("on stock increment service"), etc to find the exact line of error. #3 Does your stock increment/decrement service works? Try it with some client like postman, insomnia, curl, etc Commented Nov 21, 2021 at 5:39
  • I rendered the table, but I try to use the "Add" and "Remove" buttons to increment and decrement the values of the stock, so I create handleClick function to set a new state to the stock, but when I click the button, the app crashes. Commented Nov 21, 2021 at 5:43
  • mmmm so it is a react mistake. If your problem in in the handleClick function, and your express endpoint works: Update your question detail deleting everything and just leave the react handleClick function. Also add a log like console.log("im the handleclick") and fix your code until this log is showed on your browser console. Just after that, point it to your express endpoint. #3 Also if you can to reproduce your error mistake in a new react proyect like github.com/jrichardsz/create-react-app-redux we will able to help you Commented Nov 21, 2021 at 5:54

1 Answer 1

0

The state update in handleClick isn't correct, it is taking the 1th element value, adding 1 to it, and setting this as the this.state.fruits state value. This breaks the state invariant of fruits being an array. This is likely the "crash" you are seeing as a non-array value is attempted to be mapped.

handleClick =  () => {
  const fruits = [...this.state.fruits]
  this.setState({
    fruits: this.state.fruits[1] +1 // <-- mutates state invariant!!
  })
}

The code also isn't passing a reference to which index or fruit it is trying to update.

According to your schema the fruit items have 3 properties, name, stock, and price. For this solution I'll assume that name is sufficiently unique for identification purposes and that you are incrementing/decrementing the stock quantity.

handleClick = (name, addQuantity = 1) => () => {
  this.setState(prevState => ({
    fruits: prevState.fruits.map(fruit => 
      fruit.name === name
      ? { ...fruit, stock: fruit.stock + addQuantity}
      : fruit
    )
  }));
}

Now you need to pass the currently mapped fruit item's name property to the handler when mapping.

{this.state.fruits.map((fruit, index) => (
  <tr key={fruit.name}>
    <th scope="row">{index + 1}</th>
    <td>{fruit.name}</td>
    <td>{fruit.stock}</td>
    <td>{fruit.price}</td>
    <td>
      <div className="mb2">
        <Button
          className="btn btn-primary btn-sm"
          onClick={this.handleClick(item.name, 1)} // <-- add one
        >
          Add
        </Button>{' '}
        <Button
          className="btn btn-danger btn-sm"
          onClick={this.handleClick(item.name, -1)} // remove one
        >
          Remove
        </Button> 
      </div>
    </td>
  </tr>
))}
Sign up to request clarification or add additional context in comments.

1 Comment

@AhmedAbdu Has this addressed/resolved your question/issue?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.