Why button Enable Condition after clicking radio button is not working? - javascript

When I try to enable delete button it doesn't work although I put logic for that.. In fact after putting the following the Delete button is always enable. Can't find the problem.
Here's the code:
Sports.js
// initiallising states
const [items1, setItems1] = useState({
pageCount: '',
field_names_state: [],
toggle: false,
elements: [],
data_form: [],
sports_id: '',
buttonDisable: "0",
});
const onRadioChange = (e) => {
items1.sports_id = e.target.value;
items1.buttonDisable="1";
alert(items1.buttonDisable);
};
// mapping our fetched data and send them to Table_Sports.js
const data1 = items1.elements.map((item) => (
<Table_Sports key={item.sports_id} item={item} action={onRadioChange} />
));
<button type='button' disabled={items1.buttonDisable=="1"} onClick={(e) => handleDelete(e)}>
Delete
</button>
Table_Sports.js
<td>
<input
type='radio'
// defaultValue={props.item.sports_id}
defaultValue={props.}
name='sports_id'
onClick={(e) => props.action(e)}
/>
</td>
<td>
<input
type='text'
defaultValue={props.item.sports_name}
name='sports_name'
contentEditable='true'
/>
</td>

To change a value at the state, you should call setItems1, passing the new state object as a parameter instead of manipulate it's properties directly. If you change your onRadioChange function like this it should work:
const onRadioChange = (e) => {
setItems1({
...items1,
sports_id: e.target.value,
buttonDisable: "1",
});
};

Related

Array with inputs in React

