Custom mutation hook with arguments #3206
-
Hi, I don't see many discussions or examples around this topic where we wrap custom mutation hooks to take arguments as dependencies. For example, I'm mutating a
Now I have several ways to wrap a Approach 1: Spread path parameters along with request payload as the function useUpdateComment() {
return useMutation(({ postId, commentId, ...payload }) => http.put(`/api/posts/${postId}/comments/${commentId}`, payload));
}
function useDeleteComment() {
return useMutation(({ postId, commentId }) => http.delete(`/api/posts/${postId}/comments/${commentId}`));
}
// Usage
const { mutate: updateComment } = useUpdateComment();
const { mutate: deleteComment } = useDeleteComment();
updateComment({ postId, commentId, ...formValue })
deleteComment({ postId, commentId }) This is the default approach that I've seen in every example and discussion - the custom hooks don't take arguments. But imagine I'm on the post's detail page where the post's id is very commonly available through a router, I tend to think of const { postId } = useParams();
{comments.map(comment => <>
// postId looks like alien here 🤨
<button onClick={() => updateComment({ postId, commentId: comment.id, ...formValue })}>Update this comment</button>
<button onClick={() => deleteComment({ postId, commentId: comment.id })}>Delete this comment</button>
</>)} To push this case further, imagine I'm having api endpoints with more path parameters like:
const { userId, teamId, projectId } = useParams();
{tasks.map(task => <>
// Aliens invasion 😨
<button onClick={() => updateTask({ userId, teamId, projectId, taskId: task.id, ...formValue })}>Update this task</button>
<button onClick={() => deleteTask({ userId, teamId, projectId, taskId: task.id })}>Delete this task</button>
</>)} With this, I'd like to focus the Approach 2: Custom mutation hook takes arguments that's then used closured function useUpdateComment(postId) {
return useMutation(({ commentId, ...payload }) => http.put(`/api/posts/${postId}/comments/${commentId}`, payload));
}
function useDeleteComment(postId) {
return useMutation((commentId) => http.delete(`/api/posts/${postId}/comments/${commentId}`));
}
// Usage
const { postId } = useParams();
const { mutate: updateComment } = useUpdateComment(postId);
const { mutate: deleteComment } = useDeleteComment(postId);
updateComment({ commentId, ...formValue })
deleteComment(commentId) Now I have clean and undistracted mutation calls without repeated "alien" variables. {tasks.map(task => <>
<button onClick={() => updateTask({ taskId: task.id, ...formValue })}>Update this task</button>
<button onClick={() => deleteTask(task.id)}>Delete this task</button>
</>)} Hope to know what you guys think about the arguments approach. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 4 replies
-
I do it the same way. My rule of thumb is: Only variables, so things that change, should be passed to This basically "binds" the instance of
basically returning one update and one delete method for post with id 1. In the custom hook, this would be two Or, when reading from the router, you could scope that to within the custom hook:
we do this a lot if every invocation of the hook would read from params anyways. It's the biggest advantage of custom hooks: That you can enclose functionality like this by wrapping over multiple, other hooks. |
Beta Was this translation helpful? Give feedback.
-
@TkDodo It's a very common case to make an invalidation in |
Beta Was this translation helpful? Give feedback.
-
Is there any reason (apart from philosophical ones) to prefer passing arguments to mutation function vs to the hook function? |
Beta Was this translation helpful? Give feedback.
I do it the same way. My rule of thumb is: Only variables, so things that change, should be passed to
.mutate
or.mutateAsync
. The things that are the same for each invocation of the mutation (in the scope of my component) should go touseMutation
.This basically "binds" the instance of
useMutation
to a single usage. Custom hooks could even return multiple methods, like:basically returning one update and one delete method for post with id 1. In the custom hook, this would be two
useMutations
.Or, when reading from the router, you could scope that to within the custom hook: