Reputation: 11
Im just wondering if anyone could point out where im going wrong with my code. Im relativly new to react so began with a simple todo list. I then edited this to allow for various other forms such as menu, profile etc. Below is the code attached for the menu section. My back end works if I use postmaster which leads me to believe its my front end, and specifically my useState. I can call the data and view it within my modal and it appears, however, I cant seem to edit the specific data within the form field and/or post it to my database.
Any help would be greatly appreciated.
Ive attached my code below.
import React, { Fragment, useState } from "react";
const EditMenu = ({ menu }) => {
//editText function
const [inputs, setInputs] = useState(menu.item_title, menu.item_price, menu.item_description, menu.item_category);
const { title, category, price, description } = inputs;
const onChange = e =>
setInputs({ ...inputs, [e.target.name]: e.target.value });
const editMenuItem = async (item_id) => {
try {
const body = { title, category, price, description };
const res = await fetch(`http://localhost:5000/menu/${item_id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
});
window.location = "/admin";
} catch (error) {
console.error(error.message);
}
};
return (
<Fragment>
<button type="button" className="btn btn-warning" data-toggle="modal" data-target={`#id${menu.item_id}`}>Edit</button>
{/*id = "id21"*/}
<div className="modal" id={`id${menu.item_id}`} onClick={() => setInputs(menu.item_title, menu.item_price, menu.item_description, menu.item_category)}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h4 className="modal-title">Edit Menu</h4>
<button className="close btn-danger" data-dismiss="modal" onClick={() => setInputs(menu.item_title, menu.item_price, menu.item_description, menu.item_category)}>×</button>
</div>
<div className="modal-body">
<input type="text" name="title" placeholder="Title" className="form-control my-3" value={menu.item_title} onChange={e => onChange(e)} />
<input type="tel" name="price" placeholder="Price" className="form-control my-3" value={menu.item_price} onChange={e => onChange(e)} />
<input type="text" name="description" placeholder="Description" className="form-control my-3" value={menu.item_description} onChange={e => onChange(e)} />
<input type="text" name="category" placeholder="Category" className="form-control my-3" value={menu.item_category} onChange={e => onChange(e)} />
</div>
<div className="modal-footer">
<button type="button" className="btn btn-warning" data-dismiss="modal" onClick={() => editMenuItem(menu.item_id)}>Edit</button>
<button type="button" className="btn btn-danger" data-dismiss="modal" onClick={() => setInputs(menu.item_title, menu.item_price, menu.item_description, menu.item_category)}>Close</button>
</div>
</div>
</div>
</div>
</Fragment>
);
};
Update, Ive tried various suggested fixes using the below answers so far. Both of these fixes allow the form fields to be editable, and the information within the form fields changes and thus within the state also however it is not sent to the database. Upon refresh of the page, the old information is pulled from the database.
Ive discovered that if I removed all of the form fields but one, it successfully updates AND sends to the database. Title OR Description OR Price OR Category. Checking the network tab within the browser whilst updating shows that for more than one input field, the put request fails and no information/payload is sent to the body within the request tab.
As a result, the database returns a NOT NULL error. Based off Oliviers answer below, that setInput is only recognises one parameter, I can only imagine that this is what is breaking when there is more than one form field/input added. I unfortunatly dont know enough react to know if this is the case or not.
Upvotes: 1
Views: 1050
Reputation: 11
I eventually figured out the issue. By splitting my setInput useState into seperate individual useStates, I was able to get it to work.
So my origional code of...
const EditMenu = ({ menu }) => {
const [inputs, setInputs] = useState(menu.item_title, menu.item_price, menu.item_description, menu.item_category);
const { title, category, price, description } = inputs;
changed to this.
const EditMenu = ({ menu }) => {
const [item_title, setTitle] = useState(menu.item_title);
const [item_price, setPrice] = useState(menu.item_price);
const [item_description, setDescription] = useState(menu.item_description);
and the onChange function and form input...
const onChange = e =>
setInputs({ ...inputs, [e.target.name]: e.target.value });
<input... onChange={e => onChange(e)} />
changed to this...
value={item_title} onChange={e => setTitle(e.target.value)} />
value={item_price} onChange={e => setPrice(e.target.value)} />
value={item_description} onChange={e => setDescription(e.target.value)} />
In the end, Oliviers reasoing was correct even if the soloution didnt work for me. That my setInput only allowed for one parameter. Splitting it up allowed me to pass the remaining parameters. Thank you everyone for the help, hopefully this might help someone else some day too!
Upvotes: 0
Reputation: 18153
I see a problem in your state initialization => const [inputs, setInputs] = useState(menu.item_title, menu.item_price, menu.item_description, menu.item_category);
is not correct, useState
take a single parameter, here you must build an object representing the inputs.
Here is a solution using a function to initialize the inputs
state, to prevent computing the object each time the component is re-rendered
function buildInputs(menu) {
return {
title: menu.item_title,
category: menu.item_category,
price: menu.item_price,
description: menu.item_description
};
}
const EditMenu = ({ menu }) => {
//editText function
const [inputs, setInputs] = useState(() => buildInputs(menu));
const { title, category, price, description } = inputs;
// Needed if you want the inputs to be updtated when the menu property is updated
useEffect(() => setInputs(buildInputs(menu)), [menu]);
const onChange = e => setInputs({ ...inputs, [e.target.name]: e.target.value });
...
You must also change the input value to reflect the state variable :
<input type="text" name="title" placeholder="Title"
className="form-control my-3" value={title} onChange={onChange} />
Upvotes: 1
Reputation: 26
You should set your state like this:
const [inputs, setInputs] = useState({
title: menu.item_title,
price: menu.item_price,
category: menu.item_category,
description: menu.item_description
});
also you need to change value attributes to be variables rather than setting them to the menu values, for example:
//code
<input name="title" value={inputs.title} onChange={onChange}/>
cause values inside inputs are changeable by your onChange method, on the other hand, values inside menu object will remain with the same values.
Upvotes: 0