简体   繁体   English

使用反应查询从父组件触发多个突变

[英]Triggering multiple mutations from parent component with react-query

I'm working on a project using react-query where I'm displaying a list.我正在使用 react-query 开发一个项目,我正在其中显示一个列表。 Each entry in the list consists of multiple input fields and it should be able to save just a single entry as well as possible to save all the entries at once.列表中的每个条目都由多个输入字段组成,它应该能够只保存一个条目,也应该尽可能一次保存所有条目。

While saving the data I want to display loading indicators for the elements that are being saved a retry button in case the saving fails and a success message when it works.在保存数据时,我想为正在保存的元素显示一个加载指示器,以防保存失败,并在保存成功时显示一条成功消息。

I see it's possible to get ahold of the MutationCache , but I can't seem to find anything about triggering mutations from outside the component where it's used.我看到有可能获得MutationCache ,但我似乎找不到任何关于从使用它的组件外部触发突变的信息。

I made a small codesandbox to illustrate the setup, otherwise my code is pasted below.我制作了一个小的codesandbox来说明设置,否则我的代码粘贴在下面。 https://codesandbox.io/s/react-query-forked-5cuxgb?file=/src/Form.jsx https://codesandbox.io/s/react-query-forked-5cuxgb?file=/src/Form.jsx

Form.js表单.js

import * as React from "react";
import { Person } from "./Person";

export const Form = () => {
  const people = [
    {
      id: 1,
      name: "John Doe",
      age: 37
    },
    {
      id: 2,
      name: "Jack Johnson",
      age: 45
    },
    {
      id: 3,
      name: "Jimmie Jones",
      age: 23
    }
  ];

  const saveAll = () => {
    // Trigger mutations here?
  };

  return (
    <div>
      {people.map((person) => (
        <Person key={person.id} {...person} />
      ))}

      <hr />
      <button onClick={saveAll}>Save all</button>
    </div>
  );
};

Person.js Person.js

import * as React from "react";
import { useCreatePersonMutation } from "./useCreatePersonMutation";

export const Person = (props) => {
  const { mutate, status } = useCreatePersonMutation(props.id);

  return (
    <div>
      {status === "loading" && <span>Saving...</span>}
      {status === "success" && <span>Success</span>}
      {status === "error" && (
        <button onClick={mutate} style={{ marginRight: 12 }}>
          Retry
        </button>
      )}
      {status === "idle" && (
        <button onClick={mutate} style={{ marginRight: 12 }}>
          Create Person
        </button>
      )}

      <input value={props.name} disabled={status === "loading"} />
      <input value={props.age} disabled={status === "loading"} />
    </div>
  );
};

useCreatePersonMutation使用CreatePersonMutation

import { useMutation } from "react-query";

export const useCreatePersonMutation = (id) => {
  return useMutation({
    mutationKey: ["Create_Person", id],
    mutationFn: () => new Promise((resolve) => setTimeout(resolve, 3000))
  });
};

You can't really go into the mutation cache ( queryClient.getMutationCache() ) and look for existing mutations and invoke them, because mutations only "exist" once they have been invoked with .mutate or .mutateAsync .您不能真正将 go 放入突变缓存 ( queryClient.getMutationCache() ) 并查找现有突变并调用它们,因为突变只有在使用.mutate.mutateAsync调用后才“存在”。

So the mutations in your component aren't really "there yet".所以你的组件中的突变并不是真的“在那里”。

The easiest solution would imo be to:最简单的解决方案是:

  • have a separate mutation that lives in the Form componentForm组件中有一个单独的突变
  • you invoke all requests in there in parallel to create all users您并行调用其中的所有请求以创建所有用户
  • this will give you a separate loading state that you can either pass down to all components, or you just add one extra loading state (overlay) to the whole form while this mutation is running.这将为您提供一个单独的加载 state,您可以将其传递给所有组件,或者您只需在运行此突变时向整个表单添加一个额外的加载 state(覆盖)。

I ended up achieving the desired behaviour by executing a mutation for each person being saved.我最终通过为每个被拯救的人执行突变来实现所需的行为。

const saveAll = () => {
  Promise.allSettled(people.map((person) => mutateAsync(person)));
};

In the Person component that renders each row, I listen to the mutation cache and try to find the matching mutation by comparing the persons name with the name being passed to the mutation.在呈现每一行的Person组件中,我监听突变缓存并尝试通过将人员姓名与传递给突变的姓名进行比较来找到匹配的突变。

React.useEffect(() => {
  queryClient.getMutationCache().subscribe((listener) => {
    if (!listener) return;
    if (listener.options?.variables.name !== name) return;

    setStatus(listener.state.status);
  });
}, [queryClient, name]);

This allows each Person component to show the status of the mutation.这允许每个Person组件显示突变的状态。 And retrying a mutation is as simple as executing the mutation.重试突变与执行突变一样简单。

const retry = () => {
  const mutation = queryClient.getMutationCache().find({
    predicate: (mutation) => mutation.options.variables.name === name
  });

  if (mutation) {
    mutation.execute();
  }
};

It doesn't scale well performance wise if you work with large lists, since each Person component gets notified about each and every mutation that gets triggered.如果您使用大型列表,它不会很好地扩展性能,因为每个Person组件都会收到有关触发的每个突变的通知。 However the lists I work with are of limited size, so for now it seems to suit my needs.然而,我使用的列表的大小有限,所以现在它似乎适合我的需要。

https://codesandbox.io/s/react-query-forked-5cuxgb https://codesandbox.io/s/react-query-forked-5cuxgb

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM