修改 Mutations

Queries不同,Mutations 主要用于创建、更新或删除数据,或者执行服务器端的副作用操作。为了支持这一点,TanStack Query 提供了 useMutation 钩子。

🌰 示例:添加一个 Todo:

function App() {
  const mutation = useMutation({
    mutationFn: (newTodo) => axios.post("/todos", newTodo),
  });

  return (
    <div>
      {mutation.isPending ? (
        "正在添加 Todo..."
      ) : (
        <>
          {mutation.isError ? <div>发生错误: {mutation.error.message}</div> : null}

          {mutation.isSuccess ? <div>Todo 添加成功!</div> : null}

          <button
            onClick={() => {
              mutation.mutate({ id: new Date(), title: "洗衣服" });
            }}
          >
            创建 Todo
          </button>
        </>
      )}
    </div>
  );
}

mutation 在任何时候都只会处于以下状态之一:

  • isIdlestatus === 'idle'Mutation 处于空闲状态,或者已重置。

  • isPendingstatus === 'pending'Mutation 正在执行中。

  • isErrorstatus === 'error'Mutation 发生了错误。

  • isSuccessstatus === 'success': Mutation 成功完成,数据已可用。

除此之外,还有一些额外信息可用:

  • error:当Mutation失败时,可以通过error获取错误信息。

  • data:当Mutation成功时,可以通过data获取返回的数据。

在上面的示例中,你可以看到mutate方法接受参数,并将其传递给mutationFn进行处理。

Mutation 的增强功能

在事件处理函数中调用 mutate

在 React 16 及更早版本中,mutate不能直接在事件回调函数中使用,因为 React 事件会被回收。可以通过额外包装一层来解决:

// ❌ 这样在 React 16 及更早版本会报错
const CreateTodo = () => {
  const mutation = useMutation({
    mutationFn: (event) => {
      event.preventDefault();
      return fetch("/api", new FormData(event.target));
    },
  });

  return <form onSubmit={mutation.mutate}>...</form>;
};

// ✅ 正确的写法
const CreateTodo = () => {
  const mutation = useMutation({
    mutationFn: (formData) => fetch("/api", formData);
  });

  const onSubmit = (event) => {
    event.preventDefault();
    mutation.mutate(new FormData(event.target));
  };

  return <form onSubmit={onSubmit}>...</form>;
};

重置 Mutation 状态

如果需要清除 Mutation 产生的 errordata,可以使用 reset 方法:

const CreateTodo = () => {
  const [title, setTitle] = useState("");
  const mutation = useMutation({ mutationFn: createTodo });

  const onCreateTodo = (e) => {
    e.preventDefault();
    mutation.mutate({ title });
  };

  return (
    <form onSubmit={onCreateTodo}>
      {mutation.error && <h5 onClick={() => mutation.reset()}>{mutation.error}</h5>}
      <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} />
      <br />
      <button type="submit">创建 Todo</button>
    </form>
  );
};

Mutation 的生命周期回调

useMutation 提供了多个回调,可以在 Mutation 的不同阶段执行副作用(如更新 UI、日志记录等):

useMutation({
  mutationFn: addTodo,
  onMutate: (variables) => {
    // Mutation 即将开始!
    // 可以返回一个上下文(context),用于回滚
    return { id: 1 };
  },
  onError: (error, variables, context) => {
    console.log(`回滚操作,撤销 ID 为 ${context.id} 的更新`);
  },
  onSuccess: (data, variables, context) => {
    console.log("Mutation 成功!");
  },
  onSettled: (data, error, variables, context) => {
    console.log("无论成功还是失败,都会执行");
  },
});

这些回调可以返回 Promise,下一个回调会等待前一个回调完成后执行:

useMutation({
  mutationFn: addTodo,
  onSuccess: async () => {
    console.log("我是第一个执行的");
  },
  onSettled: async () => {
    console.log("我是第二个执行的");
  },
});

传递额外的回调

在调用 mutate 时,可以额外指定 onSuccessonErroronSettled,这些回调会在 Mutation 完成后执行:

mutate(todo, {
  onSuccess: (data, variables, context) => {
    console.log("这是额外的 onSuccess 处理");
  },
  onError: (error, variables, context) => {
    console.log("这是额外的 onError 处理");
  },
  onSettled: (data, error, variables, context) => {
    console.log("这是额外的 onSettled 处理");
  },
});

Mutation 并发与顺序执行

默认情况下,所有 Mutation 是并行执行的。如果希望某些 Mutation 按顺序执行,可以使用 scope,拥有相同 scope.idMutation 将按照顺序执行。

const mutation = useMutation({
  mutationFn: addTodo,
  scope: {
    id: "todo",
  },
});

使用 mutateAsync 获取 Promise

如果需要在代码中以 await 方式使用 Mutation,可以使用 mutateAsync

const mutation = useMutation({ mutationFn: addTodo });

try {
  const todo = await mutation.mutateAsync(todo);
  console.log(todo);
} catch (error) {
  console.error(error);
} finally {
  console.log("操作完成");
}

失败时自动重试

默认情况下,Mutation 失败后不会重试。但可以通过 retry 选项开启重试机制:

useMutation({
  mutationFn: addTodo,
  retry: 3, // 失败后最多重试 3 次
});

当设备离线导致 Mutation 失败时,会在设备恢复在线时自动重试。

Mutation 状态的持久化

Mutation 可以持久化存储,以便在应用重启后恢复未完成的 Mutation

const queryClient = new QueryClient();

queryClient.setMutationDefaults(["addTodo"], {
  mutationFn: addTodo,
  retry: 3,
});

// 持久化 Mutation 状态
const state = dehydrate(queryClient);
hydrate(queryClient, state);

// 重新恢复暂停的 Mutations
queryClient.resumePausedMutations();

最后更新于

这有帮助吗?