harsh-harlequin
harsh-harlequinβ€’10mo ago

Errors When Trying to Delete a Resource

Ok, so in my List view, I added column actions for each row, where one of the actions is to delete that row. I have my custom dataProvider setup with a deleteOne that takes in the parameters and sends out an API request to delete the request item. I have also enabled canDelete in my App For some reason though, when i click the delete action, I get the following errors in my console and deleteOne is never called:
TypeError: Cannot read properties of undefined (reading 'filter')
at useDelete.ts:296:56
at functionalUpdate (utils.ts:82:8)
at QueryClient.setQueryData (queryClient.ts:218:18)
at queryClient.ts:253:16
at Array.map (<anonymous>)
at queryClient.ts:251:10
at Object.batch (notifyManager.ts:27:16)
at QueryClient.setQueriesData (queryClient.ts:248:26)
at Object.onMutate (useDelete.ts:290:33)
at async Mutation.execute (mutation.ts:207:25)
TypeError: Cannot read properties of undefined (reading 'filter')
at useDelete.ts:296:56
at functionalUpdate (utils.ts:82:8)
at QueryClient.setQueryData (queryClient.ts:218:18)
at queryClient.ts:253:16
at Array.map (<anonymous>)
at queryClient.ts:251:10
at Object.batch (notifyManager.ts:27:16)
at QueryClient.setQueriesData (queryClient.ts:248:26)
at Object.onMutate (useDelete.ts:290:33)
at async Mutation.execute (mutation.ts:207:25)
and
TypeError: Cannot read properties of undefined (reading 'status')
at Object.onError [as mutationFn] (authProvider.ts:77:24)
at Object.fn (mutation.ts:179:31)
at run (retryer.ts:147:31)
at createRetryer (retryer.ts:204:5)
at executeMutation (mutation.ts:174:22)
at Mutation.execute (mutation.ts:216:26)
TypeError: Cannot read properties of undefined (reading 'status')
at Object.onError [as mutationFn] (authProvider.ts:77:24)
at Object.fn (mutation.ts:179:31)
at run (retryer.ts:147:31)
at createRetryer (retryer.ts:204:5)
at executeMutation (mutation.ts:174:22)
at Mutation.execute (mutation.ts:216:26)
17 Replies
kapa.ai
kapa.aiβ€’10mo ago
Hi @nick_montoya. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Hmm, I don't know enough to give you a confident answer yet. The error messages you're seeing seem to suggest that there's an issue with the data being passed to the useDelete hook or the response from your deleteOne method in your data provider. However, without more specific information about your implementation, it's hard to provide a definitive solution. I would recommend checking the following based on the information provided in the extracts: 1. Ensure that your deleteOne method in your data provider is correctly implemented and returns the expected data structure. According to the documentation (https://refine.dev/docs/tutorial/understanding-dataprovider/create-dataprovider), it should return an object with a data property. 2. Check the parameters you're passing to the useDelete hook and the mutate function it returns. The documentation (https://refine.dev/docs/api-reference/core/hooks/data/useDelete) suggests that you should be passing an object with resource and id properties to the mutate function. 3. Make sure that the canDelete prop is correctly set in your Edit component and that the usePermissions hook is returning the expected data. The documentation (https://refine.dev/docs/api-reference/mui/components/basic-views/edit) provides more information on how to use these. If you're still having trouble, I would recommend referring to the refine documentation (https://refine.dev/docs) or seeking help from the refine community. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.aiβ€’10mo ago
<@263527875506995202> kapa.ai is still learning and improving, please let us know how it did by reacting below
harsh-harlequin
harsh-harlequinβ€’10mo ago
@kapa.ai Okay so this is weird, if I type random gibberish into as the resource name in mutateDelete hook that I am using to delete the item, it will invoke my deleteOne and send out an API request fine with that random resource that has never been defined, but if I use my actual reasource name, then it results in those errors mentioned above
kapa.ai
kapa.aiβ€’10mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- This is indeed strange behavior. The resource parameter you pass to the mutate function from useDelete hook is used as an API endpoint path and is passed to the deleteOne method in your data provider. If using a random string as the resource works but using the actual resource name doesn't, it suggests that there might be an issue with how the resource is being handled in your deleteOne method or elsewhere in your application. Here are a few things you could check: 1. Check your deleteOne method: Ensure that your deleteOne method in your data provider is correctly handling the resource parameter. It should be used to construct the API endpoint URL for deleting a record. Here's an example from the documentation (https://refine.dev/docs/tutorial/understanding-dataprovider/create-dataprovider):
deleteOne: async ({ resource, id, variables }) => {
const url = `${apiUrl}/${resource}/${id}`;

const { data } = await axiosInstance.delete(url, {
data: variables,
});

return {
data,
};
},
deleteOne: async ({ resource, id, variables }) => {
const url = `${apiUrl}/${resource}/${id}`;

const { data } = await axiosInstance.delete(url, {
data: variables,
});

return {
data,
};
},
2. Check your useDelete hook: Make sure you're passing the correct resource and id to the mutate function returned by the useDelete hook. Here's an example from the documentation (https://refine.dev/docs/api-reference/core/hooks/data/useDelete):
const { mutate } = useDelete();

mutate({
resource: "products",
id: 1,
});
const { mutate } = useDelete();

mutate({
resource: "products",
id: 1,
});
3. Check for naming conflicts: Ensure that the resource name you're using doesn't conflict with any other variables or reserved words in your application. 4. Check your API: If your deleteOne method and useDelete hook are correctly implemented, there might be an issue with your API. Ensure that the API endpoint for deleting a record exists and is correctly configured to handle delete requests. If you're still having trouble, I would recommend referring to the refine documentation (https://refine.dev/docs) or seeking help from the refine community. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.aiβ€’10mo ago
<@263527875506995202> kapa.ai is still learning and improving, please let us know how it did by reacting below
harsh-harlequin
harsh-harlequinβ€’10mo ago
@kapa.ai Well the problem is that the deleteOne method never gets invoked when the resource name is set correctly in the mutateDelete, so it has nothing to do with the data provider and deleteOne, it has to do with something mutateDelete is doing before it passes it off to deleteOne, there is some code in between them that is causing this error, what could it be?
kapa.ai
kapa.aiβ€’10mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Based on the information provided in the extracts, the useDelete hook from Refine uses the deleteOne method from your data provider as the mutation function. If the deleteOne method is not being invoked when you pass the correct resource name to the mutate function, it suggests that there might be an issue with how the useDelete hook or the mutate function is being used. Here are a few things you could check: 1. Check your useDelete hook: Make sure you're correctly importing and using the useDelete hook. Here's an example from the documentation (https://refine.dev/docs/api-reference/core/hooks/data/useDelete):
import { useDelete } from "@refinedev/core";

const { mutate } = useDelete();

mutate({
resource: "products",
id: 1,
});
import { useDelete } from "@refinedev/core";

const { mutate } = useDelete();

mutate({
resource: "products",
id: 1,
});
2. Check the parameters you're passing to mutate: The mutate function expects an object with resource and id properties. Make sure you're passing these correctly. If you're dynamically generating these values, ensure that they're correctly defined at the time you're calling mutate. 3. Check for naming conflicts: Ensure that the resource name you're using doesn't conflict with any other variables or reserved words in your application. 4. Check your data provider: Even though you mentioned that the deleteOne method is not being invoked, it's worth checking that your data provider is correctly set up and passed to the <Refine> component. The useDelete hook uses the deleteOne method from the data provider you pass to <Refine>. If you're still having trouble, I would recommend referring to the refine documentation (https://refine.dev/docs) or seeking help from the refine community. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.aiβ€’10mo ago
<@263527875506995202> kapa.ai is still learning and improving, please let us know how it did by reacting below
harsh-harlequin
harsh-harlequinβ€’10mo ago
@kapa.ai So my Edit component can delete the resource fine, its just from the List view it's having trouble deleting it, why would it work with the Edit component but not the list?
kapa.ai
kapa.aiβ€’10mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The difference in behavior between the Edit and List views could be due to how the resource and id are being passed to the useDelete hook in each view. In the Edit view, the resource and id are often obtained from the route parameters, as the Edit view is typically associated with a specific record. The DeleteButton component in the Edit view reads the resource name and id from the route by default, as mentioned in the documentation (https://refine.dev/docs/api-reference/chakra-ui/components/buttons/delete-button). In the List view, however, you might be manually passing the resource and id to the useDelete hook or DeleteButton component. If these values are not correctly defined at the time you're calling the mutate function, it could lead to the issues you're experiencing. Here's an example of how you might use the DeleteButton in a List view:
import { DeleteButton } from "@refinedev/antd";

const MyListComponent = () => {
return (
<DeleteButton
resource="posts"
recordItemId="123"
/>
);
};
import { DeleteButton } from "@refinedev/antd";

const MyListComponent = () => {
return (
<DeleteButton
resource="posts"
recordItemId="123"
/>
);
};
In this example, the resource is "posts" and the recordItemId is "123". These values should match the resource and id of the record you want to delete. If you're still having trouble, I would recommend referring to the refine documentation (https://refine.dev/docs) or seeking help from the refine community. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.aiβ€’10mo ago
<@263527875506995202> kapa.ai is still learning and improving, please let us know how it did by reacting below
like-gold
like-goldβ€’10mo ago
Hey @nick_montoya, Are you sure that the getList method returns data and total? https://refine.dev/docs/tutorial/understanding-dataprovider/create-dataprovider/#methods Probaply you use mutationMode different from pessimistic so when you try a mutation the refine hooks also try to change cache data. If you don’t return a data as not expected, an error occurs.
harsh-harlequin
harsh-harlequinβ€’10mo ago
Thanks for the reply! Yes this is my getList:
getList: async ({ resource, pagination, sorters, filters }) => {
console.log("resource", resource);
const url = `${apiUrl}/${resource}`;

const queryFilters = generateFilters(filters);

const { data: response, headers } = await axiosInstance.get(`${url}?cursor=${pagination?.current || 0}&${stringify(queryFilters)}`);

console.log("DATA FROM LAMBDA", response);

// we have a valid response, extract the data from the body
const cursor = response.body.cursor;
const data = response.body.data;

const total = +headers["x-total-count"];
return {
data,
total,
cursor: {
next: cursor,
prev: pagination?.current || 0,
},
};
},
getList: async ({ resource, pagination, sorters, filters }) => {
console.log("resource", resource);
const url = `${apiUrl}/${resource}`;

const queryFilters = generateFilters(filters);

const { data: response, headers } = await axiosInstance.get(`${url}?cursor=${pagination?.current || 0}&${stringify(queryFilters)}`);

console.log("DATA FROM LAMBDA", response);

// we have a valid response, extract the data from the body
const cursor = response.body.cursor;
const data = response.body.data;

const total = +headers["x-total-count"];
return {
data,
total,
cursor: {
next: cursor,
prev: pagination?.current || 0,
},
};
},
For now, I only have 1 data in my organizations table, so I don't really need the cursor, but its there anyways. So it looks like it does work on pessimistic mode, but not on optimistic mode or undoable mode so is it a problem of being able to cache my data locally? how can I solve this? I don't really see where I have control of how mutateDelete tries to do this, does it need to match my interface perfectly?
ambitious-aqua
ambitious-aquaβ€’10mo ago
Just checked out the source code for useDelete, it looks like your data property from the getList method might not be returning an array all the times (like null or any non array). Can this be the case? In your getList method, it looks like you might benefit from having some type checking from the API response πŸ˜… Not sure if this is the real case but it looks like response.body.data is not always an array. BTW, about the error you've mentioned in the first message Cannot read properties of undefined (reading 'status') looks like its realted with the onError method of your authProvider πŸ€” Let me know when you can validate the getList method always returning an array πŸ™
harsh-harlequin
harsh-harlequinβ€’10mo ago
oh ok interesting, ill try to fix that one yea that's definitely possible, although my API should always be sending back an array even if its empty, but ill take a look What is status supposed to represent for the onError hook? Is that just the status code? Is it supposed to be placed in the error or error.response or somewhere else in the error when I return it? Is there a detailed API doc on all of these functions so I know excatly what my functions are supposed to return? just added a type check to my getList so it should be returning an array when I try to delete the resource in the list view because it doesn't throw an error on the console/app UI:
getList: async ({ resource, pagination, sorters, filters }) => {
console.log("resource", resource);
const url = `${apiUrl}/${resource}`;

const queryFilters = generateFilters(filters);

const { data: response, headers } = await axiosInstance.get(`${url}?cursor=${pagination?.current || 0}&${stringify(queryFilters)}`);

console.log("DATA FROM LAMBDA", response);

// we have a valid response, extract the data from the body
const cursor = response.body.cursor;
const data = response.body.data;

// we need to validate that the data is an array, otherwise we need to throw an error
if (!Array.isArray(data)) {
throw new Error("Data is not an array");
}

const total = +headers["x-total-count"];
return {
data,
total,
cursor: {
next: cursor,
prev: pagination?.current || 0,
},
};
},
getList: async ({ resource, pagination, sorters, filters }) => {
console.log("resource", resource);
const url = `${apiUrl}/${resource}`;

const queryFilters = generateFilters(filters);

const { data: response, headers } = await axiosInstance.get(`${url}?cursor=${pagination?.current || 0}&${stringify(queryFilters)}`);

console.log("DATA FROM LAMBDA", response);

// we have a valid response, extract the data from the body
const cursor = response.body.cursor;
const data = response.body.data;

// we need to validate that the data is an array, otherwise we need to throw an error
if (!Array.isArray(data)) {
throw new Error("Data is not an array");
}

const total = +headers["x-total-count"];
return {
data,
total,
cursor: {
next: cursor,
prev: pagination?.current || 0,
},
};
},
The thing is though, getList doesn't run when i hit the delete action, as the console log inside of it never gets ran
ambitious-aqua
ambitious-aquaβ€’10mo ago
Check out this doc about the error format in the data providers https://refine.dev/docs/api-reference/core/providers/data-provider/#error-format You can also check out our docs on auth providers. You may also find the tutorial helpful, it goes through both of these concepts πŸš€
Data Provider | refine
Data provider acts as a data layer for your app, making HTTP requests and encapsulating how the data is retrieved. The methods of these requests are then consumed by refine via data hooks.
ambitious-aqua
ambitious-aquaβ€’10mo ago
About the getList not running, I'd really appreciate if you can share some sample code about your useList (or similar) and useDelete hook usage. Do you have the same resource value set for both of them? Right after a successful delete action, useDelete invalidates the list and many caches for the same resource and data provider. If you have the same data provider and the same resource set for the useList and useDelete, it should be invalidating the query cache and therefore do a refetch using the getList πŸ€”