AnswerBun.com

Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. because of setState

There might be issue in setFormData, can someone please tell me what mistake i have done that causing rendering in the code. also want to know if there is any mistake in storing image in a state.

Code summary:

  1. I am adding image from input field, and this is taken care by imageSelectedHandler
  2. I want to know is imageUploadHandler required? or even without using I will be able to submit the form along with the image?
  3. I am also adding new input fields, everytime user clicks, add specification button. this is by addClick and removeClick handler
  4. handleChange handler take care of indivisual Input Flield.

Please help someone.

import React, { useState } from "react";
import FormInput from "../Forminput/forminput";
import CustomButton from "../Custombutton/custombutton";
import axios from "axios";

const ProductUpload = () => {
  const [formData, setFormData] = useState({
    sub_category: null,
    product_name: null,
    product_image: null,
    product_specs: [{ specification: "", specvalue: "" }],
  });

  const { sub_category, product_name, product_image, product_specs } = formData;

  const imageSelectedHandler = (event) => {
    setFormData((prevState) => ({
      product_image: event.target.files[0],
      sub_category: { ...prevState.sub_category },
      product_name: { ...prevState.product_name },
      product_specs: [
        ...prevState.product_specs,
        { specification: "", specvalue: "" },
      ],
    }));
  };

  const imageUploadHandler = () => {
    const fd = new FormData();
    fd.append("product_image", product_image, product_image.name); //.name is Imp as name is property of file
  };
  const handleChange = (i, e) => {
    const { name, value } = e.target;
    product_specs[i] = { ...product_specs[i], [name]: value };
    setFormData({ product_specs }); //TO BE CHECKED
  };
  //to add extra input field
  const addClick = () => {
    setFormData((prevState) => ({
      sub_category: { ...prevState.sub_category },
      product_image: { ...prevState.image },
      product_name: { ...prevState.product_name },
      product_specs: [
        ...prevState.product_specs,
        { specification: "", specvalue: "" },
      ],
    }));
  };
  //to remove extra input field
  const removeClick = (i) => {
    product_specs.splice(i, 1);
    setFormData((prevState) => {
      return {
        sub_category: { ...prevState.sub_category },
        product_image: { ...prevState.image },
        product_name: { ...prevState.product_name },
        product_specs: [
          ...prevState.product_specs,
          { specification: "", specvalue: "" },
        ],
      };
    });
  };
  const handleSubmit = async (event) => {
    event.preventDefault();
    const newProduct = {
      sub_category,
      product_name,
      product_image,
      product_specs,
    };
    try {
      const config = {
        headers: {
          "Content-Type": "application/json",
        },
      };
      const body = JSON.stringify(newProduct);
      const res = await axios.post("/api/product", body, config);
      console.log(res.data);
    } catch (error) {
      console.error(error.response.data);
    }
  };
  const createUI = () => {
    return product_specs.map((el, i) => (
      <div key={i} className="inputgroup">
        <FormInput
          type="text"
          name="specification"
          handleChange={handleChange}
          value={el.specification}
          label="specification"
          required
          customwidth="300px"
        ></FormInput>
        <FormInput
          type="text"
          name="specvalue"
          handleChange={handleChange}
          value={el.specvalue}
          label="specification values seperated by quomas"
          required
        ></FormInput>
        <CustomButton
          onClick={removeClick(i)}
          type="button"
          value="remove"
          style={{ margin: "12px" }}
        >
          Remove
        </CustomButton>
      </div>
    ));
  };
  return (
    <div className="container">
      <form
        action="/upload"
        method="post"
        className="form"
        onSubmit={handleSubmit}
        encType="multipart/form-data"
      >
        <h3 style={{ color: "roboto, sans-serif" }}>
          Add new product to the database
        </h3>
        <div
          style={{
            display: "flex",
            height: "200px",
            width: "200px",
            border: "2px solid #DADCE0",
            borderRadius: "6px",
            position: "relative",
          }}
        >
          <input
            style={{ display: "none" }}
            type="file"
            accept="image/*"
            onChange={imageSelectedHandler}
            ref={(fileInput) => (this.fileInput = fileInput)}
            multiple={false}
            name="product_image"
          />
          <CustomButton onClick={() => this.fileInput.click()}>
            Select Image
          </CustomButton>
          <CustomButton onClick={imageUploadHandler}>Upload</CustomButton>

          {/*as per brad- type = "submit" value="submit"  this should not be used, file should upload with the form submit */}
          <div>
            <img
              style={{
                width: "100%",
                height: "100%",
              }}
              alt="#"
            />
          </div>
        </div>

        {createUI()}
        <div>
          <CustomButton
            onClick={addClick}
            type="button"
            style={{ margin: "14px" }}
          >
            Add More Fields
          </CustomButton>
          <CustomButton type="submit" style={{ margin: "12px" }}>
            Upload Product
          </CustomButton>
        </div>
      </form>
    </div>
  );
};

export default ProductUpload;

code with the changes suggested by @AKX

import React, { useState } from "react";
import FormInput from "../Forminput/forminput";
import CustomButton from "../Custombutton/custombutton";
import axios from "axios";

