Hello.
Using this table with redux and react-redux.
and getting error:
TypeError: Cannot add property tableData, object is not extensible
(anonymous function)
node_modules/material-table/dist/utils/data-manager.js:278
275 |
276 | this.selectedCount = 0;
277 | this.data = data.map(function (row, index) {
> 278 | row.tableData = (0, _objectSpread2["default"])({}, row.tableData, {
| ^ 279 | id: index
280 | });
281 |
My code looks like:
const orders = useSelector(state =>
state.orders
);
...
<MaterialTable
title="Orders"
isLoading={isLoadingOrders}
data={orders}
/>
Am I missing something. Thanks!
Can you reproduce that in a sandbox? Because I am using the same architecture and it works for me. Can you also share a console.log of the orders you are adding? Last but not least, can you verify if orders are frozen?
@Domino987 Ok I will try in sandbox. What do you mean by frozen orders?
also if i do:
const orders = useSelector(state =>
state.orders.map(o => ({...o, tableData: {}}))
);
it works, but this is workaround, not fix
Yes you are creating a new object with that map, what happens if you do not add the empty tabledata object to it?
It is possible you freeze your objects so they cannot be changed: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#:~:text=A%20frozen%20object%20can%20no,existing%20properties%20from%20being%20changed.
Ok check this sandbox.
https://codesandbox.io/embed/pensive-wilbur-j50qd?fontsize=14&hidenavigation=1&theme=dark
Object.isFrozen(tableData[0]) returns true, so redux toolkit in fact freezes your obejcts. That is causing the errors. I am closing the issue, since this is not a material table error. But the mutation of the passed objects will be removed in the future.
But the mutation of the passed objects will be removed in the future.
How soon? I now migrated from React Apollo 2.6 to 3.1 and in new version Apollo locking objects in Apollo cache. So when using these objects with material-table, getting same error. Its preventing to upgrade to Apollo 3.0 and higher.
It is specially annoying, because I'm using editable tree data with material-table. So if I use workaround as mentioned above, after I submit the change and mutate it with Apollo, getting new array from Apollo and original tableData missing, the workaround is recreating it, but all the collapse/expand information missing so the whole table collapses.
For those coming here due to issue I mentioned earlier (incompatibility with @apollo/client). I have found a temporary workaround.
Just to give some background first, Apollo has consolidated all their Apollo related packages into @apollo/client. As part of consolidation InMemoryCache was updated based on this blog post via PR #5073, where they introduced immutability for the cache for performance.
In order to avoid the issue of incompatibility between immutable new Apollo cache and requirement of material-table for data to be mutable, I changed InMemoryCache import to be from apollo-cache-inmemory, which most likely will be deprecated soon. Luckily it still works and can be plugged into new Apollo client.
So I think material-table should still implement a way to work with immutable data, since the workaround I mentioned above is only temporary till old Apollo package will be deprecated.
Hi thanks for the workaround for Apollo. I do not know the exact timeline but I will talk to the other maintainers to get started as soon as possible.
I'm also using new @apollo/client and having same issue after upgrade.
Worked for me:
https://stackoverflow.com/questions/59648434/material-table-typeerror-cannot-add-property-tabledata-object-is-not-extensibl
It works only in some cases. Also same solution was given above by @da1z.
As I explained above in my case I have tree data and table is editable. So every time I mutate, Apollo returns new array and the expand/collapse data created by material-table is lost, so table collapses all nodes.
I am still waiting for material-table to find resolution for this use case.
Same problem with Redux toolkit https://redux-toolkit.js.org/
Is there any plan to stop modifying the input data?
Same problem with migrating to @apollo/client but I found a more robust solution. If you simply map through the rows and use the function Object.assign({}, buf) you can convert them to writable objects. This works for me. Example:
const [state, setState] = useState<TableState>({
columns: [
{ title: 'Topic Name', field: 'topic', type: 'string', editable: 'onAdd' },
{ title: 'Current Size', field: 'currSize', type: 'numeric', editable: 'never' },
{ title: 'Packets Expire (T/F)', field: 'expires', type: 'boolean', editable: 'always' },
{ title: 'Experation Time (ms)', field: 'experationTime', type: 'numeric', editable: 'always' },
{ title: 'Is Memory Limited (T/F)', field: 'sizeLimited', type: 'boolean', editable: 'always' },
{ title: 'Memory Limit (bytes)', field: 'maxSize', type: 'numeric', editable: 'always' },
],
data: [],
})
useQuery<BufferQuery>(BufferQuery, {
onCompleted: (queryData: BufferQuery) => {
const buffers = queryData.runningBuffers ? queryData.runningBuffers.map(buf => Object.assign({}, buf)) : [];
setState({ columns: state.columns, data: buffers });
}
});
return(<MaterialTable columns={state.columns} data={state.data}
//... I deleted all of my on edit stuff here to make this readable
/>)
PS: Object.assign can also be used if you use mutations. Just use newRow = Object.assign(oldRow, dataFromMutation)
PS: Object.assign can also be used if you use mutations. Just use
newRow = Object.assign(oldRow, dataFromMutation)
While it is possible, it is very complex and will have impact on performance, especially as the table data size grows. When using Apollo's useMutation to update a record, it will update the record in the Apollo cache (as long it's ID didn't change, which is the normal behavior for updates). The moment it happens, useQuery will cause the component to re-render with new set of data. So now need to iterate through updated data and for each record iterate through original data to find record by ID, because I'm not sure if Apollo will always give data in same order in array (which also requires to have original data in component state, while normally not required for Apollo data, since Apollo already provide re-render mechanism for data changes).
Again, this is a workaround, and an ugly one. I still hope material-table maintainers will find more elegant solution for this.
For those coming here due to issue I mentioned earlier (incompatibility with @apollo/client). I have found a temporary workaround.
Just to give some background first, Apollo has consolidated all their Apollo related packages into @apollo/client. As part of consolidation InMemoryCache was updated based on this blog post via PR #5073, where they introduced immutability for the cache for performance.In order to avoid the issue of incompatibility between immutable new Apollo cache and requirement of material-table for data to be mutable, I changed InMemoryCache import to be from apollo-cache-inmemory, which most likely will be deprecated soon. Luckily it still works and can be plugged into new Apollo client.
So I think material-table should still implement a way to work with immutable data, since the workaround I mentioned above is only temporary till old Apollo package will be deprecated.
how did you make it work? When I using apollo-cache-inmemory I get this error:
Type 'InMemoryCache' is missing the following properties from type 'ApolloCache<unknown>': identify, gc, modify, getFragmentDoc
Another solution is to JSON.parse(JSON.stringify()) but I think it's even worse then loops through the rows :(
how did you make it work? When I using apollo-cache-inmemory I get this error:
Type 'InMemoryCache' is missing the following properties from type 'ApolloCache<unknown>': identify, gc, modify, getFragmentDoc
like this:
import { ApolloClient } from '@apollo/client'
import { InMemoryCache } from 'apollo-cache-inmemory'
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
})
The link is complex, I create it from split of 3 links - uploadLink, WebsocketLink and authLink.
Has anyone looked at this issue? I would like to use InMemoryCache provided by Apollo instead of custom one I have to use as a workaround (see workaround provided above).
As a solution approach recommendation, please allow us to provide material-table function to mutate data object via new property. This way we can use Apollo facility to mutate object in Apollo cache as described here: #4543
Most helpful comment
For those coming here due to issue I mentioned earlier (incompatibility with @apollo/client). I have found a temporary workaround.
Just to give some background first, Apollo has consolidated all their Apollo related packages into @apollo/client. As part of consolidation InMemoryCache was updated based on this blog post via PR #5073, where they introduced immutability for the cache for performance.
In order to avoid the issue of incompatibility between immutable new Apollo cache and requirement of material-table for data to be mutable, I changed InMemoryCache import to be from apollo-cache-inmemory, which most likely will be deprecated soon. Luckily it still works and can be plugged into new Apollo client.
So I think material-table should still implement a way to work with immutable data, since the workaround I mentioned above is only temporary till old Apollo package will be deprecated.