React useMutation配置onError后onMutate出现TypeScript类型错误(乐观更新)
解决React Query useMutation乐观更新的TypeScript类型错误
问题代码
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useRef } from "react"; import { Todo } from "./hooks/useTodos"; import axios from "axios"; interface AddTodoContext { previousTodo: Todo[]; } const TodoForm = () => { const queryClient = useQueryClient(); const addTodo = useMutation<Todo, Error, Todo, AddTodoContext>({ mutationFn: (todo: Todo) => axios .post<Todo>("https://jsonplaceholder.typicode.com/todos", todo) .then((res) => res.data), onMutate: (newTodo: Todo) => { const previousTodos = queryClient.getQueryData<Todo[]>(["todos"]) || []; queryClient.setQueryData<Todo[]>(["todos"], (todos) => [ newTodo, ...(todos || []), ]); if (ref.current) ref.current.value = ""; return { previousTodos }; }, onSuccess: (savedTodo, newTodo) => { queryClient.setQueryData<Todo[]>(["todos"], (todos) => todos?.map((todo) => (todo === newTodo ? savedTodo : todo)) ); }, onError: (error, newTodo, context) => { if (!context) return; queryClient.setQueryData<Todo[]>(["todos"], context.previousTodo); }, }); const ref = useRef<HTMLInputElement>(null); return ( <> {addTodo.error && ( <div className="alert alert-danger">{addTodo.error.message}</div> )} <form className="row mb-3" onSubmit={(event) => { event.preventDefault(); if (ref.current && ref.current?.value) { addTodo.mutate({ id: 0, title: ref.current?.value, completed: false, userId: 1, }); } }} > <div className="col"> <input ref={ref} type="text" className="form-control" /> </div> <div className="col"> <button className="btn btn-primary">Add</button> </div> </form> </> ); }; export default TodoForm;
报错信息
Type '(newTodo: Todo) => { previousTodos: Todo[]; }' is not assignable to type '(variables: Todo) => AddTodoContext | Promise<AddTodoContext | undefined> | undefined'. Type '{ previousTodos: Todo[]; }' is not assignable to type 'AddTodoContext | Promise<AddTodoContext | undefined> | undefined'.ts(2322) queryClient-bm-z2rsD.d.ts(598, 5): The expected type comes from property 'onMutate' which is declared here on type 'UseMutationOptions<Todo, Error, Todo, AddTodoContext>' (property) onMutate?: ((variables: Todo) => AddTodoContext | Promise<AddTodoContext | undefined> | undefined) | undefined
解决方案
类型错误的核心原因是**AddTodoContext接口的属性名和onMutate返回对象的属性名不匹配**:
- 接口定义的是
previousTodo(单数) onMutate返回的是previousTodos(复数,多了个s)
只需要将两者的属性名统一即可,有两种修改方式:
方式一:修改AddTodoContext接口
将接口里的属性名改为和返回对象一致:
interface AddTodoContext { previousTodos: Todo[]; // 把previousTodo改为previousTodos }
同时同步修改onError里的引用:
onError: (error, newTodo, context) => { if (!context) return; queryClient.setQueryData<Todo[]>(["todos"], context.previousTodos); // 这里也改成previousTodos };
方式二:修改onMutate的返回对象
让返回对象的属性名和接口定义一致:
onMutate: (newTodo: Todo) => { // ...其他代码 return { previousTodo: previousTodos }; // 把previousTodos改为previousTodo };
两种方式任选其一,修改后TypeScript的类型检查就会通过,乐观更新的逻辑也能正常运行。
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

