RTK Query mutation 在 StrictMode 中执行两次

huangapple go评论65阅读模式
英文:

RTK Query mutation executes twice in StrictMode

问题

我使用RTK Query来获取数据,并且在一个使用案例中遇到了一个小问题。

我使用一个变异来验证应用程序用户的电子邮件地址,如下所示:

// ...导入语句

const ConfirmEmail = () => {
  const [params] = useSearchParams();

  const [confirmEmail, { isLoading, isSuccess, isUninitialized, error }] =
    useConfirmEmailMutation();

  useEffect(() => {
    if (isUninitialized) {
      confirmEmail({
        email: params.get('email'), // 从验证链接中获取
        code: params.get('code'), // 从验证链接中获取
      })
        .unwrap()
        .then(() => {
            // 处理成功...
        })
        .catch((e) => {
            // 处理错误...
        });
    }
  });

  // ...其他组件代码
}

问题是,在StrictMode下,在开发过程中变异会运行两次,这会导致API端发生并发错误。我在开发工具中看到两个网络请求,一个成功,另一个失败,取决于哪个先运行,组件会显示不一致的结果。

我知道这只会在开发过程中发生,并且我尝试遵循官方React文档中的说明,并尝试使用如此处所述的fixedCacheKey,但我无法使它在RTK Query中在开发过程中获得一致的结果。

是否有方法,还是我遗漏了什么?

**编辑:**我的问题与此问题属于相同的类别,但更具体于RTK Query。因为在RTK Query中,如果组件尝试进行与现有查询相同的查询,将不执行任何请求(如此处所述)。然而,我尝试进行的是一个变异,以确认用户的电子邮件地址(在通过电子邮件发送的链接上单击后)。变异不遵循与此处提到的规则相同。

useEffect的清理函数没有帮助,这不像购买产品的例子。

根据下面的讨论,可用的选项是:

  1. 使用查询而不是变异。但这不会在语义上正确。
  2. 强制用户再次点击按钮来启动“确认电子邮件”变异。这是重复的,不用户友好的。
  3. 使用ref来跟踪变异是否已经在运行。这是我最终实施的方式,如下所示:
// ...导入语句

const ConfirmEmail = () => {
  const verifiying = useRef(false);

  const [params] = useSearchParams();

  const [confirmEmail, { isLoading, isSuccess, isUninitialized, error }] =
    useConfirmEmailMutation();

  useEffect(() => {
    const isVerifying = verifiying.current;

    if (isUninitialized) {
      verifiying.current = true;

      confirmEmail({
        email: params.get('email'), // 从验证链接中获取
        code: params.get('code'), // 从验证链接中获取
      })
        .unwrap()
        .then(() => {
            // 处理成功...
        })
        .catch((e) => {
            // 处理错误...
        });
    }
  });

  // ...其他组件代码
}
英文:

I use RTK Query for data fetching and I am having a small issue in one of the use cases.

I use a mutation to verify the email address of the app user, as follows:

// ...imports

const ConfirmEmail = () => {
  const [params] = useSearchParams();

  const [confirmEmail, { isLoading, isSuccess, isUninitialized, error }] =
    useConfirmEmailMutation();

  useEffect(() => {
    if (isUninitialized) {
      confirmEmail({
        email: params.get('email'), // from the verification link
        code: params.get('code'), // from the verification link
      })
        .unwrap()
        .then(() => {
            // handling success...
        })
        .catch((e) => {
            // handling error...
        });
    }
  });

  // ...other component code
}

The problem is that with StrictMode the mutation is running twice in development and it is causing a concurrency error at the API side. I see two network requests in the dev tools one is successful and the other is not and depending on which one runs first the component is showing inconsistent result.

I am aware that this will only happen during development and I tried to follow the instructions in the official react documentation and I tried to use fixedCacheKey as described here but I wasn't able to make this work with RTK Query so that I get a consistent result during development.

Is there a way, or am I missing something?


Edit: my question falls into the same category as this question but it is more specific to RTK Query. Because in RTK Query if a component tries to make the same query as an existing one, no request will be performed (as mentioned here). However, what I am trying to do is a mutation in order to confirm the email address of a user (following a click on a link sent by email). Mutations do not follow the same rule as mentioned here.

useEffect cleanup function wasn't helpful and this is not like buying a product example.

Based on the discussions below, the available options were:

  1. Using a query instead of a mutation. But this won't be semantically
    correct.
  2. Force the user to click -again- on a button to initiate the "confirm email" mutation. Which is repetitive and not user-friendly.
  3. Use a ref to track whether a mutation is already running. Which is what I implemented at the end as follows:
// ...imports

const ConfirmEmail = () => {
  const verifiying = useRef(false);

  const [params] = useSearchParams();

  const [confirmEmail, { isLoading, isSuccess, isUninitialized, error }] =
    useConfirmEmailMutation();

  useEffect(() => {
    const isVerifying = verifiying.current;

    if (isUninitialized) {
      verifiying.current = true;

      confirmEmail({
        email: params.get('email'), // from the verification link
        code: params.get('code'), // from the verification link
      })
        .unwrap()
        .then(() => {
            // handling success...
        })
        .catch((e) => {
            // handling error...
        });
    }
  });

  // ...other component code
}

答案1

得分: 2

这基本上是严格性检查的目的:告诉你正在自动执行一个有问题的 API 调用。

将来,React 可能会选择在更多情况下执行具有空依赖数组的 useEffect,例如与他们正在开发的离屏特性结合使用。这只是提前告诉你,你可能在一个危险的地方这样做。

也许你可以将这个 API 调用与某种实际的用户交互结合起来,比如一个按钮点击?这对以后会更安全。

英文:

That's pretty much the point of this strictness check though: to show you that you are automatically doing a problematic api call.

In the future, React can choose to execute a useEffect with an empty dependency array on more occasions than just on first mount - for example in combination with the offscreen features they are working on.
This just tells you in advance that you are doing this in probably a dangerous place.

Can you maybe incorporate this api call in some kind of actual user interaction like a button click? That would be a lot safer going forward.

huangapple
  • 本文由 发表于 2023年2月19日 19:34:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/75499836.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定