React displaying data in a table with dynamic columns

Viewed 101

I'm building a table that is to display the user's hours associated to a particular day of the month. Since each month has different days, the columns are set dynamically. I've got the structure of it fine, with the table head displaying the appropriate number of days depending on the month and year selected. I have the data being pulled by the api and being displayed, however I'm at a total loss as how to match the dates from the data to the days in the columns.

I've made a simplified codesandbox which shows where I'm at: https://codesandbox.io/s/react-table-dynamic-columns-mr4u6w?file=/src/App.js:0-1890

To further complicate it, the data I'm working with on the actual app has 2 date objects, a start and end time. Generally the hours should not cross days, but that could happen. For this table I figure I can get away with pulling the date from one of the date objects (which is why the sandbox has only a start field).

I've considered trying to implement something like

setDataDates(
   const dataDay = new Date(data.start).getDate()

to get the day of the visit, but I'm totally lost on how to then provide that to the correct day on the table. Any guidance would be greatly appreciated, thanks!

UPDATE So I think I'm close, and it should be rendering at least, but sandbox is giving the error: Objects are not valid as a React child (found: object with keys {day}), where I'm mapping the days in the table header. This part has been functioning fine before, so I'm lost as to why its what is broken now... (this aspect has been fixed, because I made the array {key: value}, I forgot to call it in the .map as {day.day}. It is updated below

The actual functionality is still not there however

import "./styles.css";
import React from "react";

export default function App() {
  const data = [
    {
      start: "2022-04-02",
      hours: 2
    },
    {
      start: "2022-04-06",
      hours: 3
    },
    {
      start: "2022-04-11",
      hours: 1
    }
  ];
  const [newData, setNewData] = React.useState();

  const setDataforTable = () => {
    const oldData = data.map((data) => ({
      day: new Date(data.start).getDate(),
      hours: data.hours
    }));
    console.log("newData", oldData);
    setNewData(oldData);
  };

  const [days, setDays] = React.useState([]);

  const settingDays = () => {
    const daysInMonth = 30;

    let dayInfo = [];
    for (let i = 1; i <= daysInMonth; i++) {
      dayInfo.push(i);

     let result = dayInfo.map((i) => (
       {
        day: i
      }
      ));
      console.log("day ", result);
      setDays(result);
      
    }
  };

  const totalHours = data.map((i) => i.hours).reduce((a, b) => a + b, 0);

  let tableDisplay;


  if (days !== []) {
    
    tableDisplay = (
      <div>
        <table>
          <thead>
            <tr>
              <th> Date </th>
              {days && days.map((day) => <th key={day}>{day.day}</th>)}
              <th>Total Hours</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>Hours</td>
              {newData && newData.map((d) => 
              <td key={d.day}>{d.day === days.day ? d.hours : 0}</td>
              )}
              <td>{totalHours}</td>
            </tr>
          </tbody>
        </table>
      </div>
    );
  }

  return (
    <div>
      <button
        onClick={() => {
          setDataforTable(), settingDays();
        }}
      >
        Click
      </button>
      {tableDisplay}
    </div>
  );
}
1 Answers

So, here's the working solution and you could achieve what you wanted by making some changes. This solution is not fully optimized but you'll get the idea.

So, in order to get the results you'll have to create an new array of data with actual date number so we could use it later for filtering the dayArr. After that you'll have to map over your day array of object and check if the day matches with day inside data array if it matches then replace the hours.

import "./styles.css";
import React from "react";

export default function App() {
  const data = [
    {
      start: "2022-04-02",
      hours: 2
    },
    {
      start: "2022-04-06",
      hours: 3
    },
    {
      start: "2022-04-11",
      hours: 1
    }
  ];


  // filter the data so we can have actual day number
  const filteredData = data.map((item) => {
    const splitDate = item.start.split("-");
    const day = splitDate[splitDate.length - 1];

    return {
      start: parseInt(day),
      hours: item.hours
    };
  });


  const [newData, setNewData] = React.useState();

  const setDataforTable = () => {
    const dataDays = data.map((data) => ({
      day: new Date(data.start).getDate(),
      hours: data.hours
    }));

    setNewData(dataDays);
  };

  const [days, setDays] = React.useState([]);

  const settingDays = () => {
    const daysInMonth = 30;

    let dayInfo = [];
    for (let i = 1; i <= daysInMonth; i++) {
      dayInfo.push(i)

      // create an array and fill hours with 0
      const dayArr = dayInfo.map((day) => ({
        day: day,
        hours: 0
      }));


      // map over dayArr and replace the hours 
      let temp = [...dayArr];
      dayArr.map((i, idx) => {
        const getHours = filteredData.filter((item) => item.start === i.day);
        if (getHours.length) {
          const newData = {
            day: i.day,
            hours: getHours[0].hours
          };
          temp[idx] = newData;
        }
      });

      setDays(temp);
    }
  };

  const totalHours = data.map((i) => i.hours).reduce((a, b) => a + b, 0);

  let tableDisplay;


  if (days.length) {
    //convertData();
    tableDisplay = (
      <div>
        <table>
          <thead>
            <tr>
              <th> Date </th>
              {days.map((i) => (
                <th key={i}>{i?.day}</th>
              ))}
              <th>Total Hours</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>Hours</td>
              {/* {newData &&
                
              } */}
              {days && days.map((d) => <td>{d.hours}</td>)}
              <td>{totalHours}</td>
            </tr>
          </tbody>
        </table>
      </div>
    );
  }

  return (
    <div>
      <button
        onClick={() => {
          setDataforTable();
          settingDays();
        }}
      >
        Click
      </button>
      {tableDisplay}
    </div>
  );
}
Related