求助:Remix中如何让每次Submit获取最新Action返回结果
解决Remix中useSubmit返回Promise获取最新action结果的问题
核心修复方案
解决这个问题的关键是每次触发submit时创建全新的deferred实例,并通过navigation.key关联当前请求对应的actionData,确保只resolve本次submit的结果:
- 移除对deferred的持久化,改为每次调用submit时创建新实例
- 用ref存储当前活跃的deferred和请求key,避免组件重渲染丢失引用
- 监听navigation状态,仅当本次请求完成时,用当前deferred resolve最新的actionData
修改后的完整代码
import type { ActionArgs, LoaderArgs } from '@remix-run/node'; import { json } from '@remix-run/node'; import type { SubmitOptions } from '@remix-run/react'; import { useActionData, useNavigation, useSubmit } from '@remix-run/react'; import { useCallback, useEffect, useRef } from 'react'; export function loader({ request }: LoaderArgs) { return json({ message: 'Hello World' }); } export function action({ request }: ActionArgs) { let x = Math.random(); console.log('generated ' + x); return json({ message: x }); } export default function Index() { const submit = useSubmitPromise(); const doSubmit = useCallback( (e: any) => { console.log('doSubmit'); e.preventDefault(); submit({ test: '123' }, { method: 'post', action: '?index' }).then( (data) => { console.log('result', data); alert('Resolved promise with:\n' + JSON.stringify(data, null, 2)); } ); }, [submit] ); return ( <div> <h1>Welcome to Remix</h1> <button onClick={doSubmit}>useSubmitPromise</button> </div> ); } // remix doesn't export this type declare type SubmitTarget = | HTMLFormElement | HTMLButtonElement | HTMLInputElement | FormData | URLSearchParams | { [name: string]: string; } | null; function useSubmitPromise() { const submit = useSubmit(); const navigation = useNavigation(); const actionData = useActionData(); const currentDeferredRef = useRef<ReturnType<typeof deferred> | null>(null); const currentNavigationKeyRef = useRef<string | undefined>(undefined); useEffect(() => { // 仅当本次请求完成时,resolve对应的Promise if ( navigation.state === 'idle' && currentDeferredRef.current && currentNavigationKeyRef.current === navigation.key ) { currentDeferredRef.current.resolve(actionData); // 重置ref,避免下次请求混淆 currentDeferredRef.current = null; currentNavigationKeyRef.current = undefined; } }, [navigation.state, navigation.key, actionData]); const _submit = useCallback( (target: SubmitTarget, options: SubmitOptions = {}) => { // 为本次请求创建新的deferred const newDeferred = deferred(); currentDeferredRef.current = newDeferred; currentNavigationKeyRef.current = navigation.key; submit(target, options); return newDeferred.promise; }, [submit, navigation.key] ); return _submit; } // create a *deferred* promise function deferred() { let resolve: typeof Promise.resolve; let reject: typeof Promise.reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { resolve, reject, promise }; }
修复说明
- 每次调用
_submit都会生成独立的deferred,彻底避免Promise复用问题 - 通过
currentNavigationKeyRef匹配请求,确保resolve的是本次submit对应的actionData - 请求完成后重置ref,保证下一次submit的独立性,不会出现数据滞后
内容的提问来源于stack exchange,提问作者sinanspd
相关产品推荐
相关产品推荐