const ProductUpload = () => {
  const [sub_category, setSub_category] = useState({
    sub_category: "",
  });

  const [product_name, setProduct_name] = useState({
    product_name: "",
  });

  const [product_image, setProduct_image] = useState({
    product_image: "",
  });

  const [product_specs, setProduct_specs] = useState({
    product_specs: [{ specification: "", specvalue: "" }],
  });

  const imageSelectedHandler = (event) => {
    setProduct_image({ product_image: event.target.files[0] });
  };

  // const imageUploadHandler = () => {
  //   const fd = new FormData();
  //   fd.append("product_image", product_image, product_image.name); //.name is Imp as name is property of file
  // };

  const handleChange = (i, e) => {
    const { name, value } = e.target;
    product_specs[i] = { ...product_specs[i], [name]: value };
    setProduct_specs({ product_specs }); //TO BE CHECKED
  };
  //to add extra input field
  const addClick = () => {
    setProduct_specs({
      product_specs: [...product_specs, { specification: "", specvalue: "" }],
    });
  };
  //to remove extra input field
  const removeClick = (i) => {
    [product_specs].splice(i, 1);
    setProduct_specs({
      product_specs: [product_specs],
    });
  };
  const handleSubmit = async (event) => {
    event.preventDefault();
    const newProduct = {
      sub_category,
      product_name,
      product_image,
      product_specs,
    };
    try {
      const config = {
        headers: {
          "Content-Type": "application/json",
        },
      };
      const body = JSON.stringify(newProduct);
      const res = await axios.post("/api/product", body, config);
      console.log(res.data);
    } catch (error) {
      console.error(error.response.data);
    }
  };
  const createUI = () => {
    return [product_specs].map((el, i) => (
      <div key={i} className="inputgroup">
        <FormInput
          type="text"
          name="specification"
          handleChange={handleChange}
          value={el.specification}
          label="specification"
          required
          customwidth="300px"
        ></FormInput>
        <FormInput
          type="text"
          name="specvalue"
          handleChange={handleChange}
          value={el.specvalue}
          label="specification values seperated by quomas"
          required
        ></FormInput>
        <CustomButton
          onClick={removeClick(i)}
          type="button"
          value="remove"
          style={{ margin: "12px" }}
        >
          Remove
        </CustomButton>
      </div>
    ));
  };
  return (
    <div className="container">
      <form
        action="/upload"
        method="post"
        className="form"
        onSubmit={handleSubmit}
        encType="multipart/form-data"
      >
        <h3 style={{ color: "roboto, sans-serif" }}>
          Add new product to the database
        </h3>
        <div
          style={{
            display: "flex",
            height: "200px",
            width: "200px",
            border: "2px solid #DADCE0",
            borderRadius: "6px",
            position: "relative",
          }}
        >
          <input
            style={{ display: "none" }}
            type="file"
            accept="image/*"
            onChange={imageSelectedHandler}
            ref={(fileInput) => (this.fileInput = fileInput)}
            multiple={false}
            name="product_image"
          />
          <CustomButton onClick={() => this.fileInput.click()}>
            Select Image
          </CustomButton>
          <CustomButton
          //  onClick={imageUploadHandler}
          >
            Upload
          </CustomButton>

          {/*as per brad- type = "submit" value="submit"  this should not be used, file should upload with the form submit */}
          <div>
            <img
              style={{
                width: "100%",
                height: "100%",
              }}
              alt="#"
            />
          </div>
        </div>
        <FormInput
          type="text"
          name="sub_category"
          handleChange={handleChange}
          value={sub_category}
          label="select from subcategories"
          required
        ></FormInput>
        <FormInput
          type="text"
          name="product_name"
          handleChange={handleChange}
          value={product_name}
          label="product name"
          required
        ></FormInput>
        {createUI()}
        <div>
          <CustomButton
            onClick={addClick}
            type="button"
            style={{ margin: "14px" }}
          >
            Add More Fields
          </CustomButton>
          <CustomButton type="submit" style={{ margin: "12px" }}>
            Upload Product
          </CustomButton>
        </div>
      </form>
    </div>
  );
};

export default ProductUpload;

Stack Overflow Asked by Sudarshan Mane on January 2, 2021

1 Answers

One Answer

Ciao, I have never seen an use of prevState in useState hook like that. Basically, when you spread prevState it's like you saying: "keep the same values for all the state elements" then comma, then a list of new values for the state.

So, it's not necessary to re-assign the same value of prevState in current state (because it already have!). So, I think you should modify your code like this:

const imageSelectedHandler = (event) => {
  let new_product_specs = formData.product_specs;
  new_product_specs.push({ specification: "", specvalue: "" });
  setFormData((prevState) => ({
     ...prevState,
     product_image: event.target.files[0],
     product_specs: new_product_specs
   }));
};

For the question about imageUploadHandler, I don't think is required to submit form. But I'm not 100% sure.

Answered by Giovanni Esposito on January 2, 2021

Add your own answers!

Related Questions

How to get a thread dump of a running Node.js process?

3  Asked on December 23, 2020 by philipp-claen

       

Jquery Select all IDs in different Sections

1  Asked on December 23, 2020 by abcid-d

     

Can’t merge two dictionaries into one dictionary

3  Asked on December 23, 2020 by yoel-regen

 

Sharing Power BI Dashboards – External Users

1  Asked on December 23, 2020 by datageek

 

SQL query to loop through records

1  Asked on December 22, 2020 by doe

         

Is there a better way to implement nested setTimeouts()

1  Asked on December 22, 2020 by dashman

 

CMYK image being converted to RGB

0  Asked on December 22, 2020 by entity

     

Bootstrap Toast at bottom right of page

1  Asked on December 22, 2020 by sean-thorburn

     

I doesn’t receive any formated data from static class

0  Asked on December 21, 2020 by asfdasfdsfdfsd

     

SQL Notes: Hackerrank The Report Query how to save the subquery variable

3  Asked on December 21, 2020 by varad-paralikar

 

NEO4J gives node count as negative

0  Asked on December 21, 2020 by ck22

     

Ask a Question

Get help from others!

© 2022 AnswerBun.com. All rights reserved.