比如下面的代码,用一个简单 submitting 来说明(不是实际的业务需求,只是个演示)。
如果真的要在回调里去读取当前的 submitting 状态,有什么方法吗?
我知道有一个 useEffect hook ,去监听 submitting 状态的变更。但是感觉这样好麻烦,实际业务中,状态一多,就是一团麻的感觉。另外,有时候状态变更并不能很精准的表达实际发生的事情。就拿这个 submitting 来说。它可能是用户点击了 button 触发的,也可能是通过其它事件方式触发的,如果这些触发的事件也是业务需要考虑的,那简单的监听 submitting 变更似乎不能满足业务需求(可能还要加个类似 trigger by 的状态?)。
import { useState } from "react";
export const DebugPage = () => {
const [submitting, setSubmitting] = useState(false);
const handleSubmit = () => {
setSubmitting(true);
// 用 setTimeout 模拟异步请求
setTimeout(() => {
// 在异步回调的时候,这个 submitting 的状态依然是 false 。
// 这个我能理解。因为这个生成这个词法闭包上的时候 submitting 就是 false 。
//
// 但是假如我需要在这里读取 submitting 当前的准确状态,要怎么做?有办法做到吗?
console.log('submitting: ', submitting);
setSubmitting(false);
}, 1000);
}
return (
<button onClick={handleSubmit}>{submitting ? 'Submitting' : 'Submit'}</button>
)
}
不知道有没有描述清楚我的问题。谢谢各位~
1
sukinee 230 天前
useRef
|
2
Leviathann 230 天前
用 ref 记录 submitting ,每次 setState 的时候都同步修改 ref
相当于 setState 只用作触发 rerender |
3
lisongeee 230 天前
```js
const [submitting, setSubmitting] = useState(false); const onClick = async () => { setSubmitting(true); const r = await fetch('/api/hello').then((r) => r.json()); const latestValue = await new Promise<boolean>((res) => { setSubmitting((v) => (res(v), v)); }); console.log(latestValue) }; ``` |
4
weixind 230 天前
当你想多处触发同一个 submitting 的时候思路已经有点偏了。state 状态要分离,应该有个 handleSubmitting 。或者直接用 useQuery 之类的东西。你提交的 loading 就是提交的 loading 。其他的 loading 和他样式可以一样,但是逻辑不要揉到一块。如果你觉得 state 多了会是一团乱麻。多处逻辑更改同一个 state 才更是一团乱麻。追踪变更都追踪不到。
|
5
justdoit123 OP @lisongeee setSubmitting 可以传入一个函数来修改当前值这个我知道。需求是要在 callback 里就能读到状态的当前值。
|
6
justdoit123 OP @Leviathann 这种情况真的只能加上 ref 了吗?捂脸
|
7
Puteulanus 230 天前
|
8
lisongeee 230 天前
latestValue 不就是状态的当前值吗?
你是没理解我使用的 async/await 吗?我直接发你贴的代码转换之后的完整代码吧 ```tsx import { useState } from 'react'; export const DebugPage = () => { const [submitting, setSubmitting] = useState(false); const handleSubmit = async () => { setSubmitting(true); // 用 setTimeout 模拟异步请求 await new Promise((res) => setTimeout(res, 1000)); // 但是假如我需要在这里读取 submitting 当前的准确状态,要怎么做?有办法做到吗? const latestSubmitting = await new Promise<boolean>((res) => { setSubmitting((v) => (res(v), v)); }); console.log(latestSubmitting); }; return ( <button onClick={handleSubmit}> {submitting ? 'Submitting' : 'Submit'} </button> ); }; ``` |
9
justdoit123 OP @weixind 这个 use-query 看着不错。
关于 状态 还是 set 多不展开讨论。 我会问这个问题,是因为我在实现一个连续 timer 的需求。 1. timer 一开始是停止的,有个按钮让用户点开始;再点一次就停止。 2. 一个 timer 时间到了,需要提交数据,然后自动开始下一个 timer ,直到用户点停止为止。 那么如何在一个 timer 数据提交后,自动触发下一个?直接调用 startTimer 肯定是不行的,因为里面包含的 state 都是“旧”的。 我目前能想到的是通过 pubsub 来绕过这个问题,或者就像 1, 2 楼 说的用 ref 。 |
10
justdoit123 OP @lisongeee 哇哦,还有这种用法。前面没有细看,明白了。
|
11
justdoit123 OP @Puteulanus 谢谢~ 捂脸
|
12
justdoit123 OP 暂时采用 ref 的方式解决。把一些 handler 放入 ref ,类似这样:
```tsx const handlersRef = useRef<{ submitLog?: typeof submitLog; startClock?: typeof startClock; }>({}); ``` |
13
codehz 230 天前 2
我建议用一下 swr 这类请求封装好的吧,你这样滥用 swr 迟早会出事()
另外正统的读 state 当前值的方法是传递一个函数给 setState(current => { /* play with current */ return current; }) |
14
codehz 230 天前
异步请求除了处理中,还有分页(无限滚动),缓存,失败,重试,自动刷新等需求,全部集合到一起,你就重新发明了 swr ,倒不如一开始就直接用
|
15
yukinotech 230 天前
@codehz setState(current => { /* play with current */ return current; }) 太艹了
|
16
northquq 229 天前
|
17
sh666 229 天前
setSubmitting((prev) => {
console.log('submitting: ', prev); return prev; }); setSubmitting(false); |
18
rookie2luochao 229 天前
建议用一下,react-query ,swr 这种支持 promise 异步 http 请求的库,用 useQuery, useMutation 这种 hook, 可以同步请求的 loading 状态,demo 请看:
https://github.com/rookie-luochao/create-vite-app-cli/tree/master/template-react-ts#%E8%B0%83%E7%94%A8%E6%8E%A5%E5%8F%A3react-query-%E6%94%AF%E6%8C%81%E8%87%AA%E5%8A%A8loading%E5%92%8C%E6%8E%A5%E5%8F%A3%E8%AF%B7%E6%B1%82%E8%81%94%E5%8A%A8 |
19
rookie2luochao 229 天前
如果你不想用,而你的目标就是更好的控制 loading, react19 提供了一个新的 hook 来同步这种 loading ,叫 useTransition
|