POST request: Entering an object containing elements and an array - reactjs

I have a project, and it is an ECommerce project, and the website contains several interfaces, and one of these interfaces is the "Create a Product" interface,
Create a product on the backend graph, written as follows:
enter image description here
And as it is clear from the previous request, there is an object called “En” and another object called “Ar”, and “En” contains “title, description, tags[]” as well as the “Ar”
The problem is that I didn't know how to enter these two objects through React
How can I do this?
In this file I tried to use the tag "Input" and entered the title, description and tag array, and I don't know if my entry method is correct.
import { React, useState } from "react";
import {
Input,
Modal,
ModalBody,
ModalFooter,
ModalHeader,
Button,
} from "reactstrap";
import { gql, useMutation } from "#apollo/client";
import { useCategories } from "../../../../hooks/useCategories";
import { useCollections } from "../../../../hooks/useCollections";
import { useBrand } from "../../../../hooks/useBrand";
import Select from "react-select";
const CREATE_PRODUCT = gql`
mutation createProduct(
$category: String!
$collection: String!
$price: Float!
$sale: Boolean!
$discount: Float!
$stock: Int!
$new: Boolean!
$brand: String!
) {
createProduct(
createProductInput: {
category: $category
collection: $collection
price: $price
sale: $sale
discount: $discount
stock: $stock
new: $new
brand: $brand
}
) {
category,
collection,
price,
sale,
brand,
discount,
stock,
new,
}
}
`;
const GET_PRODUCTS = gql`
query getProducts($limit: Int) {
getProducts(limit: $limit) {
id
title
category{
id
name
}
price
stock
sale
}
}
`;
const AddProduct = () => {
let input;
const categories = useCategories();
const collections = useCollections();
const brands = useBrand();
const categoriesOptions = categories?.data?.categories;
const collectionsOptions = collections?.data?.collections;
const brandsOptions = brands?.data?.brands;
console.log("brands backend: ", brandsOptions, categoriesOptions);
// /////////////////////////////////////////////////
const [category, setCategory] = useState(0);
const [collection, setCollection] = useState(0);
const [brand, setBrand] = useState(0);
const [enTitle, setEnTitle] = useState("");
const [arTitle, setArTitle] = useState("");
const [price, setPrice] = useState(0.0);
const [sale, setSale] = useState(false);
const [newProduct, setNewProduct] = useState(false);
const [discount, setDiscount] = useState(0);
const [stock, setStock] = useState(0);
const [enTags, setEnTags] = useState("");
const [arTags, setArTags] = useState("");
const [enDesc, setEnDesc] = useState("");
const [arDesc, setArDesc] = useState("");
const [description, setDescription] = useState("");
const handleEnTitle = (e) => {
setEnTitle(e.target.value);
};
const handleArTitle = (e) => {
setArTitle(e.target.value);
};
const handleDescription = (e) => {
setDescription(e.target.value);
}
const handlePrice = (e) => {
setPrice(e.target.value);
};
const handleSale = (e) => {
setSale(e.target.value);
};
const handleDiscount = (e) => {
setDiscount(e.target.value);
};
const handleStock = (e) => {
setStock(e.target.value);
};
const handleNewProduct = (e) => {
setNewProduct(e.target.value);
};
const handleEnTags = (e) => {
setEnTags(e.target.value);
};
const handleArTags = (e) => {
setArTags(e.target.value);
};
const handleEnDesc = (e) => {
setEnDesc(e.target.value);
};
const handleArDesc = (e) => {
setArDesc(e.target.value);
};
// //////////////////////////////////////////////
const [modal, setModal] = useState(false);
const togglePopup = () => setModal(!modal);
const [open, setOpen] = useState(false);
const [collectionInput, setCollectionInput] = useState("");
const message = (e) => {
alert("Category was created successfully!");
// return <Alert color="primary">Hey! Pay attention.</Alert>;
};
const resetInput = () => {
setCollectionInput("");
};
const [createProduct] = useMutation(CREATE_PRODUCT, {
refetchQueries: [
{ query: GET_PRODUCTS }, // DocumentNode object parsed with gql
"getProducts", // Query name
],
});
const [inputValue, setValue] = useState("");
const [selectedValueCollection, setSelectedValueCollection] = useState(0);
// handle selection for category
const handleChangeCategory = (value) => {
setCategory(value);
};
// handle selection for collection
const handleChangeCollection = (value) => {
setCollection(value);
};
return (
<div>
{/* <Button> */}
{/* price - (price * discount / 100) */}
<a
onClick={togglePopup}
href="#"
className="btn btn-sm btn-solid"
aria-hidden="true"
>
add product
</a>
{/* </Button> */}
<Modal
isOpen={modal}
toggle={togglePopup}
centered
style={{ padding: "1rem" }}
>
<ModalHeader toggle={togglePopup} style={{ padding: "1rem" }}>
<p>Add Product</p>
</ModalHeader>
<ModalBody style={{ padding: "2rem" }}>
<Select
value={category}
onChange={(categoriesOptions) => {
console.log("value vvv categoriesOptions:", categoriesOptions);
console.log("value.id categoriesOptions: ", categoriesOptions.id);
setCategory(categoriesOptions.id);
}}
options={categoriesOptions}
getOptionLabel={(e) => e.name}
getOptionValue={(e) => e.id}
/>
<Select
value={collection}
// onChange={handleChangeCollection}
onChange={(collectionsOptions) => {
console.log("value vvv:", collectionsOptions);
console.log("value.id: ", collectionsOptions.id);
setCollection(collectionsOptions.id);
}}
options={collectionsOptions}
getOptionLabel={(e) => e.name}
getOptionValue={(e) => e.id}
/>
<Select
value={brand}
//onChange={handleChangeCollection}
onChange={(brandsOptions) => {
console.log("value vvv:", brandsOptions);
console.log("value.id: ", brandsOptions.id);
setBrand(brandsOptions.id);
}}
options={brandsOptions}
getOptionLabel={(e) => e.name}
getOptionValue={(e) => e.id}
/>
<input
id="title"
value="En.title"
onChange={handleEnTitle}
placeholder="Price"
type="text"
style={{
padding: "0.7rem",
paddingRight: "13rem",
margin: "0.7rem",
}}
/>
<input
id="description"
value="En.description"
onChange={handleDescription}
placeholder="Price"
type="text"
style={{
padding: "0.7rem",
paddingRight: "13rem",
margin: "0.7rem",
}}
/>
<input
id="tags"
value="En.tags.[0]"
onChange={handleEnTags}
placeholder="tags"
type="text"
style={{
padding: "0.7rem",
paddingRight: "13rem",
margin: "0.7rem",
}}
/>
<input
id="price"
value={price}
onChange={handlePrice}
placeholder="Price"
type="text"
style={{
padding: "0.7rem",
paddingRight: "13rem",
margin: "0.7rem",
}}
/>
<input
id="sale"
value={sale}
onChange={handleSale}
placeholder="Sale"
type="text"
style={{
padding: "0.7rem",
paddingRight: "13rem",
margin: "0.7rem",
}}
/>
<input
id="discount"
value={discount}
onChange={handleDiscount}
placeholder="discount"
type="text"
style={{
padding: "0.7rem",
paddingRight: "13rem",
margin: "0.7rem",
}}
/>
<input
id="stock"
value={stock}
onChange={handleStock}
placeholder="Stock"
type="text"
style={{
padding: "0.7rem",
paddingRight: "13rem",
margin: "0.7rem",
}}
/>
<input
id="newProduct"
value={newProduct}
onChange={handleNewProduct}
placeholder="New"
type="text"
style={{
padding: "0.7rem",
paddingRight: "13rem",
margin: "0.7rem",
}}
/>
{/* <AsyncSelect
cacheOptions
defaultOptions
value={selectedValueCollection}
getOptionLabel={(e) => e.name}
getOptionValue={(e) => e._id}
propsLoadOptions={collectionsOptions}
onInputChange={handleInputChangeCollection}
onChange={handleChangeCollection}
/> */}
{/* <input
id="enTitle"
value={enTitle}
onChange={handleETitle}
placeholder="English Title"
type="text"
style={{
padding: "0.7rem",
paddingRight: "13rem",
margin: "0.7rem",
}}
/> */}
{/* <input
id="arTitle"
value={arTitle}
onChange={handleArTitle}
placeholder="Arabic Title"
type="text"
style={{
padding: "0.7rem",
paddingRight: "13rem",
margin: "0.7rem",
}}
/> */}
{/* <input
id="enTag"
value={enTags}
onChange={handleEnTags}
placeholder="English Tag"
type="text"
style={{
padding: "0.7rem",
paddingRight: "13rem",
margin: "0.7rem",
}}
/>
<input
id="arTag"
value={arTags}
onChange={handleArTags}
placeholder="Arabic Tag"
type="text"
style={{
padding: "0.7rem",
paddingRight: "13rem",
margin: "0.7rem",
}}
/> */}
{/* <input
id="enDesc"
value={enDesc}
onChange={handleEnDesc}
placeholder="English Desc"
type="text"
style={{
padding: "0.7rem",
paddingRight: "13rem",
margin: "0.7rem",
}}
/>
<input
id="arDesc"
value={arDesc}
onChange={handleArDesc}
placeholder="Arabic Desc"
type="text"
style={{
padding: "0.7rem",
paddingRight: "13rem",
margin: "0.7rem",
}}
/> */}
</ModalBody>
<ModalFooter style={{ paddingRight: "2rem" }}>
<Button
color="primary"
onClick={(e) => {
e.preventDefault();
togglePopup();
createProduct({
variables: {
category,
collection,
price,
sale,
brand,
discount,
stock,
neew,
},
});
console.log(
"jjjjjjjjjjjjjjjjjjjjjjjj: ",
category,
collection,
price,
sale,
brand,
discount,
stock,
neew
);
}}
>
Save
</Button>{" "}
<Button onClick={togglePopup}>Cancel</Button>
</ModalFooter>
</Modal>
</div>
);
};
export default AddProduct;