Im trying to create a table with an array of products, the problem I have is that the inputs take the value of any input with the same "name". Also, when I try to remove any of the products, it always removes the last one.
https://stackblitz.com/edit/react-gto9bw?file=src/App.js
const [producto, setProducto] = useState({
codigo: '',
nombre: '',
descripcion: '',
precio: '',
cantidad: '',
estado: '',
});
const [productos, setProductos] = useState([]);
const addProducto = () => {
setProductos([...productos, producto]);
};
const removeProducto = (e, index) => {
e.preventDefault();
const list = [...productos];
list.splice(index, 1);
setProductos(list);
};
const handleInputChangeProducto = (e, index) => {
e.preventDefault();
const { name, value } = e.target;
const list = [...productos];
list[index][name] = value;
setProductos(list);
};
The return its a table that has a button to add the product
return (
<div>
<table className="table-size" style={{ border: '1px solid black' }}>
<thead>
<tr>
<th scope="col">Nombre</th>
<th scope="col">Descripcion</th>
</tr>
{productos.map((producto, index) => (
<tr key={index}>
<td>
<input
name="nombre"
onChange={(e) => handleInputChangeProducto(e, index)}
/>
</td>
<td>
<input
name="descripcion"
onChange={(e) => handleInputChangeProducto(e, index)}
/>
</td>
<td onClick={(e) => removeProducto(e, index)}>
<Button>Borrar Producto {index}</Button>
</td>
</tr>
))}
</thead>
</table>
<br />
<button onClick={addProducto}>Crear Producto</button>
</div>
I've tried separating the "tr" into a separate component but that doesn't work either.
This is a common issue when using map with index instead of unique key.
So you can try like this:
Please add a global variable key in your case.
let key = 0;
Then set this key in your producto and increase it.
(In my full code, I used codigo as a key field.)
It should always be increased.
On backend API, you should get unique key as well.
Here is a full code.
import React, { useState } from 'react';
import { Button } from 'react-bootstrap';
import './style.css';
let key = 0;
export default function App() {
const [producto, setProducto] = useState({
codigo: '',
nombre: '',
descripcion: '',
precio: '',
cantidad: '',
estado: '',
});
const [productos, setProductos] = useState([]);
const addProducto = () => {
setProductos([...productos, { ...producto, codigo: ++key }]);
};
const removeProducto = (e, index) => {
e.preventDefault();
const list = [...productos];
list.splice(index, 1);
setProductos(list);
};
const handleInputChangeProducto = (e, index) => {
e.preventDefault();
const { name, value } = e.target;
const list = [...productos];
list[index][name] = value;
setProductos(list);
};
console.log({ productos });
return (
<div>
<table className="table-size" style={{ border: '1px solid black' }}>
<thead>
<tr>
<th scope="col">Nombre</th>
<th scope="col">Descripcion</th>
</tr>
{productos.map((producto, index) => (
<tr key={producto.codigo}>
<td>
<input
name="nombre"
onChange={(e) => handleInputChangeProducto(e, index)}
/>
</td>
<td>
<input
name="descripcion"
onChange={(e) => handleInputChangeProducto(e, index)}
/>
</td>
<td onClick={(e) => removeProducto(e, index)}>
<Button>Borrar Producto {index}</Button>
</td>
</tr>
))}
</thead>
</table>
<br />
<button onClick={addProducto}>Crear Producto</button>
</div>
);
}
Although the other answers are not wrong - and they all (implicitly) fixed the issues below, I wanted to call out the root cause of the problem you were seeing. It's not the use of indexes as keys (although having unique keys is a good idea for performance reasons).
When you created a new producto (at least in your original code sample), you were doing this:
const addProducto = () => {
setProductos([...productos, producto]);
};
Note the new producto being added to the array is always the same object. That means that your array contains a list of pointers that are all pointing to the same object. Modifying one will modify all of them. That's probably not what you want. Instead, do this:
const addProducto = () => {
setProductos([...productos, {...producto} ]);
};
That spreads the properties of your blank producto object onto a new object and ensures that each object in the array is really a separate thing.
The form's values were not actually controlled by the state. Going from your original code sample, make these changes:
{productos.map((producto, index) => (
<tr key={index}>
<td>
<input
name="nombre"
value={producto.nombre} // <-- this was missing. without it there is no relation between what the user is seeing in the input and what value is stored in the productos array.
onChange={(e) => handleInputChangeProducto(e, index)}
/>
</td>
<td>
<input
name="descripcion"
value={producto.descripcion} // <-- Same thing here
onChange={(e) => handleInputChangeProducto(e, index)}
/>
</td>
You need an unique key. Try to generate this unique key on your addProducto:
const addProducto = () => {
setProductos([...productos, { ...producto, id: Date.now() }]);
};
And then on your productos.map pass this generated id in your <tr key={producto.id}>.
Its recommended you have "static" indexes as key, React will understand better how manipulate your list and if occurs any modifications in this list, he will not recalculate everything.
Another point, if you need to manipulate something that depends of your previous state, like these add or remove operations, try to use functions for updates, like:
const addProducto = () => {
setProductos(prevProductos => [...prevProductos, { ...producto, id: Date.now() }]);
};
const removeProducto = (e, index) => {
e.preventDefault();
setProductos(prevProductos => [...prevProductos.slice(0, index), ...prevProductos.slice(index + 1)]);
};
Add an id field to your product JSON.
const [producto, setProducto] = useState({
id: '',
codigo: '',
nombre: '',
descripcion: '',
precio: '',
cantidad: '',
estado: '',
});
Update the ID for every product addition,
const addProducto = () => {
setProductos([...productos, {id: (prodcutos.length + 1), ...producto}]);
};
Replace your current key from index to producto.id,
{productos.map((producto, index) => (
<tr key={producto.id}>
Pass this id as param for you handleInputChangeProducto function,
<td>
<input
name="nombre"
onChange={(e) => handleInputChangeProducto(e, producto.id)}
/>
</td>
<td>
<input
name="descripcion"
onChange={(e) => handleInputChangeProducto(e, producto.id)}
/>
</td>
<td onClick={(e) => removeProducto(e, producto.id)}>
<Button>Borrar Producto {index}</Button>
</td>
const addProducto = () => {
setProductos((list) => [
...list,
{
id: list.length, //this should be unique anyway
codigo: '',
nombre: '',
descripcion: '',
precio: '',
cantidad: '',
estado: '',
},
]);
};
const removeProducto = (e, id) => {
setProductos((list) => {
list.splice(id, 1);
return [...list]
});
};
you should change your list in callback manner as shown.

Dynamically add and remove inputs and update nested array

How to add new input fields dynamically to object in nested array in react js when user clicks on plus sign? dynamically add and remove inputs
I want to add and delete propositionTimes dynamically in the handlepropositionTimeAddClick and handlepropositionTimeRemoveClick methods I shared below. How can I do that? And I want to do the same with propositionResponseTimes and analyzers.
const [issue, setIssue] = useState({
firstResponseDuration: "",
firstResponseOvertime: "",
solutionDuration: "",
solutionOvertime: "",
propositionTimes: [{
propositionTime: ""
}],
propositionResponseTimes: [{ propositionResponseTime: "" }],
analyzers: [{ analyzerName: "", analyzerHuaweiId: "" }],
});
const { firstResponseDuration, firstResponseOvertime,solutionDuration, solutionOvertime, propositionTimes, propositionResponseTimes, analyzers } = issue;
.
.
.
// handle click event of the Remove button
const handlepropositionTimeRemoveClick = index => {
};
// handle click event of the Add button
const handlepropositionTimeAddClick = (i) => {
};
.
.
.
{
issue.propositionTimes.map((item, i) => {
return (
<div key={i} className="form-group" >
<label>
Proposition Time
</label>
<TextField
type="datetime-local"
placeholder="Enter propositionTime"
name="propositionTime"
format="dd-MM-yyyy HH:mm"
value={item.propositionTime}
onChange={e => onInputPropositionTimes(e, i)}
/>
<div>
{issue.propositionTimes.length !== 1 && <button
className="mr10"
onClick={() => handlepropositionTimeRemoveClick(i)}>Remove</button>}
{issue.propositionTimes.length - 1 === i && <button onClick={handlepropositionTimeAddClick(i)}>Add</button>}
</div>
</div>
)
})
}
// handle click event of the Remove button
const handlepropositionTimeRemoveClick = index => {
const issueObj = {...issue};
const filteredIssue = issue.propositionTimes.filter((item, ind) => ind !== index);
issueObj.propositionTimes = filteredIssue;
setIssue(issueObj);
};
// handle click event of the Add button
const handlepropositionTimeAddClick = (i) => {
const issueObj = {...issue};
const newObj = {
propositionTime: "" // Code to add new propositionTime
}
issueObj.propositionTimes.push(newObj)
setIssue(issueObj);
};
Also in your handlepropositionTimeAddClick function, don't call it directly. Just pass the reference
{issue.propositionTimes.length - 1 === i && <button onClick={() => handlepropositionTimeAddClick(i)}>Add</button>}

React - How do I remove array item from state?

In my react app, I have created a page named Add Product with a button named Add Variation to allow adding small, medium, large variations of a product but can't figure out how to remove the small, medium, or large variation object from the state if user changes their mind.
Here's a summary of the problem:
Here's what the component looks like now:
const AddProduct = () => {
const [addVar, setAddVar] = useState(0)
const [values, setValues] = useState({
name: "",
description: "",
categories: [],
category: "",
photo: "",
loading: false,
error: "",
createdProduct: "",
redirectToProfile: false,
variations: [],
formData: ""
});
const {
name,
description,
price,
categories,
category,
photo,
loading,
error,
createdProduct,
redirectToProfile,
variations,
formData
} = values;
const addVariation = (e) => {
e.preventDefault()
setAddVar(addVar + 1)
let oldV = Array.from(variations); // gets current variations
let n = oldV.length; // get current array position
console.log(`Current number of variations is: ${n}`);
let vPost = [{
number: n,
vname: "",
vprice: "",
vquantity: "",
vshipping: ""
}]
let newV = oldV.concat(vPost);
setValues({
...values,
variations: newV,
error: ""
})
}
const handleVariationChange = (name, numberVal) => event => {
// numberVal is the iteration number
// name is the variation property which can be vname, vprice, vshipping, vquantity
// these are tested next in the following if statements
const value = event.target.value;
console.log(`numberVal: `, numberVal);
event.preventDefault()
let newVariations = Array.from(variations)
if(name === "vname") {
newVariations[numberVal].vname = value;
console.log(`newVariations[numberVal].vname value: `, newVariations)
}
if(name === "vprice") {
newVariations[numberVal].vprice = value;
console.log(`newVariations[numberVal].vprice value: `, newVariations)
}
if(name === "vshipping") {
newVariations[numberVal].vshipping = value;
console.log(`newVariations[numberVal].vshipping value: `, newVariations)
}
if(name === "vquantity") {
newVariations[numberVal].vquantity = value;
console.log(`newVariations[numberVal].vquantity value: `, newVariations)
}
setValues({...values, variations: newVariations})
formData.set("variations", JSON.stringify(newVariations));
};
const removeVariation = (e) => {
e.preventDefault()
let newVariations = Array.from(variations)
let popped = newVariations.pop()
setValues({
...values,
variations: newVariations,
error: ""
})
}
const newPostForm = () => (
<form className="mb-3" onSubmit={clickSubmit}>
<h4>Main Photo</h4>
<div className="form-group">
<label className="btn btn-secondary">
<input
onChange={handleChange("photo")}
type="file"
name="photo"
accept="image/*"
/>
</label>
</div>
<div className="form-group">
<label className="text-muted">Main Product Name</label>
<input
onChange={handleChange("name")}
type="text"
className="form-control"
value={name}
placeholder="Add main product name"
/>
</div>
<div className="form-group">
<label className="text-muted">Description</label>
<textarea
onChange={handleChange("description")}
className="form-control"
value={description}
placeholder="Add description"
/>
</div>
<div className="form-group">
<label className="text-muted">Category</label>
<select
onChange={handleChange("category")}
className="form-control"
>
<option>Please select</option>
{categories &&
categories.map((c, i) => (
<option key={i} value={c._id}>
{c.name}
</option>
))}
</select>
</div>
<div>
<button onClick={addVariation}>Add variation</button>
</div>
{variations ? VariationComponent() : null}
<br />
<br />
<button type="submit" className="btn btn-outline-primary">Create Product</button>
</form>
);
return (
<Layout>
<div className="row">
<div className="col-md-8 offset-md-2">
{newPostForm()}
</div>
</div>
</Layout>
);
};
export default AddProduct;
Every time Add variation is clicked, another VariationComponent form is appended to the page . For example, if Add variation button was clicked 3 times, it would result in 3 VariationComponent forms with 3 attached Remove variation buttons. Unfortunately, I do not see how to tell React the position of the #2 item in variations to remove it so I resorted to solving this with .pop(), which is not what I want.
How can I tell React to remove the right array item when Remove variation button is clicked?
If I understand correctly, you can use Arrray.filter() determine which variation to remove. It returns a new array with all but the matching numberVal.
onClick={e=>removeVariation(e)}
const removeVariation = e => {
e.preventDefault();
setValues({
...values,
variations: variations.filter(item => item.name !== e.target.value),
error: ''
});
};
Thanks to #RobinZigmond's and #7iiBob's answers, I was able to solve this by this code:
const removeVariation = (e, num) => {
e.preventDefault();
setValues({
...values,
variations: variations.filter(item => item.number !== num),
error: ''
});
};
Remove variation button:
<button onClick={(e) => removeVariation(e, variations[i].number)} className="btn-danger">
{`Remove Variation`}
</button>
Keep in mind the empty variation object looks like this:
{
number: n,
vname: "",
vprice: "",
vquantity: "",
vshipping: ""
}
and n is coming from addVariation here:
const addVariation = (e) => {
e.preventDefault()
setAddVar(addVar + 1)
let oldV = Array.from(variations); // gets current variations
let n = oldV.length; // get current array position
console.log(`Current number of variations is: ${n}`);
let vPost = [{
number: n,
vname: "",
vprice: "",
vquantity: "",
vshipping: ""
}]
let newV = oldV.concat(vPost);
setValues({
...values,
variations: newV,
error: ""
})
}
Wholehearted thank you as this cost me hours of headache!

how to update array of object in react state based on id

i'm new learning react. i have study case with basic operation (CRUD) in react. i create react saving app for CRUD expense or income.
i have three components i.e InputData for input data, TotalMoney for display total money,expense and income, and table for display all data.
*nb pemasukan=income,pengeluaran=expense, jumlah=amount
example data like this
this.state: {
items: [
{id:1, tipe:"pemasukan", jumlah:20000},
{id:1, tipe:"pemasukan", jumlah:20000}
]
}
this is image
the problem is when want to update data. like the image,when change the first data what changes is the data afterwards, not the first data. the second problem is when the type of the first data changes from income to expense, total money for total expenditure and total income not changed on display.
what is the best way for updating data in this study case?
this is my function to update data
updateItem = event => {
event.preventDefault();
const updatedTipe = this.state.tipe;
const updatedJumlah = parseInt(this.state.jumlah);
const updatedJudul = this.state.judul;
const updatedItems = Object.assign({}, this.state.items, {
tipe: updatedTipe,
jumlah: updatedJumlah,
judul: updatedJudul
});
console.log(updatedItems);
const itemLists = this.state.itemLists.map(itemList =>
itemList.id === this.state.items.id ? updatedItems : itemList
);
console.log(itemLists);
this.setState({ jumlah: 0, judul: "", itemLists: itemLists });
this.setEditing(false);
};
this is declaration table in component inputdata
<Table
items={items}
itemLists={itemLists}
editing={editing}
editItem={this.editItem}
tipe={tipe}
jumlah={jumlah}
judul={judul}
setEditing={this.setEditing}
deleteItem={this.deleteItem}
onChange={this.onChange}
updateItem={this.updateItem}
/>
this is input value for update data
<tr className="rowHover">
<td>
<select
className="form-control form-table"
name="tipe"
onChange={props.onChange}
>
<option>Pilih</option>
<option value="pengeluaran">Pengeluaran</option>
<option value="pemasukan">Pemasukan</option>
</select>
</td>
<td>
<input
type="number"
className="form-control form-table"
name="jumlah"
value={props.jumlah}
onChange={props.onChange}
/>
</td>
<td>
<input
type="text"
className="form-control form-table"
name="judul"
value={props.judul}
onChange={props.onChange}
/>
</td>
<td>
<button
onClick={props.updateItem}
type="button"
className="btn btn-success buttonStyles"
>
Update
</button>
<button
onClick={() => props.setEditing(false)}
type="button"
className="btn btn-warning buttonStyles"
>
Cancel
</button>
</td>
</tr>
this is the state
this.state = {
id: null,
judul: "",
tipe: "",
jumlah: 0,
pengeluaran: 0,
pemasukan: 0,
totalUang: 0,
items: {},
itemLists: [],
editing: false
};
this is my code
https://github.com/saldhyyoga/test
I have run your code to see what the problem is and I found a solution that works.
You have to make 2 updates:
- the first one is persisting the id of the updated item too
editItem = item => {
this.setState({
editing: true,
id: item.id, // add this line
jumlah: item.jumlah,
tipe: item.tipe,
judul: item.judul
});
};
the second one is to access the edited item data based on the persisted id
updateItem = event => {
event.preventDefault();
const updatedTipe = this.state.tipe;
const updatedJumlah = parseInt(this.state.jumlah);
const updatedJudul = this.state.judul;
const updatedId = this.state.id; // take edited item id
const updatedItems = Object.assign({}, this.state.items[updatedId], { // access the information of the editem item
tipe: updatedTipe,
jumlah: updatedJumlah,
judul: updatedJudul
});
console.log(updatedItems);
const itemLists = this.state.itemLists
.map(itemList =>
itemList.id === this.state.id ? updatedItems : itemList // replace this.state.items.id with this.state.id
);
console.log(itemLists);
this.setState({ jumlah: 0, judul: "", itemLists: itemLists });
this.setEditing(false);
};
Here is link where you can find the updated solution which is working as expected.
Edit
In order to fix the amounts calculation just invoke the amount function at the end of updateItem function:
updateItem = event => {
event.preventDefault();
const updatedTipe = this.state.tipe;
const updatedJumlah = parseInt(this.state.jumlah);
const updatedJudul = this.state.judul;
const updatedId = this.state.id;
const updatedItems = Object.assign({}, this.state.items[updatedId], {
tipe: updatedTipe,
jumlah: updatedJumlah,
judul: updatedJudul
});
console.log(updatedItems);
const itemLists = this.state.itemLists.map(itemList =>
itemList.id === this.state.id ? updatedItems : itemList
);
console.log(itemLists);
this.setState({ jumlah: 0, judul: "", itemLists: itemLists });
this.setEditing(false);
this.amount(itemLists); // add this line
};
You can also find the update at the same link

How to edit object and pass new one to array? ReactJS, Material UI

I need to edit array of objects and show new value to the view. I'm pretty new in ReactJS, so I tried do it like below, but after edit I lose everything except for the one I have edited. Can you give me info if my way to do this is correct? Any advices how to do this?
Parent component:
constructor(props) {
super(props);
this.state = { data: [], open: false, openEdit: false, openAlert:false, priority:'', nameTask:'', deadline:new Date() };
}
// Edit row from table
handleEdit = e => {
const index = e.currentTarget.getAttribute('index');
let foundObject = this.state.data[index];
let nameTaskNew = foundObject.nameTask;
let priorityNew = foundObject.priority;
let deadlineNew = foundObject.deadline;
this.setState({openEdit: true, nameTask: nameTaskNew, priority: priorityNew, deadline: deadlineNew });
}
handleSubmitEdit = (e) => {
const err = this.validate();
if (!err) {
this.setState({
// Set new data array
data: [this.state],
// Clear form
openEdit: false,
nameTask: "",
nameTaskError: "",
priority: "Low",
deadline: new Date()
});}}
render() {
const actions = [
<FlatButton label="Edit" primary={true} keyboardFocused={true} onClick={e => this.handleSubmitEdit(e)} />];
return (
{/* Edit form */}
<form>
<Dialog title="Edit your Task" open={this.state.openEdit} actions={actions}>
<TextField floatingLabelText="Task" value={this.state.nameTask} errorText={this.state.nameTaskError}
onChange={e => this.handleTextFieldChange(e)}
onKeyPress={this.handleKeyPress} />
<DatePicker floatingLabelText="Deadline" value={this.state.deadline} onChange={this.handleChangeDate} />
<SelectField floatingLabelText="Priority" value={this.state.priority} onChange={this.handleChangeSelectField}>
<MenuItem value="High" primaryText="High" />
<MenuItem value="Medium" primaryText="Medium" />
<MenuItem value="Low" primaryText="Low" />
</SelectField>
</Dialog>
</form>
);}}
export default Home;
I suggest you check this post. I believe it's what you're looking for:
https://medium.com/#thejasonfile/using-the-spread-operator-in-react-setstate-c8a14fc51be1
I think you are missing spreading the full state.
handleSubmitEdit = (e) => {
const err = this.validate();
if (!err) {
this.setState({
// Set new data array
...this.state,
// Clear form
openEdit: false,
nameTask: "",
nameTaskError: "",
priority: "Low",
deadline: new Date()
});}}
This may help

Categories