Search code examples
javascriptreactjsecmascript-6

Unable to pass values from one component to a table component in ReactJS application


I just want to transfer the data from one component to a table component. So after selecting the option from the dropdown menu, it should reflect all the details of that selected option inside the table. I have made two components:

Devdropdown.jsx:

import React, { useState, useEffect, useMemo } from "react";
import Tables from "./Tables";

const Devdropdown = () => {
  const [devices, setDevices] = useState([{}]);
  const [selected, setSelected] = useState();
  const url = "http://localhost:3000/devices";

  useEffect(() => {
    async function getDevices() {
      const response = await fetch(url);
      const body = await response.json();
      setDevices(body.data);
    }
    getDevices();
  }, []);

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

  const uniqueDeviceIds = useMemo(
    () => Array.from(new Set(devices.map((device) => device.device_id))),
    [devices]
  );

  console.log(devices);
  return (
    <div>
      <h1>Aircraft {selected}</h1>
      <select
        value={selected}
        onChange={handleChange}
        placeholder="select an option"
      >
        {devices
          ? uniqueDeviceIds.map((deviceId) => (
              <option key={deviceId}>{deviceId}</option>
            ))
          : null}
      </select>
      <Tables data={devices[selected]} />
    </div>
  );
};

export default Devdropdown;

Tables.jsx:

import React from "react";

const Tables = (props) => {
  return (
    <div>
      <table>
        <tr>
          <th>Timestamp</th>
          <th>Location</th>
        </tr>
        <tr>
          <td>{props.data}</td>
          <td>{props.data}</td>
        </tr>
      </table>
    </div>
  );
};

export default Tables;

I just want to reflect the timsetamp and location features of the selected element , inside the table. Below is the screenshot of the devices object where I want timestamp and location attributes inside the table component.

enter image description here

Thanks and regards


Solution

  • You can create a Map to group by your device_id, this will allow you to populate your dropdown with unique options by getting the keys of the map (rather than using a Set), and also allow you to associate the selected option with an array of devices that all have the same device_id:

    const deviceMap = useMemo(() => {
        const grouped = new Map();
        for (const device of devices) {
          const currDeviceArr = grouped.get(device.device_id) ?? [];
          currDeviceArr.push(device); // add the current device to the associated device_id array
          grouped.set(device.device_id, currDeviceArr);
        }
        return grouped;
      }
      [devices]
    );
    
    return (
        <div>
          <h1>Aircraft {selected}</h1>
          <select
            value={selected}
            onChange={handleChange}
            placeholder="select an option"
          >
            {devices
              ? Array.from(deviceMap.keys(), (device_id) => (
                  <option key={device_id}>{device_id}</option>
                ))
              : null}
          </select>
          <Tables data={deviceMap.get(selected)} />
        </div>
    );
    

    Then you can update your Table component to map over the devices with the selected device_id:

    const Tables = ({ data = []}) => {
      return (
        <div>
          <table>
            <tr>
              <th>Timestamp</th>
              <th>Location</th>
            </tr>
            {
              data.map((device, i) => <tr key={i}>
                <td>{device.timestamp}</td>
                <td>{device.location}</td>
              </tr>)
            }
          </table>
        </div>
      );
    };
    

    Another approach would be to stick the Devdropdown component that you have now, and then filter the devices array out based on the selected option, but this isn't as efficient as you now looping over devices multiple times:

    <Tables data={devices.filter(device => device.device_id === selected)} />
    

    You could also put the above .filter() computation in a useMemo() hook so that it only recomputes the array when devices or selected changes, and not on every rerender. Once you have done this, you can apply the same change to your Tables component as shown above.