I have an array of objects with the following format
const [type1Options, setType1Options] = useState([
{
name: "Name1",
value: "Value1",
},
{
name: "Name2",
value: "Value2",
},
]);
const [type2Options, setType2Options] = useState([
{
name: "Name1",
value: "Value1",
},
{
name: "Name2",
value: "Value2",
},
]);
I am rendering these objects category wise with copy and delete buttons per entry. Delete will delete the entry from the array, and copy will copy the clicked items content into a new entry and placed right below the clicked entry. Delete works just fine, but copy on the last entry doesn’t work as expected. Can someone help?
Sandbox: https://codesandbox.io/p/sandbox/i18-demo-7594gf?file=%2Fsrc%2FApp.js%3A5%2C2-24%2C6
Utils for copy and delete functions
export const deleteItems = (list, idx) => {
const temp = [...list];
temp.splice(idx, 1);
return temp;
};
export const copyItems = (list, idx) => {
const newItem = { ...list[idx] };
const newItems = [...list.slice(0, idx + 1), newItem, ...list.slice(idx + 1)];
return newItems;
};
import { useState } from "react";
import List from "./List";
export default function App() {
const [type1Options, setType1Options] = useState([
{
name: "Name1",
value: "Value1",
},
{
name: "Name2",
value: "Value2",
},
]);
const [type2Options, setType2Options] = useState([
{
name: "Name1",
value: "Value1",
},
{
name: "Name2",
value: "Value2",
},
]);
return (
<div>
<List
type1Options={type1Options}
type2Options={type2Options}
setType1Options={setType1Options}
setType2Options={setType2Options}
/>
</div>
);
}
import Type1 from "./Type1";
import Type2 from "./Type2";
export default function List(props) {
const { type1Options, type2Options, setType1Options, setType2Options } =
props;
return (
<>
<div>
Category 1
{type1Options.map((obj, index) => (
<Type1
index={index}
obj={obj}
type1Options={type1Options}
setType1Options={setType1Options}
/>
))}
</div>
<br />
<div>
Category 2
{type2Options.map((obj, index) => (
<Type2
index={index}
obj={obj}
type2Options={type2Options}
setType1Options={setType2Options}
/>
))}
</div>
</>
);
}
import "./styling.css";
import { deleteItems, copyItems } from "./utils";
export default function Type1(props) {
const { index, obj, type1Options, setType1Options } = props;
const copyHandler = () => setType1Options(copyItems(type1Options, index + 1));
const deleteHandler = (index) =>
setType1Options(deleteItems(type1Options, index + 1));
return (
<div className="box-container">
<div className="box-header">
<h3>Index {index + 1}</h3>
<div className="box-buttons">
<button onClick={copyHandler}>Copy</button>
<button onClick={deleteHandler}>Delete</button>
</div>
</div>
<div className="box-content">
{obj.name}: {obj.value}
</div>
</div>
);
}
2
Answers
Issue was the mismatch of 1 and 2 in type1Options and type2Options in 2 files. here is the updated code.
List.jsx
and type2.jsx
Issues
The
Type2
component is passingindex + 1
to the utility functions and copying/deleting the wrong array element.The
Type1
andType2
components fail to pass the array index to thedelete
utility function, they instead are passing theonClick
event object and the wrong element is removed from the source array.Solution/Suggestions
onClick
event object through to utility function.Additional tips/fixes/etc
id
property to the options to uniquely identify them, especially since you are mutating the array by adding and removing elements. This should help avoid any React rendering issues.Type2
where the setter is named like it’s theType1
component setter, i.e.setType1Options
instead ofsetType2Options
. There is no bug here, but the naming convention could be confusing to future readers/maintainers of your code.App.jsx
utils.ts
List.jsx
Type1.jsx
Type2.jsx