I'm still new to React, and functional programming, and Javascript, and JSX, so go easy if this is a stupid question.
I'm modifying one of the example material-ui tables from react-table v7. The original code can be found here. The example is completely functional and is using React Hooks as opposed to classes, as do all of the components of the template I'm using (shout out to creative-tim.com!)
My parent function (representative of a page in my dashboard application), for instance Users.js or Stations.js fetches data from a backend api inside a useEffect hook. That data is then passed as a prop to my subcomponent ReactTables.js
For some reason ReactTables.js does not receive changes to the "data" prop after the parent page's useEffect finishes. However, once I modify the data from a subcomponent of ReactTables (in this case AddAlarmDialog.js) then the table re-renders and all of my data suddenly appears.
How can I trigger the re-render of my subcomponent when data is returned from the parent component's useEffect? I noticed that in older versions of React there was a lifecycle function called componentWillReceiveProps(). Is this the behavior I need to emulate here?
Example Parent Component (Alarms.js):
import React, { useEffect, useState } from "react";
// @material-ui/core components
// components and whatnot
import GridContainer from "components/Grid/GridContainer.js";
import GridItem from "components/Grid/GridItem.js";
import ReactTables from "../../components/Table/ReactTables";
import { server } from "../../variables/sitevars.js";
export default function Alarms() {
const [columns] = useState([
{
Header: "Alarm Name",
accessor: "aName"
},
{
Header: "Location",
accessor: "aLocation"
},
{
Header: "Time",
accessor: "aTime"
},
{
Header: "Acknowledged",
accessor: "aAcked"
},
{
Header: "Active",
accessor: "aActive"
}
]);
const [data, setData] = useState([]);
const [tableType] = useState("");
const [tableLabel] = useState("Alarms");
useEffect(() => {
async function fetchData() {
const url = `${server}/admin/alarms/data`;
const response = await fetch(url);
var parsedJSON = JSON.parse(await response.json());
var tableElement = [];
parsedJSON.events.forEach(function(alarm) {
tableElement = [];
parsedJSON.tags.forEach(function(tag) {
if (alarm.TagID === tag.IDX) {
tableElement.aName = tag.Name;
}
});
tableElement.aTime = alarm.AlarmRcvdTime;
parsedJSON.sites.forEach(function(site) {
if (site.IDX === alarm.SiteID) {
tableElement.aLocation = site.Name;
}
});
if (alarm.Active) {
tableElement.aActive = true;
} else {
tableElement.aActive = false;
}
if (!alarm.AckedBy && !alarm.AckedTime) {
tableElement.aAcked = false;
} else {
tableElement.aAcked = true;
}
//const newData = data.concat([tableElement]);
//setData(newData);
data.push(tableElement);
});
}
fetchData().then(function() {
setData(data);
});
}, [data]);
return (
<div>
<GridContainer>
<GridItem xs={12} sm={12} md={12} lg={12}>
<ReactTables
data={data}
columns={columns}
tableType={tableType}
tableLabel={tableLabel}
></ReactTables>
</GridItem>
</GridContainer>
</div>
);
}
Universal Table Subcomponent (ReactTables.js):
import React, { useState } from "react";
// @material-ui/core components
import { makeStyles } from "@material-ui/core/styles";
// @material-ui/icons
import Assignment from "@material-ui/icons/Assignment";
// core components
import GridContainer from "components/Grid/GridContainer.js";
import GridItem from "components/Grid/GridItem.js";
import Card from "components/Card/Card.js";
import CardBody from "components/Card/CardBody.js";
import CardIcon from "components/Card/CardIcon.js";
import CardHeader from "components/Card/CardHeader.js";
import { cardTitle } from "assets/jss/material-dashboard-pro-react.js";
import PropTypes from "prop-types";
import EnhancedTable from "./subcomponents/EnhancedTable";
const styles = {
cardIconTitle: {
...cardTitle,
marginTop: "15px",
marginBottom: "0px"
}
};
const useStyles = makeStyles(styles);
export default function ReactTables(props) {
const [data, setData] = useState(props.data);
const [columns] = useState(props.columns);
const [tableType] = useState(props.tableType);
const [skipPageReset, setSkipPageReset] = useState(false)
const updateMyData = (rowIndex, columnId, value) => {
// We also turn on the flag to not reset the page
setData(old =>
old.map((row, index) => {
if (index === rowIndex) {
return {
...old[rowIndex],
[columnId]: value
};
}
return row;
})
);
};
const classes = useStyles();
return (
<GridContainer>
<GridItem xs={12}>
<Card>
<CardHeader color="primary" icon>
<CardIcon color="primary">
<Assignment />
</CardIcon>
<h4 className={classes.cardIconTitle}>{props.tableLabel}</h4>
</CardHeader>
<CardBody>
<EnhancedTable
data={data}
columns={columns}
tableType={tableType}
setData={setData}
updateMyData={updateMyData}
skipPageReset={skipPageReset}
filterable
defaultPageSize={10}
showPaginationTop
useGlobalFilter
showPaginationBottom={false}
className="-striped -highlight"
/>
</CardBody>
</Card>
</GridItem>
</GridContainer>
);
}
ReactTables.propTypes = {
columns: PropTypes.array.isRequired,
data: PropTypes.array.isRequired,
tableType: PropTypes.string.isRequired,
tableLabel: PropTypes.string.isRequired,
updateMyData: PropTypes.func,
setData: PropTypes.func,
skipPageReset: PropTypes.bool
};
**For the record: if you notice superfluous code in the useEffect it's because I was messing around and trying to see if I could trigger a re-render.