React-query setQueryData not re-rendering component

题意:"React-query 的 `setQueryData` 没有触发组件重新渲染"

问题背景:

I've been dealing for a while with this problem and still can't tackle it.

"我已经处理这个问题一段时间了,但仍然无法解决它。"

I'm using React-query as a server state management library and I'm trying to get my UI state synchronized with my server state when a mutations occurs. Since I can use the mutation response to avoid a new API call, I'm using the setQueryData feature that React-query gives us.

"我正在使用 React-query 作为服务器状态管理库,试图在发生变更时将我的 UI 状态与服务器状态同步。由于我可以使用变更响应来避免新的 API 调用,因此我使用了 React-query 提供的 `setQueryData` 功能。"

The problem is that the old-data is being correctly modified (I can see it in the react-query DevTools) when a mutation is successful, but the component using it isn't being re-rendered, making my UI State not synchronized with my Server state (well, at least the user can't see the update).

"问题是,当变更成功时,旧数据被正确修改(我在 React-query DevTools 中可以看到),但是使用它的组件没有重新渲染,这导致我的 UI 状态与服务器状态不同步(好吧,至少用户无法看到更新)。"

Let me show some code and hope someone can give me some insights.

"让我展示一些代码,希望有人能给我一些见解。"

Component using the query:        使用查询的组件

const Detail = ({ orderId }) => {
  const { workGroups } = useWorkGroups();
  const navigate = useNavigate();

  const queryClient = useQueryClient();
  const orderQueries = queryClient.getQueryData(["orders"]);
  const queryOrder = orderQueries?.find((ord) => ord.id === orderId);

// more code

Component mutating the query:        "变更查询的组件:"

const Deliver = ({
  setIsModalOpened,
  artisan,
  index,
  queryQuantity,
  queryOrder,
}) => {
  const [quantity, setQuantity] = useState(() => queryQuantity);

  const { mutate: confirmOrderDelivered } = useMutateOrderDeliveredByArtisan(
    queryOrder.id
  );

  const onSubmit = () => {
    confirmOrderDelivered(
      {
        id: queryOrder.artisan_production_orders[index].id,
        artisan: artisan.user,
        items: [
          {
            quantity_delivered: quantity,
          },
        ],
      },
      {
        onSuccess: setIsModalOpened(false),
      }
    );
  };

// more code

Now the mutation function (ik it's a lot of logic but I dont' want to refetch the data using invalidateQueries since we're dealing with users with a really bad internet connection). Ofc you don't need to understand each step of the fn but what it basically does is update the old queried data. In the beginning I thought it was a mutation reference problem since React using a strict comparison under the hood but I also checked it and It doesn't look like it's the problem. :

"现在是变更函数(我知道逻辑很多,但我不想使用 `invalidateQueries` 来重新获取数据,因为我们正在处理网络连接非常差的用户)。当然,你不需要理解函数的每一步,但它基本上是在更新旧的查询数据。一开始我认为这是一个变更引用问题,因为 React 在内部使用严格比较,但我也检查过,这似乎不是问题所在。"

{
      onSuccess: (data) => {
        queryClient.setQueryData(["orders"], (oldQueryData) => {
          let oldQueryDataCopy = [...oldQueryData];
          const index = oldQueryDataCopy.findIndex(
            (oldData) => oldData.id === orderId
          );

          let artisanProdOrders =
            oldQueryDataCopy[index].artisan_production_orders;

          let artisanProductionOrderIdx = artisanProdOrders.findIndex(
            (artProdOrd) => artProdOrd.id === data.id
          );

          artisanProdOrders[artisanProductionOrderIdx] = {
            ...artisanProdOrders[artisanProductionOrderIdx],
            items: data.items,
          };

          const totalDelivered = artisanProdOrders.reduce((acc, el) => {
            const delivered = el.items[0].quantity_delivered;
            return acc + delivered;
          }, 0);

          oldQueryDataCopy[index] = {
            ...oldQueryDataCopy[index],
            artisan_production_orders: artisanProdOrders,
            items: [
              {
                ...oldQueryDataCopy[index].items[0],
                quantity_delivered: totalDelivered,
              },
            ],
          };
          return oldQueryDataCopy;
        });
      },

      onError: (err) => {
        throw new Error(err);
      },
    }

And last but not least: I already checked that the oldQueryData is being correctly modified (console loging in the onSuccess fn in the mutation response) and, as I said before, the data is correctly modified in the React-query DevTools.

"最后但同样重要的是:我已经检查过 `oldQueryData` 被正确修改了(在 mutation 响应的 `onSuccess` 函数中进行了控制台日志记录),并且正如我之前所说,数据在 React-query DevTools 中也被正确修改。"

I know this is a lot of code and the problem seems to be complex but I really believe that it might be a really easy thing that I'm not pointing out because of how tired I already am.

"我知道这段代码很多,问题似乎很复杂,但我真的相信这可能是一个非常简单的事情,我没有指出来是因为我已经很疲惫了。"

Thanks!

问题解决:

Well, I fixed it in the worst possible way imho, so I will answer this question but I really would like to read your thoughts.

"好吧,我以我认为最糟糕的方式修复了它,所以我会回答这个问题,但我真的很想听听你的想法。"

It looks like the new query data setted on the expected query is re-rendering the component only if the mutation function is located in the component that we actually want to re-render.

"看起来,如果 mutation 函数位于我们实际想要重新渲染的组件中,那么设置在预期查询上的新查询数据只会重新渲染该组件。"

With that in mind what I did was just colocate my mutation function in the parent component and pass it down through the child component.

"考虑到这一点,我所做的就是将我的 mutation 函数放在父组件中,并通过子组件传递下去。"

Something like this:        类似这样

const Detail = ({ orderId }) => {
  const { workGroups } = useWorkGroups();
  const navigate = useNavigate();

const { mutate: confirmOrderDelivered } = useMutateOrderDeliveredByArtisan(
    queryOrder.id
  ); ==============> THE MUTATION FN IS NOW IN THE PARENT COMPONENT

  const queryClient = useQueryClient();
  const orderQueries = queryClient.getQueryData(["orders"]);
  const queryOrder = orderQueries?.find((ord) => ord.id === orderId);

// more code

First child:        第一个子类

const Assigned = ({ artisan, queryOrder, index, confirmOrderDelivered }) => {

// THE IMPORTANT PART HERE IS THE PROP BEING PASSED DOWN.

 <Modal
          isOpen={isModalOpened}
          toggleModal={setIsModalOpened}
          // className="w312"
          width="80%"
          height="fit-content"
          justifyCont="unset"
          alignItems="unset"
        >
          <Deliver
            setIsModalOpened={setIsModalOpened}
            artisan={artisan}
            queryQuantity={quantity}
            queryOrder={queryOrder}
            index={index}
            confirmOrderDelivered={confirmOrderDelivered} => HERE
          />
        </Modal>

Component that actually needs the mutation fn:

"实际上需要 mutation 函数的组件:"

const Deliver = ({
  setIsModalOpened,
  artisan,
  index,
  queryQuantity,
  queryOrder,
  confirmOrderDelivered,
}) => {
  const [quantity, setQuantity] = useState(() => queryQuantity);

  const onSubmit = () => {
    confirmOrderDelivered( => HERE.
      {
        id: queryOrder.artisan_production_orders[index].id,
        artisan: artisan.user,
        items: [
          {
            quantity_delivered: quantity,
          },
        ],
      }
    );
  };

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

营赢盈英

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值