0

I have this React application that serves as an inventory system. I have this page where there exists a plus and minus button that allows the user to increase or decrease the stock number for an individual item (it's pretty much a counter). So what's happening is that at first decreasing/increasing the counters for individual products were working but as my number of products, it just stopped working. When I tried to increase/decrease product, it would just jump to the top of the page. After analysis, I found that I could increase/decrease stock numbers for products that were already on the page when first accessed (aka I don't need to scroll down to view them). The ones that were not working were the ones that required some scrolling down to see them.

Here is the component (IncreaseStockItem) that renders the product name along with the plus and minus button

import React, { Component } from "react";

class IncreaseStockItem extends Component {
  constructor(props) {
    super(props);
    this.state = {
      inStock: props.product.inStock,
      name: props.product.name,
      barcode: props.product.barcode,
    };
  }

  onProductIncrease = (e) => {
    e.preventDefault();
    var updatedNumber = this.state.inStock + 1;
    this.setState({ inStock: updatedNumber }, function () {
      this.handleAfterChange();
    });
  };

  onProductDecrease = (e) => {
    e.preventDefault();
    var updatedNumber = this.state.inStock - 1;
    this.setState({ inStock: updatedNumber }, function () {
      this.handleAfterChange();
    });
  };

  handleAfterChange = () => {
    this.props.onInputChange(this.state);
  };

  
  render() {
    return (
      <div className="columns">
        <div className="column is-two-thirds ml-4">
          <div className="field">
            <input className="input" value={this.state.name} readOnly></input>
          </div>
        </div>
        <div className="column">
          <span className="field">
            <div onClick={this.onProductDecrease} className="button is-danger">
              <i className="fa fa-minus"></i>
            </div>

            <input
              value={this.state.inStock}
              className="input mx-5 has-text-centered"
              type="text"
              style={{ width: "45px" }}
              readOnly
            ></input>
          </span>
          <div onClick={this.onProductIncrease} className="button is-success">
            <i className="fa fa-plus"></i>
          </div>
        </div>
      </div>
    );
  }
}

export default IncreaseStockItem;

Here is the component that renders the list of items:

import React from "react";
import IncreaseStockItem from "./IncreaseStockItem";

const IncreaseStockProductList = ({ products, onInputChange }) => {
  const renderedList = products.map((product) => {
    return (
      <IncreaseStockItem
        key={product._id}
        product={product}
        onInputChange = {onInputChange}
      />
    );
  });

  return renderedList;
};

export default IncreaseStockProductList;

The component looks like this: Component

I have tried my best to describe the problem and it still might be unclear. Let me know if you need more clarifications.

Note: The same problem happens when I use react-router Link with a link tag that's out of context.

EDIT 1:

Whenever I zoom out so that all components are within the context of the screen (aka no need of scrolling down), everything works fine.

EDIT 2:

The component hierarchy goes like this:

IncreaseStock -> IncreaseStockProductList -> IncreaseStockItem

Code for IncreaseStock below:

import React, { Component } from "react";
import axios from "axios";
import { withRouter } from "react-router-dom";
import Button from "./Button";
import IncreaseStockProductList from "./IncreaseStockProductList";
import Message from "./Message";
import SearchBar from "./SearchBar";

class IncreaseStock extends Component {
  constructor(props) {
    super(props);
    this.state = { products: [], updatedProducts: [] };
  }

  routingFunction = () => {
    var title = "Mises à jour du nombre de produits";
    var message =
      "Le nombre d'articles des produits suivants a été mis à jour:";

    this.props.history.push({
      pathname: "/products/",
      state: {
        title: title,
        message: message,
        products: this.state.updatedProducts,
      },
    });
  };

  onSearchBarChange = async (target) => {
    var barcode = target.value;
    axios.get("/api/products/barcode/" + barcode).then((response) => {
      if (response.data !== null) {
        this.props.history.push({
          pathname: "/products/" + barcode,
        });
      }
    });
  };

  componentDidMount() {
    axios.get("/api/products").then((response) => {
      this.setState({ products: response.data });
    });
  }

  // https://stackoverflow.com/questions/37435334/correct-way-to-push-into-state-array
  onInputChange = (data) => {
    var tempData = {
      inStock: data.inStock,
      barcode: data.barcode,
      name: data.name,
    };
    var tempArray = this.state.updatedProducts.slice();
    tempArray = tempArray.filter(function (obj) {
      return obj.barcode !== data.barcode;
    });
    tempArray.push(tempData);
    this.setState({ updatedProducts: tempArray });
  };

  onFormSubmit = (event) => {
    event.preventDefault();
    var payload = this.state.updatedProducts;
    axios({
      method: "post",
      url: "/api/products/increase",
      data: payload,
    }).then((response) => {
      this.routingFunction();
    });
  };

  render() {
    return (
      <div className="section">
        <div className="container">
          <h1 className="title has-text-centered is-size-4 mb-3">
            Modifier Votre Stock
          </h1>
          <div className="columns">
            <div className="column is-three-fifths is-offset-one-fifth">
              <Message
                products={[]}
                title="Instructions"
                type="info"
                message="Pour rechercher un produit scanner son barcode en &#233;tant sur cette page."
              />
              <SearchBar onSearchBarChange={this.onSearchBarChange} />

              <form onSubmit={this.onFormSubmit}>
                <div className="card mb-5">
                  <IncreaseStockProductList
                    products={this.state.products}
                    onInputChange={this.onInputChange}
                  />
                </div>
                <Button text="Confirmer" />
              </form>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

export default withRouter(IncreaseStock);

EDIT 3: Same problem with react-router-dom

The same problem happens when I use react-router-dom's Link component that is out of context, clicking on the link just makes the page go to the top.

I have a component page that lists all products inside of a table and I have a modify link that takes the user the edit page for a specific produt.

Below is the TableCell component that displays individual cell/product.

Image of TableCell

import React, {Component} from "react";
import { Link } from "react-router-dom";

class TableCell extends Component {
  constructor(props) {
    super(props);
    var notice = props.product.inStock === 0 ? "has-background-warning" : "";
    this.state = {notice:notice, id: props.product._id}
  }

  onDelete = (e) => {
    this.props.onDelete(this.props.product._id);
  }

  render() {
    return (
        <tr className={this.state.notice}>
        <td>{this.props.product.barcode}</td>
        <td>{this.props.product.name}</td>
        <td>{this.props.product.price}</td>
        <td>{this.props.product.inStock}</td>
        <td>
          <b>
            <Link
              to={{
                pathname: "/products/edit/" + this.props.product._id,
                product: this.props.product,
              }}
              className="button is-info"
            >
              Modifier
            </Link>
          </b>
        </td>
        <td>
          <b>
            <button onClick={this.onDelete} className="button is-danger has-text-white delete-button"><b>Supprimer</b></button>
          </b>
        </td>
      </tr>
    )
  }
}

export default TableCell;
Papes Traore
  • 1
  • 1
  • 9
  • 2
    The code you posted is one line of the component in the image (i.e one stock item). If your page looks like the image then you must have a list of these, right? If so, can you post the code for the list? I suspect maybe you don't have a stable key for the list. – user2740650 Aug 06 '20 at 02:33
  • I suspect that your callback function in the setState() needs to be converted to an arrow func, so that it can take the lexical scope's THIS, instead of creating its own scope. Try it – codemax Aug 06 '20 at 04:33
  • @codemax That did not do the trick sadly. – Papes Traore Aug 06 '20 at 05:11
  • @PapesTraore are the value of props and state defined in handleAfterChange()? – codemax Aug 06 '20 at 05:28
  • What happens if you disable `handleAfterChange`? Maybe that's interfering. – user2740650 Aug 06 '20 at 12:49
  • @user2740650 I have disabled it but the same thing happens. – Papes Traore Aug 06 '20 at 20:54
  • @codemax I am going to post the code for that so you can see. But to handleAfterChange() uses a function props (onInputChange) that comes from IncreaseStock component (code located above) – Papes Traore Aug 06 '20 at 20:55
  • Your `onInputChange` implementation in `IncreaseStock` looks highly suspect, because whatever element you modify, it's going to take it out of the list and put it on the end. You're **SURE** you disabled that code properly? Can you try commenting out the last line of `onInputChange` where it does `this.setState({ updatedProducts: tempArray });`? I know that will disable the increment/decrement but it will provide a clue. – user2740650 Aug 07 '20 at 04:26
  • Failing that, maybe you can create a demo on jsfiddle for us to try. – user2740650 Aug 07 '20 at 04:33

2 Answers2

0

You can try binding "this" to the function, maybe the render or the click its executed out of context, try with this:

onClick={this.onProductIncrease.bind(this)}

as tip, inquire about, execution context in javascript, and go to reacts official documentation

José
  • 114
  • 5
  • This did not do the trick. The same problem happens when I use react-router Link with a link tag that's out of context. – Papes Traore Aug 06 '20 at 04:23
0

Is there any output in the console when you try to fire the function? Try adding a console.log in the onProductIncrease function to see if it is firing properly.

Dave Yu
  • 315
  • 4
  • 15
this
  • 191
  • 13
  • The log is printed only for the component that did not need scrolling down. But again if I zoom out to like 50% so that no scrolling is required, everything works fine. – Papes Traore Aug 06 '20 at 21:10