React JS custom table – handling select dropdown in table

Tags: ,



A bit of context for this table – I want to create a table where user can select an option in the dropdown on the table header and compare. So this table has two columns and each column represents the data that’s been selected.

What I want to achieve is when one is selected say business in column one, how can I disable business / prevent it from being selected in column 2? As it doesn’t make sense to compare two of the same ones.

Also is there a better way to handle the onChange for the two select dropdowns? At the moment I have two methods and they are quite similar. I’ve tried to have only one onChange method but was not able to prevent both from changing when one is selected.

Here’s what I’ve done so far my table:

import React, { useEffect, useState } from "react";
import "./styles.css";

const Table = () => {
  const [plan, setPlan] = useState("business");
  const [planToCompare, setPlanToCompare] = useState("pro");

  const handleChange = (e) => {
    e.preventDefault();
    setPlan(e.target.value);
  };

  const handleChange2 = (e) => {
    e.preventDefault();
    setPlanToCompare(e.target.value);
  };

  const data = [
    {
      business: ["Lorem", "Ipsum", "Dolor sit amet"],
      pro: ["Lorem 1", "Ipsum 2", "Dolor sit amet 3"],
      free: ["Lorem 4", "Ipsum 5", "Dolor sit amet 6"]
    }
  ];

  const renderData = (plan1, plan2) => {
    const arr = data[0][plan1];
    const arr2 = data[0][plan2];

    return arr.map((item, index) => (
      <tr key={`plan_${index + 1}`}>
        <td>{item}</td>
        <td>{arr2[index]}</td>
      </tr>
    ));
  };

  return (
    <div className="container">
      <table>
        <thead>
          <tr>
            <th>
              <select datachosen={plan} onChange={(e) => handleChange(e)}>
                <option value={"business"}>Business</option>
                <option value={"pro"}>Pro</option>
                <option value={"free"}>Free</option>
              </select>
            </th>
            <th>
              <select
                id={"select2"}
                datachosen={planToCompare}
                onChange={(e) => handleChange2(e)}
              >
                <option value={"pro"}>Pro</option>
                <option value={"free"}>Free</option>
                <option value={"business"}>Business</option>
              </select>
            </th>
          </tr>
        </thead>
        <tbody>{renderData(plan, planToCompare)}</tbody>
      </table>
    </div>
  );
};

export default Table;

Here’s a working codesandbox for a full view

Answer

You need to keep the dropdown data in a variable, but as you initiating it in const data = .. it will recreates every time when the components get rendered, so you need to keep it in a useState or use useMemo

useState: this is good if you want to change the data, for ex: add another option

const [data, setData] = useState([
  {
    business: ["Lorem", "Ipsum", "Dolor sit amet"],
    pro: ["Lorem 1", "Ipsum 2", "Dolor sit amet 3"],
    free: ["Lorem 4", "Ipsum 5", "Dolor sit amet 6"]
  }
]);

useMemo: this is good if you are not willing to change the options.

const data = useMemo(() => {
  return [
    {
      business: ["Lorem", "Ipsum", "Dolor sit amet"],
      pro: ["Lorem 1", "Ipsum 2", "Dolor sit amet 3"],
      free: ["Lorem 4", "Ipsum 5", "Dolor sit amet 6"]
    }
  ];
}, []);

and then I need two variables to hold the options for each dropdowns

// used useMemo because i want to update my planDropDownOptions only if data or planToCompare values gets changed
const planDropDownOptions = useMemo(() => {
  return Object.keys(data[0]).filter((option) => option !== planToCompare);
}, [data, planToCompare]);

// used useMemo because i want to update my planToCompareDropDownOptions only if data or plan values gets changed
const planToCompareDropDownOptions = useMemo(() => {
  return Object.keys(data[0]).filter((option) => option !== plan);
}, [data, plan]);

now when we rendering the options it should be like

<select datachosen={plan} onChange={(e) => handleChange(e)}>
  {planDropDownOptions.map((option) => {
    return (
      <option key={option} value={option}>
        {option}
      </option>
    );
  })}
</select>

here is the demo



Source: stackoverflow