React Query:如何优雅编写多useMutation自定义Hook?
Optimizing Your
useListMutation Hook to Eliminate Duplication Great call noticing the repetition in your custom hook—let’s clean this up to make it more maintainable and readable! The core idea is to extract shared logic into reusable helpers, so each mutation only defines the unique part (how it updates the cache).
Here’s the optimized version of your hook:
import * as List from "./list-api"; import { useMutation, queryCache } from 'react-query'; // Assuming these base imports import { v4 as uuidv4 } from 'uuid'; const useListMutation = (onSettled, onError) => { // Extract shared error handling logic const handleMutationError = (err, variables, rollback) => { rollback(); onError(err); }; // Extract shared post-settled logic const handleMutationSettled = () => { queryCache.invalidateQueries("lists"); onSettled(); }; // Factory function to build mutation configs const buildMutationConfig = (updateCache) => ({ onMutate: (variables) => { // Shared onMutate setup: cancel pending queries and save current cache state queryCache.cancelQueries("lists"); const currentLists = queryCache.getQueryData("lists"); // Apply the operation-specific cache update queryCache.setQueryData("lists", prev => updateCache(prev, variables)); // Return rollback function (shared across all mutations) return () => queryCache.setQueryData("lists", currentLists); }, onError: handleMutationError, onSettled: handleMutationSettled, }); // Create each mutation with its unique cache update logic const [createList] = useMutation(List.createList, buildMutationConfig((prev, listTitle) => [ ...prev, { title: listTitle, id: uuidv4() } ])); const [updateList] = useMutation(List.updateList, buildMutationConfig((prev, { id, titleValue }) => [ ...prev.filter(list => list._id !== id), { _id: id, title: titleValue } ])); const [deleteList] = useMutation(List.deleteList, buildMutationConfig((prev, id) => prev.filter(list => list._id !== id) )); return { createList, updateList, deleteList }; }; export default useListMutation;
Key Improvements:
- Shared Handler Extraction:
handleMutationErrorandhandleMutationSettledencapsulate the identical logic across all three mutations. Now if you need to adjust error handling or post-settled behavior, you only change it once. - Mutation Config Factory: The
buildMutationConfigfunction takes care of repetitiveonMutatesetup (canceling queries, saving current cache state, returning the rollback function). You only pass in the unique logic for how that mutation should update the cache. - Focused Unique Logic: Each mutation now only defines the part specific to it—how it modifies the list cache. This makes the code easier to scan and debug, since you don’t have to wade through repeated boilerplate to find differences between create/update/delete.
Bonus: Even More Cleanup (Optional)
If you want to separate cache update logic entirely for reusability or clarity, you could define them as standalone functions outside the hook:
// Cache update utilities (can live in a separate file if needed) const updateCacheForCreate = (prev, listTitle) => [ ...prev, { title: listTitle, id: uuidv4() } ]; const updateCacheForUpdate = (prev, { id, titleValue }) => [ ...prev.filter(list => list._id !== id), { _id: id, title: titleValue } ]; const updateCacheForDelete = (prev, id) => prev.filter(list => list._id !== id); // Then use them in the hook: const [createList] = useMutation(List.createList, buildMutationConfig(updateCacheForCreate)); const [updateList] = useMutation(List.updateList, buildMutationConfig(updateCacheForUpdate)); const [deleteList] = useMutation(List.deleteList, buildMutationConfig(updateCacheForDelete));
This is perfect if you want to reuse these cache update functions elsewhere, or keep your hook even more focused on mutation orchestration.
内容的提问来源于stack exchange,提问作者jake_prentice
相关产品推荐
相关产品推荐

