3

I am attempting to create a react-table component using data that is pulled from a database. From the documentation I have read (https://react-table.tanstack.com/docs/quick-start), it seems like the react-table library uses a useMemo hook to create the data which will be displayed on the table. However, I am having trouble actually adding data to the useMemo hook, since I am not familiar with it.

I have a simple JS object that holds the counts of instances of each category of outages that occur in our database. Once I have the counts, I attempt to pass it to my instance of useMemo, however properties of undefined 'streamCount' is returned. I think I am passing the object to useMemo incorrectly. Any help is appreciated.

function Leaderboard(props){
    const data = props.tableData;
    console.log(data); //this is the data that is pulled from our DB and is passed as a prop into the component
    
    let counts = {
      streamCount: 0,
      powerCount: 0,
      internetCount: 0,
      gamingPlatformCount: 0,
      cableCount: 0,
      websiteCount: 0,
    } //the object that holds the number of instances each category occurs in our data

    for(var i = 0; i < data.length; i++){ //checks the data and updates the counts for each category
      switch(data[i].service_type) {
        case "Streaming":
          counts.streamCount += 1;
          break;
        case "Power":
          counts.powerCount+= 1;
          break;
        case "Internet":
          counts.internetCount+= 1;
          break;
        case "Gaming Platform":
          counts.gamingPlatformCount += 1;
          break;
        case "Cable":
          counts.cableCount += 1;
          break;
        case "Website":
          counts.websiteCount += 1;
          break;
        default:
          break;
      }
    }

    console.log(counts) //This returns the correct values of each count when line 41-69 is commented, but returns 0 for all values when those lines are uncommented.

    let outageCounts = React.useMemo(
      (counts) => [
        {
          type: 'Streaming',
          count: counts.streamCount,
        },
        {
          type: 'Power',
          count: counts.powerCount,
        },
        {
          type: 'Internet',
          count: counts.internetCount,
        },
        {
          type: 'GamingPlatform',
          count: counts.gamingPlatformCount,
        },
        {
          type: 'Cable',
          count: counts.cableCount,
        },
        {
          type: 'Website',
          count: counts.websiteCount,
        },
      ],
      []
    );
    
    //this will be updated to have the accessor be 'count' from outageCounts instead of 'service_type' from data when the bug is resolved. For now it is just using data to test to see if the table would render at all.
    const columns = React.useMemo(
        () => [
          {
            Header: 'Service Type',
            accessor: 'service_type',
          },
        ],
        []
    );
    
    const {
        getTableProps,
        getTableBodyProps,
        headerGroups,
        rows,
        prepareRow,
    } = useTable({ columns, data}) //data will eventually be changed to outageCounts
    
    return (
        <table {...getTableProps()} style={{ border: 'solid 1px blue' }}>
          <thead>
            {headerGroups.map(headerGroup => (
              <tr {...headerGroup.getHeaderGroupProps()}>
                {headerGroup.headers.map(column => (
                  <th
                    {...column.getHeaderProps()}
                    style={{
                      borderBottom: 'solid 3px red',
                      background: 'aliceblue',
                      color: 'black',
                      fontWeight: 'bold',
                    }}
                  >
                    {column.render('Header')}
                  </th>
                ))}
              </tr>
            ))}
          </thead>
          <tbody {...getTableBodyProps()}>
            {rows.map(row => {
              prepareRow(row)
              return (
                <tr {...row.getRowProps()}>
                  {row.cells.map(cell => {
                    return (
                      <td
                        {...cell.getCellProps()}
                        style={{
                          padding: '10px',
                          border: 'solid 1px gray',
                          background: 'papayawhip',
                        }}
                      >
                        {cell.render('Cell')}
                      </td>
                    )
                  })}
                </tr>
              )
            })}
          </tbody>
        </table>
    );
  }
export default Leaderboard;

1 Answer 1

2

The useMemo hook's callback function doesn't take any arguments, it simply takes a callback function that returns a value you want, or need, to memoize, the dependency array lists external references that the memoized value depends on.

useMemo

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

Returns a memoized value.

Pass a “create” function and an array of dependencies. useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.

Move the logic for computing the counts into the useMemo callback and use data (the props value) as the dependency. You can simplify/reduce the code to be more DRY by abstracting the common pattern of mapping the service_type to one of the counts keys and then mapping back but just using the service_type as the counts keys. With this change you can simply use dynamic object properties to update the counts for each outage type. When the counts have been computed create an array of key-value pairs from the object and map this to the array of objects with type and count keys.

const outageCounts = React.useMemo(() => {
  const counts = {
    Streaming: 0,
    Power: 0,
    Internet: 0,
    "Gaming Platform": 0,
    Cable: 0,
    Website: 0
  };

  data.forEach(({ service_type }) => {
    if (Object.hasOwnProperty.call(counts, service_type)) {
      counts[service_type] += 1;
    }
  });

  return Object.entries(counts).map(([type, count]) => ({ type, count }));
}, [data]);

function App({ data = [] }) {
  const outageCounts = React.useMemo(() => {
    const counts = {
      Streaming: 0,
      Power: 0,
      Internet: 0,
      "Gaming Platform": 0,
      Cable: 0,
      Website: 0
    };

    data.forEach(({ service_type }) => {
      if (Object.hasOwnProperty.call(counts, service_type)) {
        counts[service_type] += 1;
      }
    });

    return Object.entries(counts).map(([type, count]) => ({ type, count }));
  }, [data]);

  //console.log({ outageCounts });

  return (
    <div className="App">
      <h1>Outage Counts</h1>
      <ul>
        {outageCounts.map(({ type, count}) => (
          <li key={type}>
            {type}: {count}
          </li>
        ))}
      </ul>
    </div>
  );
}

const service_types = [
  "Streaming",
  "Power",
  "Internet",
  "Gaming Platform",
  "Cable",
  "Website"
];

// Generate "random" outage data
const data = Array.from({ length: Math.floor(Math.random() * 1000) }, () => ({
  service_type: service_types[Math.floor(Math.random() * service_types.length)]
}));

const rootElement = document.getElementById("root");
ReactDOM.render(
  <App data={data} />,
  rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root" />

Sign up to request clarification or add additional context in comments.

4 Comments

Maybe a good answer but I understood nothig
@maxkuku What was unclear? What could be improved in my answer to help you?
Thank you! I hardly understand React at all. Your answer is like React docs – all explainations are right but very complicated :) In more simple words if it is possible
@maxkuku Well, depending on your background, React can have a bit of a steep learning curve IMO. Unfortunately an explanation to the specific issue the OP had can't be made much more trivial here than it already is. It sounds like you might have a more specific or different question/issue that you could create a post for. If you create a new post/question feel free to ping me here in a comment with a link to the post and I can take a look when available.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.