Related

Setting content editable returns Cannot create property 'contentEditable' on string error

I am visualising a List of Cards with content and what I want to achieve is to set contentEditable=true to the clicked card via its button. I am able to select the content of the element, however contentEditable is not set to true and it returns the following error instead:
Uncaught TypeError: Cannot create property 'contentEditable' on string
And I cant figure out why. What am I doing wrong here and how can I set contentEditable to true for the clicked element via its button?
Here is my code:
import * as React from 'react';
import { ListView } from '#progress/kendo-react-listview';
import { Input } from '#progress/kendo-react-inputs';
import {
Card,
CardTitle,
CardImage,
CardActions,
CardBody,
} from '#progress/kendo-react-layout';
import { Pager } from '#progress/kendo-react-data-tools';
import articles from '../data/articles.json';
import { BubbleChart } from "../components/products/BubbleChart"
const onEdit = (item) => {
console.log(item.Content)
let cardContent = item.Content.contentEditable = "true";
return cardContent
}
const MyItemRender = (props) => {
let item = props.dataItem;
return (
<div
style={{
padding: '20px 20px',
}}
className="parent-container"
>
<div className="k-card-list">
<Card
style={{
width: 260,
height: 340
}}
>
<CardBody
style={{
borderBottom: 'solid 1px rgba(0,0,0,.08)'
}}
>
<CardImage
src={require(`../assets/article-images/${item.Image}`)}
style={{
width: 260,
height: 140,
maxWidth: 260,
}}
/>
<CardTitle
style={{
fontSize: 18,
}}
/>
<CardTitle>{item.Title}</CardTitle>
<CardTitle>{item.Subtitle}</CardTitle>
<p className="content">
Some quick example text to build on the card title and make up the
bulk of the card content.
</p>
</CardBody>
<CardActions>
<div className="footer-buttons-container">
<span>
<span className="k-button k-button-md k-button-rectangle k-rounded-md k-button-flat k-button-flat-base">
<span className="k-icon k-i-preview"></span>Review
</span>
</span>
<span>
<span className="k-button k-edit-button k-button-md k-button-rectangle k-rounded-md k-button-flat k-button-flat-primary" onClick={() => onEdit(item)}>
<span className="k-icon k-i-edit"></span>Edit
</span>
</span>
</div>
</CardActions>
</Card>
</div>
</div>
);
};
export const Products = () => {
const [filteredList, setFilteredList] = React.useState(articles);
const [value, setValue] = React.useState('');
const [page, setPage] = React.useState({
skip: 0,
take: 10,
});
const handlePageChange = (e) => {
setPage({
skip: e.skip,
take: e.take,
});
};
const handleChange = React.useCallback((event) => {
setValue(event.target.value);
const results = articles.filter(post => {
if (event.target.value === "") return articles
return post.Title.includes(event.target.value)
})
console.log(results)
setFilteredList(results)
});
const { skip, take } = page;
return <div>
<div className="chart-container">
<br/>
<br/>
<BubbleChart/>
</div>
<div className="input-container">
<Input
style={{
border: '2px solid #ccc',
boxShadow: 'inset 0px 0px 0.5px 0px rgba(0,0,0,0.0.1)',
}}
placeholder={'Search'}
value={value}
onChange={handleChange}
/>
</div>
<div className="listbox-card-container">
<ListView
data={filteredList.slice(skip, skip + take)}
item={MyItemRender}
style={{
width: '100%',
height: '100%',
}}
/>
<Pager
skip={skip}
take={take}
onPageChange={handlePageChange}
total={articles.length}
/>
</div>
</div>
}
Can't do multiple assigning in one line. add separate lines
const onEdit = (item) => {
console.log(item.Content)
const content = {...item.Content }
content.contentEditable = "true";
return content
}

