You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
plugin-form: the redirect delayMs timer outlives the component — a submitter who leaves during the delay is yanked back by a full-page navigation #5033
Found while implementing objectui#4989 (the contract half of the same two call sites). Filed unassigned, NOT fixed there — #4989's card scopes itself to five named defects and this is not one of them.
What
ObjectForm and WizardForm arm the declared submitBehavior: { kind: 'redirect' } delay with a bare setTimeout inside the submit handler:
packages/plugin-form/src/ObjectForm.tsx (redirect arm of the submitBehavior switch)
Nothing ties that timer to the component. It is not stored, not cleared on unmount, and not owned by an effect. So for the whole of delayMs there is a pending full-page navigation that survives the form being taken off screen.
Consequence
A submitter who acts during the delay gets pulled away from wherever they went. The reachable shapes, in rising order of how ordinary they are:
the form is a modal/drawer variant (ModalForm, DrawerForm both wrap these), the user dismisses it after the confirmation and starts doing something else — then the timer fires and the browser leaves the page.
the host is an SPA and the user clicks any in-app link during the delay; the timer's window.location.assign overrides that navigation with a full page load.
the host unmounts the form for its own reasons (route change, tab switch, data refresh that re-keys a subtree).
delayMs exists precisely so the confirmation is readable before the redirect, so a non-trivial value is the intended authoring, not an edge case. The longer the declared delay, the wider the window.
Prior art in this repo — the same defect, already fixed on the console side
apps/console's FormPage had this and PR #4992 moved the wait into an effect keyed on the pending destination, with a clearTimeout cleanup. Its changeset states the reason in one line: "the wait now lives in an effect tied to the component, so a submitter who navigates away during the delay is no longer yanked back by a timer that outlived the page."
So the fix shape is already decided and demonstrated in this repo; this is the same move for the published renderer's two call sites. It should not change delayMs semantics at all — the pause stays a pause, an unset value still means "go now".
Reachability, honestly
Needs a form authoring a redirect behaviour WITH a delayMs, and an unmount or navigation inside that window. Nobody loses data — the write has already succeeded before the timer is armed — so the harm is a surprising navigation, not corruption. Cheap to fix and cheap to pin (arm the delay, unmount, assert nothing was assigned).
Not to be confused with
objectui#4989, which is about WHAT the url means (relative-only, interpolation, escaping, and refusing an out-of-contract value loudly). That card's PR leaves the timer exactly as it found it. This issue is only about the timer's lifetime.
Activity
yinlianghui commentedon Aug 17, 2026
认领(objectui 分片 PM 直辖,批次 22)—— session
session_01GTRjn8xBqp75dk7kFupVRt,分支claude/issue-5033-redirect-timer-lifetime。os-dev 席位实施,PM 只读验收。范围钉子:只修定时器生命周期 ——
ObjectForm.tsx/WizardForm.tsx两处 redirect 臂的setTimeout移入组件所有的 effect(照 PR #4992 在 console FormPage 已验证的形状:effect 键在待定目的地上,cleanupclearTimeout)。delayMs语义不变(停顿仍是停顿,未设仍是立即走);⛔ 不动 #4989/PR #5032 已落地的checkSubmitRedirectUrl契约裁定流。钉子测试:armed delay → unmount → 断言未发生任何 assign。Generated by Claude Code
fix(plugin-form): redirect 的 delayMs 等待改由组件持有,unmount 即取消 (objectstac…
fix(plugin-form): thank-you redirect 的等待改由组件持有,unmount 与「再填一份」均取消 (ob…