useTransition的isPending未等待API调用完成就变为false的问题求助
我正在尝试解决
useTransition钩子的问题。我有一个按钮,点击它会触发一个发起API调用的函数。问题在于useTransition返回的isPending在API调用完成前就被设置为false了。
以下是我的代码:
function App() { const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); const [isPending, startTransition] = useTransition(); const [data, setData] = useState(null); useEffect(()=>{ console.log("Is Pending changed", isPending) },[isPending]) const fetchData = async () => { startTransition(async () => { await delay(2000); const response = await fetch('https://jsonplaceholder.typicode.com/todos/1'); // Replace with your API endpoint const result = await response.json(); setData(result); }); }; return ( <div> <button onClick={fetchData}>Fetch Data</button> {isPending ? ( <p>Fetching data...</p> ) : ( data && <pre>{JSON.stringify(data, null, 2)}</pre> )} </div> ); }
我原本期望
useTransition会像文档里说的那样等待API调用完成,是不是我哪里用错了?
嘿,我来帮你捋清楚这个问题!问题的核心是你对useTransition的作用理解有点偏差——它并不是用来跟踪异步请求加载状态的工具,而是用来标记状态更新的优先级,让React知道某些状态更新可以延迟执行,优先响应用户的交互(比如点击、输入操作)。
具体到你的代码,startTransition接受的是一个同步函数,它只会跟踪这个同步函数执行过程中触发的状态更新。你把异步函数传给它之后,startTransition会立刻执行完这个异步函数的同步阶段(也就是进入await delay(2000)之前的代码),然后就认为过渡已经结束了,所以isPending马上就变成了false。而后面的API请求和setData都是在异步任务里执行的,这时候过渡早就结束了,自然不会再触发isPending的变化。
那该怎么调整呢?分两种场景给你方案:
场景1:想用isPending跟踪状态更新的过渡(而非API请求加载)
如果你的API请求本身很快,只是setData之后的渲染可能比较耗时,那应该把异步请求放在startTransition外面,等数据拿到后再用startTransition包裹状态更新:
function App() { const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); const [isPending, startTransition] = useTransition(); const [data, setData] = useState(null); // 自己维护一个状态跟踪API请求的加载 const [loading, setLoading] = useState(false); useEffect(()=>{ console.log("Is Pending changed", isPending) },[isPending]) const fetchData = async () => { setLoading(true); try { await delay(2000); const response = await fetch('https://jsonplaceholder.typicode.com/todos/1'); const result = await response.json(); // 拿到数据后,用startTransition包裹状态更新 startTransition(() => { setData(result); }); } finally { setLoading(false); } }; return ( <div> <button onClick={fetchData}>Fetch Data</button> {/* 用loading显示API加载状态,isPending显示状态更新的过渡 */} {loading || isPending ? ( <p>Fetching data...</p> ) : ( data && <pre>{JSON.stringify(data, null, 2)}</pre> )} </div> ); }
场景2:想让加载状态覆盖整个API请求过程
如果你的核心需求是在API请求的整个过程中显示加载状态,那useTransition其实不是合适的工具,直接自己维护一个loading状态就够了,useTransition可以用来优化状态更新时的UI体验(比如避免渲染卡顿):
function App() { const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); const [isPending, startTransition] = useTransition(); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const fetchData = async () => { setLoading(true); try { await delay(2000); const response = await fetch('https://jsonplaceholder.typicode.com/todos/1'); const result = await response.json(); // 如果渲染数据时可能卡顿,就用startTransition包裹 startTransition(() => { setData(result); }); } catch (err) { console.error('请求出错:', err); } finally { setLoading(false); } }; return ( <div> <button onClick={fetchData}>Fetch Data</button> {loading ? ( <p>Fetching data...</p> ) : isPending ? ( <p>Processing data...</p> ) : ( data && <pre>{JSON.stringify(data, null, 2)}</pre> )} </div> ); }
总结一下:useTransition的isPending只跟踪同步回调里的状态更新,异步操作的加载状态需要自己用useState来维护哦!
备注:内容来源于stack exchange,提问作者Vaibhav Tiwari

