英文:
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
的清理函数没有帮助,这不像购买产品的例子。
根据下面的讨论,可用的选项是:
- 使用查询而不是变异。但这不会在语义上正确。
- 强制用户再次点击按钮来启动“确认电子邮件”变异。这是重复的,不用户友好的。
- 使用
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:
- Using a query instead of a mutation. But this won't be semantically
correct. - Force the user to click -again- on a button to initiate the "confirm email" mutation. Which is repetitive and not user-friendly.
- 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论