补丁多个ID和一个请求,带有React查询

发布于 2025-02-03 19:00:46 字数 2617 浏览 2 评论 0原文

我有一个非常基本的应用程序,可以预订座位。用户选择带有<代码>可用的座椅/座椅,点击书籍,补丁请求:false 通过React查询发送到伪造的API(JSON-SERVER),库将请求无效,并立即显示服务器的响应。

数据库结构看起来像这样:

{
  "hallA": [
    {
      "id": 1,
      "seat": 1,
      "available": false
    },
    {
      "id": 2,
      "seat": 2,
      "available": true
    },
    {
      "id": 3,
      "seat": 3,
      "available": false
    }
  ]
}

选择的逻辑,预订座椅看起来像这样:

const App = () => {
  const { data, isLoading } = useGetHallLayout("hallA");
  const [selected, setSelected] = useState<
    { id: number; seat: number; available: boolean }[]
  >([]);

  const handleSelect = useCallback(
    (seat: { id: number; seat: number; available: boolean }) => {
      const itemIdx = selected.findIndex((element) => element.id === seat.id);

      if (itemIdx === -1) {
        setSelected((prevState) => [
          ...prevState,
          { id: seat.id, seat: seat.seat, available: !seat.available },
        ]);
      } else {
        setSelected((prevState) =>
          prevState.filter((element) => element.id !== seat.id)
        );
      }
    },
    [selected]
  );

  const takeSeat = useTakeSeat({
    onSuccess: () => {
      useGetHallLayout.invalidate();
    },
  });

  const sendRequest = useCallback(() => {
    selected.forEach((item) =>
      takeSeat.mutateAsync({ id: item.id, hall: "hallA" })
    );
    setSelected([]);
  }, [selected, takeSeat]);

  return (
    <>
      {!isLoading && (
        <ConcertHall
          layout={data}
          onSeatSelect={handleSelect}
          activeSeats={selected}
        />
      )}
      <button disabled={isLoading} onClick={sendRequest}>
        Take selected
      </button>
    </>
  );
};

查询看起来像这样:

export const useGetHallLayout = (hall: string) => {
  const { data, isLoading } = useQuery(["hall"], () =>
    axios.get(`http://localhost:3000/${hall}`).then((res) => res.data)
  );
  return { data, isLoading };
};

export const useTakeSeat = (options?: UseMutationOptions<unknown, any, any>) =>
  useMutation(
    (data: { hall: string; id: number }) =>
      axios.patch(`http://localhost:3000/${data.hall}/${data.id}`, {
        available: false,
      }),
    {
      ...options,
    }
  );

useGetHallLayout.invalidate = () => {
  return queryClient.invalidateQueries("hall");
};

上述代码的问题是,我在每个循环中更新每个ID的操作非常昂贵(可用:false:false )并在每次更改后都不会更新一次后查询它无效。

问题是:考虑到JSON服务器的局限性,是否有更好的方法可以做到这一点?任何批处理更新而不是单独向每个ID发送请求?也许逻辑上有一些变化?

提前致谢

I have a very basic prototype of app that allows to book a seat. User selects the seat/seats, clicks book, patch request with available: false is sent to the fake api (json-server) with React Query, library invalidates the request and immediately shows response from the server.

Database structure looks like this:

{
  "hallA": [
    {
      "id": 1,
      "seat": 1,
      "available": false
    },
    {
      "id": 2,
      "seat": 2,
      "available": true
    },
    {
      "id": 3,
      "seat": 3,
      "available": false
    }
  ]
}

and the logic for selecting, booking seats looks like this:

const App = () => {
  const { data, isLoading } = useGetHallLayout("hallA");
  const [selected, setSelected] = useState<
    { id: number; seat: number; available: boolean }[]
  >([]);

  const handleSelect = useCallback(
    (seat: { id: number; seat: number; available: boolean }) => {
      const itemIdx = selected.findIndex((element) => element.id === seat.id);

      if (itemIdx === -1) {
        setSelected((prevState) => [
          ...prevState,
          { id: seat.id, seat: seat.seat, available: !seat.available },
        ]);
      } else {
        setSelected((prevState) =>
          prevState.filter((element) => element.id !== seat.id)
        );
      }
    },
    [selected]
  );

  const takeSeat = useTakeSeat({
    onSuccess: () => {
      useGetHallLayout.invalidate();
    },
  });

  const sendRequest = useCallback(() => {
    selected.forEach((item) =>
      takeSeat.mutateAsync({ id: item.id, hall: "hallA" })
    );
    setSelected([]);
  }, [selected, takeSeat]);

  return (
    <>
      {!isLoading && (
        <ConcertHall
          layout={data}
          onSeatSelect={handleSelect}
          activeSeats={selected}
        />
      )}
      <button disabled={isLoading} onClick={sendRequest}>
        Take selected
      </button>
    </>
  );
};

Queries look like this:

export const useGetHallLayout = (hall: string) => {
  const { data, isLoading } = useQuery(["hall"], () =>
    axios.get(`http://localhost:3000/${hall}`).then((res) => res.data)
  );
  return { data, isLoading };
};

export const useTakeSeat = (options?: UseMutationOptions<unknown, any, any>) =>
  useMutation(
    (data: { hall: string; id: number }) =>
      axios.patch(`http://localhost:3000/${data.hall}/${data.id}`, {
        available: false,
      }),
    {
      ...options,
    }
  );

useGetHallLayout.invalidate = () => {
  return queryClient.invalidateQueries("hall");
};

The problem of the above code is that I perform very expensive operation of updating each id in a for each loop (to available: false) and query invalidates it after each change not once all of them are updated.

The question is: is there any better way to do this taking into account the limitations of json-server? Any batch update instead of sending request to each and every id seperately? Maybe some changes in a logic?

Thanks in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

行雁书 2025-02-10 19:00:46

您当然可以进行一个启动多个请求的突变,并以promise.allPromise.allsettled返回结果。类似的内容:

useMutation((seats) => {
  return Promise.allSettled(seats.map((seat) => axios.patch(...))
})

然后,所有查询都将有一个“生命周期”(加载 /错误 /成功),而OnSuccess < / code>只能调用一次。

我看到的另一个陷阱是,您真的希望Hall字符串成为查询密钥的一部分:

- useQuery(["hall"], () =>
+ useQuery(["hall", hall], () =>

You can certainly make one mutation that fires of multiple requests, and returns the result with Promise.all or Promise.allSettled. Something like:

useMutation((seats) => {
  return Promise.allSettled(seats.map((seat) => axios.patch(...))
})

then, you would have one "lifecycle" (loading / error / success) for all queries together, and onSuccess will only be called once.

Another gotcha I'm seeing is that you'd really want the hall string to be part of the query key:

- useQuery(["hall"], () =>
+ useQuery(["hall", hall], () =>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文