Skip to content

fix: Linked table nested form lifecycle - preserve parent form state across child form sessions#591

Open
najuna-brian wants to merge 1 commit intoOpenDataEnsemble:devfrom
najuna-brian:linked-table-support
Open

fix: Linked table nested form lifecycle - preserve parent form state across child form sessions#591
najuna-brian wants to merge 1 commit intoOpenDataEnsemble:devfrom
najuna-brian:linked-table-support

Conversation

@najuna-brian
Copy link
Copy Markdown
Contributor

Description

Summary

Linked-table question types in AnthroCollect need to open a child form on top of a parent form, wait for the child to be completed or dismissed, and then return to the parent form exactly where it was — same page, same field state, same scroll position. This matches the behavior that existed in the original OMO repo running on top of ODK-X.

This PR fixes the root cause: the native Formulus app was destroying the parent form modal before opening the child form, making state restoration impossible.

image

Problem

The original App.tsx maintained a single formplayer modal instance. When openFormplayer was called while a modal was already visible, it explicitly closed the existing modal first:

// Old code — parent is torn down before child opens
if (formplayerVisibleRef.current) {
  setFormplayerVisible(false);
  await new Promise<void>(resolve => setTimeout(() => resolve(), 300));
}

This meant:

  • The parent form's React state was unmounted and lost
  • The parent's current page position was lost
  • The parent's WebView was destroyed and would have to reinitialise from scratch
  • Returning to the parent after the child completed required the parent to reload entirely

Solution

Replace the single-modal design with a modal stack. Each openFormplayer call pushes a new entry onto the stack. The parent modal stays mounted underneath the child modal. When the child closes, the parent becomes the top of the stack again and receives a focus notification so the form JS layer can react.

Key concepts

Concept Detail
formplayerStack Array of FormplayerStackEntry objects, one per open form level
isActive prop Only the top-of-stack modal is isActive=true; all others are false
Submission guard Submission handler only registers when visible && isActive - inactive parent modals don't compete
State preservation Form state reset only fires when visible goes false, not when isActive changes
Focus handoff When isActive transitions false → true on a ready WebView, onReceiveFocus() is injected into the WebView JS context
Retry initialiser initializeStackEntry polls for the modal ref up to 20 times × 100ms to handle slow mounts before calling initializeForm

@najuna-brian najuna-brian requested a review from r0ssing April 8, 2026 14:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant