Search code examples
javascriptreactjsdom-eventsreact-propsreact-state

Copying an item next to the item does not work accordingly


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>
  );
}

Solution

  • Issue was the mismatch of 1 and 2 in type1Options and type2Options in 2 files. here is the updated code.

    List.jsx

    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}
                setType2Options={setType2Options}
              />
            ))}
          </div>
        </>
      );
    }
    

    and type2.jsx

    import React from "react";
    import "./styling.css";
    import { deleteItems, copyItems } from "./utils";
    
    export default function Type2(props) {
      const { index, obj, type2Options, setType2Options } = props;
    
      const copyHandler = () => setType2Options(copyItems(type2Options, index));
    
      const deleteHandler = () => setType2Options(deleteItems(type2Options, index));
    
      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>
      );
    }