React - Add data to a list

Viewed 245

I have two mapped lists of items. When I click on the div using onClick={(e) => handleCheckClick(ele)} I want to display the clicked items in a table but I am getting errors.

handleCheckClick function

 function handleCheckClick(ele) {
 if (trayItems?.length > 0) {
     settrayItems(
       trayItems.map((item) => {
         return { ...item, quantity: item.quantity + 1 };
       })
     );
   } else {
     settrayItems([...trayItems, ele]);
   }
 }

How I map two list items into div

<div>
{
    item && item.filter((person) => person.status == "0").map((ele) => {
        return (
            <div
                className="newmo-card"
                style={styles.innerbox}
                onClick={(e) => handleCheckClick(ele)}>
                {`${ele.item}`}
                <br />
                <span> {`${ele.total_price}`}</span>
            </div>

        );
    })
}
</div>
<Tray trayItems2={trayItems} trayItems1={trayPrice} /> 

Table of clicked items:

  function Tray({ trayItems }) {
    return (
      <>
      <div className="foo">
        <table>
          {trayItems &&
                trayItems.map((ele, index) => {
                  return (
                    <tr key={index}>
                      <td>{ele.item}</td>
                      <td>{ele.price}</td>
                      <td>{ele.quantity}</td>
                    </tr>
         
        </table>
      </div>
    </>
  );
}

Check the sandbox so you can identify the issue

6 Answers

here why do you manage two diff states? pls check the working demo here.

import { Fragment, useState } from "react";
import "./styles.css";
const item = [
  {
    user_id: 1,
    item: "biriani",
    price: "50",
    selling_price: "60",
    created_at: "2022-08-29T10:12:58.000000Z",
    updated_at: "2022-09-15T06:17:20.000000Z",
    tax: "5%",
    total_price: "80",
    status: 1,
    quantity: 0

  },
  {
    user_id: 5,
    item: "alfarm",
    price: "100",
    selling_price: "120",
    created_at: "2022-09-07T11:06:23.000000Z",
    updated_at: "2022-09-07T11:06:23.000000Z",
    tax: "5%",
    total_price: "140",
    status: 0,
    quantity: 0
  }
];
export default function App() {
  const [trayItems, settrayItems] = useState([]);

  const handleCheckClick = (ele) => {
    if (trayItems?.length > 0) {
      settrayItems(trayItems.map((item) => {
        return { ...item, quantity: item.quantity + 1 };
      }))
    } else {
      settrayItems([...trayItems, ele]);
    }
  };

  function Tray({ trayItems }) {
    return (
      <>
        <div className="foo">
          <table>
            <tbody>
              {trayItems &&
                trayItems.map((ele, index) => {
                  return (
                    <tr key={index}>
                      <td>{ele.item}</td>
                      <td>{ele.price}</td>
                    </tr>
                  );
                })}
            </tbody>
          </table>
        </div>
      </>
    );
  }

  return (
    <>
      <div>
        {item &&
          item
            .filter((person) => person.status === 0)
            .map((ele, index) => {
              return (
                <div
                  key={index}
                  className="newmo-card"
                  onClick={() => handleCheckClick(ele)}
                >
                  {`${ele.item}`}
                  <br />
                  <span> {`${ele.total_price}`}</span>
                </div>
              );
            })}
      </div>
      <Tray trayItems={trayItems} />
    </>
  );
}


There is a warning that <td> and <tr> cannot appear as a child of <table>.

Wrap body with <body> tag and use <tr> tag to represent each row.

<div className="foo">
  <table>
    <tbody>
      {trayItems2.length === trayItems1.length &&
        trayItems2.map((ele, index) => {
          return (
            <tr key={ele}>
              <td>{ele}</td>
              <td>{trayItems1[index]}</td>
            </tr>
          );
        })}
    </tbody>
  </table>
</div>

Because of using trayItems1[index] to get map items in another array so you need to pre-check them. You need to refine it for tighter conditions.

Also, define unique key props for each child element in <table>. Here I simply use key={ele}. And using two arrays is redundant. Merge them could be easier.

I'm sharing the significant parts of the code here. Check the code sandbox for the working code.

Here are the significant changes:

  1. Moved the Tray definition outside the App component. Defining a component inside another component is almost always a bad idea. You won't have an issue because you don't have any state in Tray. If you had any state, it would have been reset to the initial value on every render of App.
  2. Changed the trayItems to an object. The keys are the ids of each item. And the value is an object that contains the name, quantity, and price.
  3. We'll only render the item in the Tray if the quantity is greater than 0.
  4. Changed the handleCheckClick to handle the object.
function Tray({ trayItems }) {
  return (
    <>
      <div className="foo">
        <table>
          <tbody>
            {Object.entries(trayItems)
              .filter(([, v]) => v.quantity > 0)
              .map(([k, v]) => {
                return (
                  <tr key={k}>
                    <td>{v.item}</td>
                    <td>{v.price}</td>
                    <td>{v.quantity}</td>
                  </tr>
                );
              })}
          </tbody>
        </table>
      </div>
    </>
  );
}

export default function App() {
  const [trayItems, setTrayItems] = useState(
    item.reduce((prev, curr) => {
      return {
        ...prev,
        [curr.user_id]: { item: curr.item, quantity: 0, price: curr.price }
      };
    }, {})
  );

  const handleCheckClick = (ele) => {
    setTrayItems((prev) => {
      return {
        ...prev,
        [ele.user_id]: {
          ...prev[ele.user_id],
          quantity: prev[ele.user_id].quantity + 1
        }
      };
    });
  };

  return (
    <>
      <div>
        {item &&
          item
            .filter((person) => person.status === 0)
            .map((ele, index) => {
              return (
                <div
                  key={index}
                  className="newmo-card"
                  onClick={() => handleCheckClick(ele)}
                >
                  {`${ele.item}`}
                  <br />
                  <span> {`${ele.total_price}`}</span>
                </div>
              );
            })}
      </div>
      <Tray trayItems={trayItems} />
    </>
  );

Edit intelligent-brahmagupta-qchf18

  1. The method (find) was added to compare the id with the trayItems array using the selection id, if it is the same it does not make changes, but if it is different a new array is created, this is done to avoid duplicates and add ele. quantity = 1

  2. Two buttons were created, one to increase and one to decrease, Both in the incre and decre functions the method (map) was added, within the method the id is compared with the id of the selection, if it is equal, it is added or subtracted, depending on the selection of the button, if it is not , is still the same number.

https://codesandbox.io/s/fast-currying-nfmxfm?file=/src/App.js

  export default function App() {
      const [trayItems, settrayItems] = useState([]);
     
      const handleCheckClick = (ele) => {
        // 1.
        const dupe = trayItems.find((obj) => obj.user_id === ele.user_id);
        settrayItems(
          dupe
            ? trayItems
            : [...trayItems, { ...ele, quantity: (ele.quantity = 1) }]
        );
      };
   
  // 2.
  const incre = (idd) => {
    settrayItems(
      trayItems.map((stat) =>
        stat.user_id === idd ? { ...stat, quantity: stat.quantity + 1 } : stat
      )
    );
  };

  // 2.
  const decre = (idd) => {
    settrayItems(
      trayItems.map((stat) =>
        stat.user_id === idd
          ? {
              ...stat,
              quantity:
                stat.quantity !== 1 ? stat.quantity - 1 : (stat.quantity = 1)
            }
          : stat
      )
    );
  };

  function Tray({ trayItems }) {
    return (
      <>
        <div className="foo">
          <table>
            <tbody>
              {trayItems &&
                trayItems.map((ele, index) => {
                  return (
                    <tr key={index}>
                      <td>{ele.item}</td>
                      <td>{ele.price}</td>
                      <td>{ele.quantity}</td>
                      <td>
                        <button onClick={() => incre(ele.user_id)}>+</button>
                        <button onClick={() => decre(ele.user_id)}>-</button>
                      </td>
                    </tr>
                  );
                })}
            </tbody>
          </table>
        </div>
      </>
    );
  }

  return (
    <>
      <div>
        {item &&
          item
            .filter((person) => person.status === 0)
            .map((ele, index) => {
              return (
                <div
                  style={{ cursor: "pointer" }}
                  key={index}
                  className="newmo-card"
                  onClick={() => handleCheckClick(ele)}
                >
                  {`${ele.item}`}
                  <br />
                  <br />
                  <span> {`${ele.total_price}`}</span>
                </div>
              );
            })}
      </div>
      <Tray trayItems={trayItems} />
    </>
  );
}

There is an issue with your handleCheckClick function

const handleCheckClick = (ele) => {
let index = trayItems.findIndex((item) => item.user_id === ele.user_id);
if (index >= 0) {
  let newItems = trayItems;
  let item = trayItems.find((item) => item.user_id === ele.user_id);
  item = { ...item, quantity: item.quantity + 1 };

  newItems.splice(index, 1, item);

  settrayItems([...newItems]);
} else {
  settrayItems([...trayItems, ele]);
}
};

For more details Check the sandbox

The issue in your code was how you handled the click function and the modification of state subsequently.

  1. Removed duplicate products (having same id)
const items = [
  {
    user_id: 1,
    item: "biriani",
    price: "50",
    selling_price: "60",
    created_at: "2022-08-29T10:12:58.000000Z",
    updated_at: "2022-09-15T14:05:45.000000Z",
    tax: "5%",
    total_price: "80",
    status: 0,
    quantity: 0
  },
  {
    user_id: 2,
    item: "burger",
    price: "80",
    selling_price: "80",
    created_at: "2022-08-29T10:21:13.000000Z",
    updated_at: "2022-09-14T04:46:36.000000Z",
    tax: "5%",
    total_price: "100",
    status: 0,
    quantity: 0
  },
  {
    user_id: 3,
    item: "alfarm",
    price: "100",
    selling_price: "120",
    created_at: "2022-09-07T11:06:23.000000Z",
    updated_at: "2022-09-07T11:06:23.000000Z",
    tax: "5%",
    total_price: "140",
    status: 0,
    quantity: 0
  },
  {
    user_id: 4,
    item: "sandwich",
    price: "100",
    selling_price: "120",
    created_at: "2022-09-07T13:27:33.000000Z",
    updated_at: "2022-09-14T04:46:37.000000Z",
    tax: "5",
    total_price: "140",
    status: 0,
    quantity: 0
  },
  {
    user_id: 5,
    item: "pizza",
    price: "200",
    selling_price: "220",
    created_at: "2022-09-07T13:27:33.000000Z",
    updated_at: "2022-09-07T13:27:33.000000Z",
    tax: "5",
    total_price: "240",
    status: 0,
    quantity: 0
  },
  {
    user_id: 6,
    item: "chicken sadwich",
    price: "200",
    selling_price: "220",
    created_at: "2022-09-07T13:27:33.000000Z",
    updated_at: "2022-09-07T13:27:33.000000Z",
    tax: "5",
    total_price: "250",
    status: 0,
    quantity: 0
  },
  {
    user_id: 7,
    item: "sharja shake",
    price: "60",
    selling_price: "70",
    created_at: "2022-09-09T06:58:45.000000Z",
    updated_at: "2022-09-15T14:05:53.000000Z",
    tax: "5%",
    total_price: "80",
    status: 0,
    quantity: 0
  }
];
  1. Kept single variable in state for products & updated state accordingly
export default function App() {
  const [trayItems, setTrayItems] = useState([]);

  const handleCheckClick = (item) => {
    const itemExists = trayItems.findIndex((ite) => ite.user_id === item.user_id) !== -1;
    if (itemExists) {
      setTrayItems(
        trayItems.map((prod) => ({
          ...prod,
          quantity:
            prod.user_id === item.user_id ? prod.quantity + 1 : prod.quantity
        }))
      );
    } else {
      setTrayItems([...trayItems, { ...item, quantity: item.quantity + 1 }]);
    }
  };

  return (
    <>
      <table>
        {items
          ?.filter((item) => item.status === 0)
          ?.map((item, index) => {
            return (
              <tbody
                key={index}
                onClick={() => handleCheckClick(item)}
              >
                <td>{item.item}</td>
                <td>{item.total_price}</td>
              </tbody>
            );
          })}
      </table>
      <Tray trayItems={trayItems} />
    </>
  );
}
  1. Moved the child component outside parent.
const Tray = ({ trayItems }) => {
  return (
    <table>
      <tbody>
          {trayItems?.map((ele, index) => (
            <tr key={index}>
              <td>{ele.item}</td>
              <td>{ele.price}</td>
              <td>{ele.quantity}</td>
            </tr>
          ))}
      </tbody>
    </table>
  );
};

Related