I have data coming in as a prop like below.
I usually click a button in the parent component to change the prop in the Performancetbl component. But when i click on the button once, table is not rerendering with new data. When I click on it one more time, it rerenders though. Why is that happening?
I tried to save props into a state variable state in Performancetbl component, but that did not change the behavior at all.
I also tried console.log(props.datas) to see if correct data is appearing the first time I click on the button. And it is indeed the correct value! Can you guys figure out why this is happening?
function Performancetbl(props) {
const options = {
...
};
console.log(props.datas)
return(
<div style={{ maxWidth: "100%" }}>
<MaterialTable
title="Overall"
data={props.datas}
columns={props.columns}
options={options}
components={props.components}
/>
</div>
);
}
export default Performancetbl;
Thanks!
Are you passing in data to props after fetching from an API? How are you getting the data you are passing in?
Overall this doesn't seem like an issue with material-table... Can you try asking this question on Stack Overflow?
No responses yet.
Yes, I am getting data from an API call, which I am passing in as a prop to Performancetbl.
It is weird, I tried everything and this is happening first time.
One thing I am doing in my code is that I am changing the number of columns for each data I fetch. For example, some data will need 10 columns and others 5. So I am sending both column and data as prop to the table. Is that allowed?
You will need to show the code from the parent component. It would be ideal if you could write a quick mock up of the issue you are having using something like JSON Placeholder as the API.
This is most likely happening because you are rendering the table before the data arrives from the API, and your state is always "one tick" behind (meaning your GUI is not reflecting actual state because when the GUI is rendered, the data has not arrived from the API yet)..
Thanks man. It seems to work now. I am not exactly sure what is the actual reason why it worked, but it is something like you said.
I was initially creating column array (which depends on date range), then created component array (This made custom header and summary footer), then waited for data to come from API which triggered another function that arranged data into the format required for data array.
The above order is column -> component -> data
When I changed the above order to column -> data -> component, it worked properly.
Btw, I even tried previously to convert prop to state, and then give that state variable to Material Table. Although state changed, it did not trigger a rerender. I thought in React state changes invoke rerender. Why is that it did not happen this time?
Dude if you want help with specific issues that you can reproduce, you will need to supply some code. Please provide a _working demo_ of this happening.
Furthermore, as you stated that would be a React issue, not a material-table issue... please close out this issue as it has been resolved.
@oze4 Have the exact same issue, the state is not changing if the column/data state provided is not within the component itself. I think @codenamethanos case was his data is coming from parent components, whereas in my case, i am passing in the data/columns stuff from a statecontainer. After some deep diving into the src code, i realized the problem might be due to ...this.dataManager.getRenderState() in materiable-table.js under onRowUpdate. getRenderState method itself is returning data: this.sortedData,, which in our case this.sortedData's initial value still persist since this doesn't have the updated state values.
hey @Sheing can you write up a demo of this issue? I think it would be easier for others to understand, and visualize, what is happening. From your description, it still appears to be an issue with how state is being passed in.
@Sheing I wouldn't expect that to update the data... you have to update the state/data yourself...
import React, { useState } from "react";
import MaterialTable from "material-table";
import { makeStyles } from "@material-ui/styles";
import tableIcons from "./TableIcons.js";
const originalData = ["Rock", "Paper", "Scissors"].map(word => ({
id: Math.floor(Math.random() * 300),
name: word
}));
const useStyles = makeStyles({
myEditIcon: {
color: "red",
fontSize: "60px"
}
});
export default function AppTable() {
const classes = useStyles();
const [data, setData] = useState(originalData);
return (
<MaterialTable
data={data}
title="My Table"
icons={{
...tableIcons,
Edit: () => <tableIcons.Edit className={classes.myEditIcon} />
}}
columns={[
{
title: "Id",
field: "id"
},
{
title: "Name",
field: "name"
}
]}
editable={{
onRowUpdate: (newData, oldData) => {
return new Promise((resolve, reject) => {
try {
// First make a copy of state, per best practices
const dataCopy = [...data];
// Find the index of the updated row - we have to use old data since
// new data is not part of state yet
const index = dataCopy.indexOf(oldData)
// Update the found index with the new data
dataCopy[index] = newData;
// Update our state
setData(dataCopy);
// Since `onRowUpdate` has to return a Promise, resolve it
resolve();
} catch (err) {
// Since `onRowUpdate` has to return a Promise, reject it if there is an error
reject(err);
}
});
}
}}
/>
);
}
^ you were really fast in replying. I saw your other live demo and figured out the solution. Thanks man. Also, using stateprovider from 'Unstated-Next' works really well with this table.
Simpler version inside Try--->
setData(prevState=>{
const dataCopy = [...prevState];
const index = dataCopy.indexOf(oldData);
dataCopy[index] = newData;
return dataCopy;
})
@codenamethanos is it ok if you close out this issue?
Most helpful comment
^ you were really fast in replying. I saw your other live demo and figured out the solution. Thanks man. Also, using stateprovider from 'Unstated-Next' works really well with this table.
Simpler version inside Try--->