How to validate my custom input fields before save?

"react-admin": "^3.7.1"
import {
Create,
SimpleForm,
TextInput,
SelectInput,
NumberInput,
required,
} from "react-admin";
import { getChoices } from "../../utils";
import RichTextInput from "ra-input-rich-text";
import { makeStyles } from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import CloudUploadIcon from "#material-ui/icons/CloudUpload";
import EditIcon from "#material-ui/icons/Edit";
import CropperModal from "./Modal";
import GoodForField from "./goodForField";
export const MEASUREMENT_UNITS = {
GM: "GM",
PIECE: "Piece",
GLASS: "Glass",
KATORI: "Katori",
CUP: "Cup",
BOWL: "Bowl",
};
export const RECIPE_PREPARATION_COMPLEXITY = {
EASY: "EASY",
MEDIUM: "MEDIUM",
HARD: "HARD",
};
const useStyles = makeStyles((theme) => ({
paper: {
position: "absolute",
backgroundColor: theme.palette.background.paper,
border: "2px solid #000",
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
root: {
margin: "10px 0",
},
input: {
display: "none",
},
}));
const RecipeCreate = (props) => {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const [file, setFile] = React.useState("");
const [imageLink, setImageLink] = React.useState("");
const [goodForIds, setGoodForIds] = React.useState([]);
const imageRef = React.useRef();
imageRef.current = imageLink;
const goodForRef = React.useRef();
goodForRef.current = goodForIds;
const transform = (data) => {
console.log({ imageRef });
return {
...data,
image_url: imageRef.current,
good_for: goodForRef.current,
};
};
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const handleChangeFileInput = (e) => {
setFile(e.target.files[0]);
handleOpen();
};
const handleClickEdit = () => {
setFile(imageLink);
handleOpen();
};
return (
<>
<CropperModal
open={open}
handleClose={handleClose}
file={file}
handleImageLink={setImageLink}
handleVisibility={setOpen}
/>
<Create transform={transform} {...props}>
<SimpleForm redirect={false}>
{imageLink && (
<div
style={{
display: "flex",
width: "100px",
height: "100px",
marginBottom: "20px",
position: "relative",
}}
>
<img
src={imageLink}
alt="dish"
style={{
width: "100px",
height: "100px",
border: "5px solid #c9c5c5",
cursor: "pointer",
}}
onClick={handleClickEdit}
/>
<EditIcon
onClick={handleClickEdit}
style={{
position: "absolute",
cursor: "pointer",
right: "-10px",
bottom: "-10px",
backgroundColor: "#c9c5c5",
fontSize: "1.2rem",
}}
/>
</div>
)}
<div className={classes.root}>
<input
accept=".png, .jpg, .jpeg"
className={classes.input}
id="contained-button-file"
multiple
type="file"
onChange={handleChangeFileInput}
/>
<label htmlFor="contained-button-file">
<Button
style={{ textTransform: "none" }}
startIcon={<CloudUploadIcon />}
variant="contained"
color="primary"
component="span"
>
{imageLink ? "Upload new" : "Upload picture"}
</Button>
</label>
</div>
<TextInput source="name" validate={required()} />
<SelectInput
source="measurement_unit"
lable="Measurement Unit"
choices={getChoices(MEASUREMENT_UNITS)}
validate={required()}
/>
<TextInput validate={required()} source="description" multiline />
<RichTextInput source="recipe" validate={required()} />
<TextInput
validate={required()}
source="preparation_time"
lable="Preparation time"
/>
<SelectInput
validate={required()}
source="preparation_complexity"
label="Preparation Complexity"
choices={getChoices(RECIPE_PREPARATION_COMPLEXITY)}
/>
<GoodForField goodForIds={goodForIds} setGoodForIds={setGoodForIds} />
<NumberInput validate={required()} source="calories" />
<NumberInput validate={required()} source="carbs" />
<NumberInput validate={required()} source="protein" />
<NumberInput validate={required()} source="fats" />
<NumberInput validate={required()} source="fibre" />
<TextInput source="preparation_video_link" label="Video url" />
</SimpleForm>
</Create>
</>
);
};
I am using Create component of react-admin to create the recipe resource. I also have an image scource through which I am uploading the image to cloud storage and later passing the url to create throught transform function.
CropperModal is a component that is used to crop the image and upload the image to cloud stoarge. When the form is submitted I am passing the image URL to the request body with the help of transform function. My problem is how can I validate this custom field before save ?
Note: CropperModal is purely a custom component, not using any of the react-admin utilities.

Passing value of component in separate jsx file's value to event on Full-Calendar

I am new to React. I am trying to pass the value of a select box imported from a separate .jsx file to an event for full-calendar. I can't find anything on this or figure out how to do it for the life of me. This is my component for the select box in a separate file.
import React from 'react'
export const Dropdown = (props) => (
<div className="form-group">
<select
className="form-control"
name="partner"
onChange={props.onChange}
>
<option defaultValue>Select Partner</option>
{props.options.map((item, index) => (
<option key={index} value={item.value}>
{item.label}
</option>
))}
</select>
</div>
)
export default class DropdownList extends React.Component {
constructor() {
super()
this.state = {
list: [],
chosenValue: '',
}
}
componentDidMount() {
fetch('/calendar/users')
.then((response) => response.json())
.then((item) => this.setState({ list: item }))
}
onDropdownChange = (e) => {
this.setState({ chosenValue: e.target.value })
}
render() {
return (
<div className="form-group">
<Dropdown
name="partner"
id="partner"
options={this.state.list}
onDropdownChange={this.onDropdownChange}
/>
</div>
)
}
}
This is my modal that contains the form to pass through as an event to full-calendar
import React, {useState} from "react";
import Modal from "react-modal";
import DateTimePicker from "react-datetime-picker";
import 'bootstrap-icons/font/bootstrap-icons.css'
import DropdownList from "./SelectBox";
export default function ({ isOpen, onClose, onEventAdded }) {
const [title, setTitle] = useState("");
const [start, setStart] = useState(new Date());
const [end, setEnd] = useState(new Date());
const [selectValue] = useState(DropdownList);
const onSubmit = (event) => {
event.preventDefault();
onEventAdded({
title,
start,
partner: selectValue,
end
});
onClose()
};
return (
<Modal isOpen={isOpen}
onClose={onClose}
style={{
overlay: {
backgroundColor: 'rgba(0, 0, 0, 0.5)'
},
content: {
top: '50%',
left: '50%',
right: 'auto',
bottom: 'auto',
marginRight: '-50%',
transform: 'translate(-50%, -50%)',
border: '1px solid #ccc',
background: '#fff',
overflow: 'auto',
WebkitOverflowScrolling: 'touch',
borderRadius: '4px',
outline: 'none',
padding: '20px',
maxWidth: '1200px',
minHeight: '80%',
textAlign: 'center'
}
}}
>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch#4.5.2/dist/lux/bootstrap.min.css" />
<form onSubmit={onSubmit}>
<div className='form-group'>
<input className='form-control' placeholder="Title" value={title} onChange={e => setTitle(e.target.value)} />
</div>
<DropdownList />
<div className='form-group'>
<DateTimePicker name='start' value={start} onChange={date => setStart(date)} />
</div>
<div className='form-group'>
<DateTimePicker name='end' value={end} onChange={date =>setEnd(date)} />
</div>
<button className="btn btn-primary btn-block" type="submit">Add Event</button>
<button className="btn btn-danger btn-block" onClick={onClose}>Cancel</button>
</form>
</Modal>
)
}
I have tried DropdownList.value, redoing the onDropdownChange(e) function in the modal jsx file to no avail. I'm sure this is really simple and I'm getting a headache so thank you for your kindness ahead of time. I'm currently trying to use props but it's all coming up undefined.
Edit: tried to add to component in the modal file
const [partner, setPartner] = useState();
<DropdownList
ref={partnerRef}
onDropdownChange={e => setPartner(this.chosenValue = e.target.value)}
/>

Validation of textbox message:- (fill the value)

I dont know how to validate the textbox in this so can anyone do for one textbox other all box i will do on myself i watch videos but i am not geatting it.....
I dont know how to validate the textbox in this so can anyone do for one textbox other all box i will do on myself i watch videos but i am not geatting it.....
I dont know how to validate the textbox in this so can anyone do for one textbox other all box i will do on myself i watch videos but i am not geatting it.....
I dont know how to validate the textbox in this so can anyone do for one textbox other all box i will do on myself i watch videos but i am not geatting it.....
import React, { useState } from 'react'
import './AddRecipes.css'
import axios from 'axios'
function AddRecipes() {
const [state, setstate] = useState({
label: '',
source: '',
dietlabels: [],
healthlabels: [],
cuisineType: [],
ingredients: [],
mealType: [],
makingDescription: '',
recipeImage: null
})
state={
dietlabels= " This is empty fild",
healthlabels= "",
cuisineType ="",
ingredients= "",
mealType= ""
};
const onFormSubmit = async (e) => {
e.preventDefault()
console.log(state.ingredients)
try {
const fd = new FormData()
fd.append('recipeImage', state.recipeImage, state.recipeImage.name)
fd.append('label', state.label)
fd.append('source', state.source)
fd.append('dietlabels', JSON.stringify(state.dietlabels))
fd.append('healthlabels', JSON.stringify(state.healthlabels))
fd.append('ingredients', JSON.stringify(state.ingredients))
fd.append('cuisineType', JSON.stringify(state.cuisineType))
fd.append('mealType', JSON.stringify(state.mealType))
fd.append('makingDescription', state.makingDescription)
// console.log(fd)
const { data: response } = await axios.post(
'http://localhost:8000/recipe',
fd
)
console.log('api response' + response)
} catch (error) {
console.log(error)
}
}
const [arrayState, arraySetSate] = useState({
dietlabels: '',
healthlabels: '',
cuisineType: '',
ingredients: { text: '', weight: '' },
mealType: ''
})
const subIngredientStatePush = (event) => {
let replica = { ...arrayState }
replica.ingredients[event.target.name] = event.target.value
arraySetSate(replica)
}
//string type field data handling
const stringTypeFieldDataHandler = (event) => {
let replicateState = { ...state }
replicateState[event.target.name] = event.target.value
setstate(replicateState)
}
//image file handling
const imageUploadFileHandler = (event) => {
let replicateState = { ...state }
replicateState['recipeImage'] = event.target.files[0]
setstate(replicateState)
}
//array elements data handling
const arrayElementSeparateStateHandler = (event) => {
let repilica = { ...arrayState }
repilica[event.target.name] = event.target.value
arraySetSate(repilica)
}
const addDietLabels = () => {
let replica = { ...state }
let arrayreplica = { ...arrayState }
replica.dietlabels.push(arrayState.dietlabels)
setstate(replica)
arrayreplica.dietlabels = ''
arraySetSate(arrayreplica)
}
const newlable =()=>{
}
const addHealthLables = () => {
let replica = { ...state }
let arrayreplica = { ...arrayState }
replica.healthlabels.push(arrayState.healthlabels)
setstate(replica)
arrayreplica.healthlabels = ''
arraySetSate(arrayreplica)
}
const addcuisineType = () => {
let replica = { ...state }
let arrayreplica = { ...arrayState }
replica.cuisineType.push(arrayState.cuisineType)
setstate(replica)
arrayreplica.cuisineType = ''
arraySetSate(arrayreplica)
}
const addmealType = () => {
let replica = { ...state }
let arrayreplica = { ...arrayState }
replica.mealType.push(arrayState.mealType)
setstate(replica)
arrayreplica.mealType = ''
arraySetSate(arrayreplica)
}
const AddingredientsClick = () => {
let replica = { ...state }
let arrayreplica = { ...arrayState }
let ingredientObj = {
text: arrayState.ingredients.text,
weight: arrayState.ingredients.weight
}
replica.ingredients.push(ingredientObj)
setstate(replica)
arrayreplica.ingredients = { text: '', weight: '' }
arraySetSate(arrayreplica)
}
return (
<div className="recipeForm container">
<form
style={{
display: 'flex',
flexDirection: 'column',
width: '70%',
alignItems: 'center'
}}
>
<label className="addrecipe-labels" htmlFor="label">
Enter Recipe Name
</label>
<input
type="text"
id="label"
name="label"
placeholder="recipe name"
className="inputs"
value={state.label}
onChange={stringTypeFieldDataHandler}
/>
<br />
<label className="addrecipe-labels" htmlFor="source">
Recipe Source
</label>
<input
type="text"
id="source"
name="source"
placeholder="enter source"
value={state.source}
onChange={stringTypeFieldDataHandler}
className="inputs"
/>
{/* <label className="addrecipe-labels" htmlFor="url">
URL
</label>
<input
type="text"
id="url"
name="url"
placeholder="paste url"
className="inputs"
/> */}
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-evenly',
margin: '5px 5px'
}}
>
<label className="addrecipe-labels" htmlFor="dietlabels">
diet labels
</label>
<input
type="text"
id="dietlabels"
name="dietlabels"
onChange={arrayElementSeparateStateHandler}
onChange={newlable}
value={arrayState.dietlabels}
placeholder="type labels"
className="inputs"
/>
<div>{this.state.nameError}</div>
<button
style={{ width: '40px' }}
type="button"
onClick={addDietLabels}
>
Add
</button>
<p>{String(state.dietlabels)}</p>
</div>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-evenly',
margin: '5px 5px'
}}
>
<label className="addrecipe-labels" htmlFor="healthlabels">
Health labels
</label>
<input
type="text"
value={arrayState.healthlabels}
onChange={arrayElementSeparateStateHandler}
id="healthlabels"
name="healthlabels"
placeholder="health lables"
className="inputs"
/>
<button
style={{ width: '40px' }}
type="button"
onClick={addHealthLables}
>
Add
</button>
{String(state.healthlabels)}
</div>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-evenly',
margin: '5px 5px'
}}
>
<label className="addrecipe-labels" htmlFor="cuisineType">
cuisine type
</label>
<input
type="text"
id="cuisineType"
value={arrayState.cuisineType}
onChange={arrayElementSeparateStateHandler}
name="cuisineType"
placeholder="add cautions"
className="inputs"
/>
<button
style={{ width: '40px' }}
type="button"
onClick={addcuisineType}
>
Add
</button>
{String(state.cuisineType)}
</div>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-evenly',
margin: '5px 5px'
}}
>
<label className="addrecipe-labels" htmlFor="mealtype">
meal type
</label>
<input
type="text"
id="mealtype"
name="mealType"
value={arrayState.mealType}
onChange={arrayElementSeparateStateHandler}
placeholder="add cautions"
className="inputs"
/>
<button style={{ width: '40px' }} type="button" onClick={addmealType}>
Add
</button>
{String(state.mealType)}
</div>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-evenly',
margin: '5px 5px'
}}
>
<label className="addrecipe-labels" htmlFor="ingredients">
ingredients
</label>
<input
type="text"
id="ingredients"
name="text"
value={arrayState.ingredients.text}
placeholder="ingredient"
className="inputs"
onChange={subIngredientStatePush}
style={{ margin: '0 5px' }}
/>
<input
type="text"
id="quantity"
name="weight"
value={arrayState.ingredients.weight}
onChange={subIngredientStatePush}
placeholder="quantity"
className="inputs"
/>
<button
style={{ width: '50px', marginLeft: '7px' }}
type="button"
onClick={AddingredientsClick}
>
Add
</button>
{JSON.stringify(state.ingredients)}
</div>
{/*
<label className="addrecipe-labels" htmlFor="country">
Country
</label>
<select id="country" name="country" className="inputs">
<option value="australia">Australia</option>
<option value="canada">Canada</option>
<option value="usa">USA</option>
</select> */}
<label className="addrecipe-labels" htmlFor="recipe-description">
Recipe description
</label>
<textarea
id="recipe-description"
name="makingDescription"
value={state.makingDescription}
onChange={stringTypeFieldDataHandler}
placeholder="Write something.."
style={{ height: '200px' }}
className="inputs"
></textarea>
<div style={{ margin: '15px 0' }}>
<label className="addrecipe-labels" htmlFor="recipeImage">
Add recipe image
</label>
<input
type="file"
name="recipeImage"
onChange={imageUploadFileHandler}
id="recipeImage"
/>
</div>
<input
type="submit"
value="Submit"
className="inputs submit"
onClick={onFormSubmit}
/>
</form>
</div>
)
}
export default AddRecipes
You have a couple of mistakes within your code but I'll focus on your question.
To validate, you add the validation logic either to the onChange or onSubmit callback.
onChange callback example
const subIngredientStatePush = ({ target: { name, value }}) => {
// validate
if (name === "text" && value.length > 10) {
setError((errors) => ({ ...errors, [name]: "too long" });
}
//... etc
// update state
const replica = { ...arrayState };
replica.ingredients = { ...replica.ingredients }; // need to copy this state as well
replica.ingredients[name] = value
arraySetSate(replica)
}

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:
I am adding image from input field, and this is taken care by imageSelectedHandler
I want to know is imageUploadHandler required? or even without using I will be able to submit the form along with the image?
I am also adding new input fields, everytime user clicks, add specification button. this is by addClick and removeClick handler
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;
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.

Resources