T
TanStack3y ago
rival-black

Multiple mutations based on dynamic list - separate mutation for each row in the table

Is it possible to have multiple (dynamic) lists of mutations? I have a list (table) of items. For each element I'd like to be able to click the 'Delete' button separately. So each row would have a different isLoading state for mutations. For example, imagine the following GUI:
name | actions
------|--------
element1 | delete
element2 | delete
element3 | delete
name | actions
------|--------
element1 | delete
element2 | delete
element3 | delete
and when clicking the delete button on element1 and element2:
name | actions
------|--------
element1 | delete...
element2 | delete
element3 | delete...
name | actions
------|--------
element1 | delete...
element2 | delete
element3 | delete...
3 Replies
rival-black
rival-blackOP3y ago
Ok I think it's possible to handle this on my own by creating some kind of object like:
const elementIdsBeingDeleted: {
[id]: boolean;
};
const elementIdsBeingDeleted: {
[id]: boolean;
};
and manually deal with it on starting mutation, error and success. I'm not sure if that's correct approach but it's the only one that I am aware of. This is what I came with:
const [elementIdsBeingDeleted, setElementIdsBeingDeleted] = useState<{ [id: string]: boolean }>({});

const deleteElementMutation = useMutation({
mutationFn: deleteElement,
onMutate: (variables) => {
// when starting mutation I set the flag for element id that it's being deleted
setElementIdsBeingDeleted((state) => ({ ...state, [variables.elementId]: true }));
},
onSuccess: (_data, variables, _context) => {
// when mutation succeeded then I remove the element from cache (I could do cache invalidation too)
const oldData = queryClient.getQueryData(['elements']) as GetElementsResponse;
const newElements = oldData.elements.filter((element) => element.id !== variables.elementId);
queryClient.setQueryData(['elements'], { ...oldData, elements: newElements });
},
onError: (_error, variables, _context) => {
// when mutation failed I could show some kind of toast
console.log(">> couldn't delete", variables);
},
onSettled: (data, error, variables, context) => {
// when mutation settled (failed or succeeded) then I'm resetting the flag for the element
setElementIdsBeingDeleted((state) => ({ ...state, [variables.elementId]: false }));
},
});
const [elementIdsBeingDeleted, setElementIdsBeingDeleted] = useState<{ [id: string]: boolean }>({});

const deleteElementMutation = useMutation({
mutationFn: deleteElement,
onMutate: (variables) => {
// when starting mutation I set the flag for element id that it's being deleted
setElementIdsBeingDeleted((state) => ({ ...state, [variables.elementId]: true }));
},
onSuccess: (_data, variables, _context) => {
// when mutation succeeded then I remove the element from cache (I could do cache invalidation too)
const oldData = queryClient.getQueryData(['elements']) as GetElementsResponse;
const newElements = oldData.elements.filter((element) => element.id !== variables.elementId);
queryClient.setQueryData(['elements'], { ...oldData, elements: newElements });
},
onError: (_error, variables, _context) => {
// when mutation failed I could show some kind of toast
console.log(">> couldn't delete", variables);
},
onSettled: (data, error, variables, context) => {
// when mutation settled (failed or succeeded) then I'm resetting the flag for the element
setElementIdsBeingDeleted((state) => ({ ...state, [variables.elementId]: false }));
},
});
If you encountered a similar problem and solved it differently please share it here.
rival-black
rival-blackOP3y ago
This is how it works:
other-emerald
other-emerald3y ago
For cases like this, I would put each item inside their own component and put the mutation and loading state in there. That should make the code simpler.

Did you find this page helpful?