Reputation: 353
I'm using the datatables.net library with React to generate nice-looking tables. To get a working example I hard-coded a variable dataSet
and sent it as a prop to the component like this:
import React from "react"
import { TblFunc } from "./TblFunc"
export default function AppFunc() {
const dataSet = [
["site1", "device1"],
["site1", "device2"],
["site1", "device3"],
["site1", "device4"],
["site1", "device5"],
["site1", "device6"],
["site1", "device7"],
["site1", "device8"],
["site1", "device9"],
["site2", "device1"],
["site2", "device2"],
["site2", "device3"],
["site2", "device4"],
["site2", "device5"],
["site2", "device6"],
["site2", "device7"],
["site2", "device8"]
]
return (
<div>
<TblFunc data={dataSet} />
</div>
)
}
Now, instead of hard coding the dataSet
variable, I want to generate it automatically by calling an API. I need to create an Array of Arrays object to mimic dataSet
. The API returns an object like this:
{
"site1": [
"device-a01",
"device-a02",
"device-b01",
"device-b02"
],
"site2": [
"device-a01",
"device-a02",
"device-b01",
"device-b02",
"device-c01",
"device-c02"
]
}
Here is what I have tried:
import React from "react"
import { TblFunc } from "./TblFunc"
export default function AppFunc() {
const [tableData, setTableData] = React.useState([])
function buildTableData(site, device) {
const hubValue = []
hubValue.push(site)
hubValue.push(device)
return hubValue
}
React.useEffect(() => {
async function getData() {
const res = await fetch("https://api.com/api/devices/")
const data = await res.json()
Object.entries(data).forEach(([site, devices]) => {
const _tableData = devices.map(device => buildTableData(site, device))
console.log(_tableData)
setTableData(_tableData)
})
}
getData()
},[])
return (
<div>
{/* <TblFunc data={dataSet} /> // THIS WORKS */}
<TblFunc data={tableData} />
{JSON.stringify(tableData)}
</div>
)
}
When printing tableData
to the screen I get this:
[["site1","device01"],["site1","device02"],["site1","device03"],["site1","device04"],["site1","device05"],["site1","device06"]["site1","device07"]]
I believe I have generated an Array of Array's object but for some reason when I pass it to the <TableFunc />
component it does not populate the table. This leads me to believe that tableData
is not matching the dataSet
object.
This is what <TableFunc />
looks like:
import React, { useEffect, useRef } from "react"
import $ from 'jquery'
export function TblFunc(props) {
$.DataTable = require('datatables.net')
const tableRef = useRef()
useEffect(() => {
// console.log(tableRef.current)
const table = $(tableRef.current).DataTable(
{
data: props.data,
columns: [
{ title: "Site"},
{ title: "Device"}
],
destroy: true // I think some clean up is happening here
}
)
// Extra step to do extra clean-up.
return function() {
console.log("Table destroyed")
table.destroy()
}
},[])
return (
<div>
<table className="display" width="100%" ref={ tableRef }></table>
</div>
)
}
Upvotes: 0
Views: 3208
Reputation: 12956
The table update relies on the jquery code in the useEffect
in your TblFunc
component, but you have passed an empty dependency array so the table is never rebuilt after the first render. Simply add props.data
to the dependency array and the table should update as expected after the fetch completes in the parent's useEffect
.
useEffect(() => {
const table = $(tableRef.current).DataTable(
{
data: props.data,
columns: [
{ title: "Site" },
{ title: "Device" }
],
destroy: true // I think some clean up is happening here
}
)
return function () {
console.log("Table destroyed")
table.destroy()
}
}, [props.data])
// ^^^^^^^^^^ add relevant dependency to trigger rerender
Secondly, you're using forEach
and setting the state at the end of each iteration, so you will only ever have the last entry refactored. You should instead refactor the full Object.entries
array, and then set state.
React.useEffect(() => {
async function getData() {
const res = await fetch("https://api.com/api/devices/")
const data = await res.json()
const _tableData = Object.entries(data).flatMap(([site, devices]) =>
devices.map((device) => [site, device])
);
setTableData(_tableData);
}
getData();
}, []);
The above uses a streamlined refactoring method using flatMap()
with a nested map()
on the inner devices
array.
const data = { "site1": ["device-a01", "device-a02", "device-b01", "device-b02"], "site2": ["device-a01", "device-a02", "device-b01", "device-b02", "device-c01", "device-c02"] };
const _tableData = Object.entries(data).flatMap(([site, devices]) =>
devices.map((device) => [site, device])
);
console.log(_tableData);
Example sandbox.
Upvotes: 1