So i have an array that is mapped and displaying multiple table rows. And each of them also has a button that can be clicked. So i want to make it so, that whenever the button is clicked, it changes from displaying data ( tags) to input tags. So i can edit that data. My problem is that, whenever the button is clicked, it changes not only one table row, but all the table rows. I know it's because of the map function, but i don't really know how to fix that. I thought doing something similar to handleRowRemove function, but i really can't seem to figure out how to do that.
import { useState } from "react";
const Pricing = ({ register, errors }) => {
const [tableRows, setTableRows] = useState([
{ price: 12, description: "E", deliveryTime: "10 work days" },
]);
const [edit, setEdit] = useState(false);
const [rowValues, setRowValues] = useState({
price: 5,
description: "",
deliveryTime: "",
});
const handleRowRemove = (itemToRemove) => {
setTableRows(tableRows.filter((item) => item !== itemToRemove));
};
const handelRowAdd = () => {
setTableRows([...tableRows, { ...rowValues }]);
console.log(tableRows);
};
const handleEdit = (i, index) => {
//
};
return (
<section>
<table className="table w-full text-white ">
<thead className="">
<tr>
<th>Price</th>
<th>Description</th>
<th>Delivery time</th>
<th>Edit/Delete</th>
</tr>
</thead>
<tbody>
{tableRows.map((i) => (
<tr>
{edit ? (
<>
<td>IN EDIT MODE</td>
<td>IN EDIT MODE</td>
<td>IN EDIT MODE</td>
</>
) : (
<>
<td>{i.price}</td>
<td>{i.description}</td>
<td>{i.deliveryTime}</td>
</>
)}
<td>
<div className="flex">
{edit ? (
<button type="button" onClick={() => setEdit(false)}>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</button>
) : (
<>
<button
className="pr-4 tooltip"
data-tip="Edit"
onClick={() => setEdit(true)}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"
/>
</svg>
</button>
<button
className="tooltip"
data-tip="Remove"
onClick={() => handleRowRemove(i)}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
</button>
</>
)}
</div>
</td>
</tr>
))}
<tr>
<td>
<input
type="text"
className="input bg-indigo-500 focus:bg-indigo-500 placeholder-gray-200"
placeholder="Price"
value={rowValues.price}
onChange={(e) =>
setRowValues({ ...rowValues, price: e.target.value })
}
/>
</td>
<td>
<input
type="text"
className="input bg-indigo-500 focus:bg-indigo-500 placeholder-gray-200"
placeholder="Description"
value={rowValues.description}
onChange={(e) =>
setRowValues({ ...rowValues, description: e.target.value })
}
/>
</td>
<td>
<input
type="text"
className="input bg-indigo-500 focus:bg-indigo-500 placeholder-gray-200"
placeholder="Delivery Time"
value={rowValues.deliveryTime}
onChange={(e) =>
setRowValues({ ...rowValues, deliveryTime: e.target.value })
}
/>
</td>
<td>
{" "}
<button onClick={handelRowAdd} type="button">
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-10 w-10"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</button>
</td>
</tr>
</tbody>
</table>
</section>
);
};
export default Pricing;
Any help would be appreciated. Thanks.
CodePudding user response:
I would recommend you extract the contents of your map function into a separate functional component and handle the edit flag within that. Along the lines of
const Row = ({ rowData, onEditComplete }) => {
const [edit, setEdit] = useState(false);
return (
<tr>
// ...etc
<td>{rowData.price}</td>
// ...etc
</tr>
);
}
Then in your Pricing function it becomes
{tableRows.map((i) => (
<Row
rowData={i}
onEditComplete={() => {
//use this to pass edited row data back to the parent
}
/>
)}
CodePudding user response:
Get the index from the map tableRows.map((i, index) =>
Add a new state to keep track of the edit index.
onClick={() => {setEditIndex(index);
setEdit(true)}}
Check if the row is the edit row.
{edit && editIndex === index ? (
<>
<td>IN EDIT MODE</td>
<td>IN EDIT MODE</td>
<td>IN EDIT MODE</td>
</>
) : (
<>
<td>{i.price}</td>
<td>{i.description}</td>
<td>{i.deliveryTime}</td>
</>
)}