ryer.io

Why Our Modals Live Outside React's Hierarchy

TL;DR

  • Layered views capture touches before they reach the view underneath, and modals sit outside React’s hierarchy entirely.
  • Putting modals inside the hierarchy to get context access caused hierarchy-wide re-renders and unacceptable performance.
  • We moved to an event-driven architecture with a single unified context, passing data imperatively.
  • This year extends the same model to carry both program and user data into cards that each launch their own modal.

Reviewing last year’s architectural work ahead of a meeting, and it holds up better than I expected — mostly because the constraint that forced it hasn’t gone away.

The original problem

Managing touch gesture events in React Native across Android and iOS, specifically nested ones. When two views are layered, the touch gets captured by one and never reaches the view underneath.

What made it genuinely hard is that our hierarchy isn’t strictly parent-child. Modals exist outside the main hierarchy in React, so event propagation doesn’t follow the tree you’d draw on a whiteboard. The documentation didn’t have much to say about this case.

The approach that failed

Our first instinct was to embed modals within the React hierarchy so they could use context normally. It works, and it’s unusable: every state update triggered re-renders across the hierarchy, and performance degraded badly.

That’s the tradeoff in a sentence. Getting context access the idiomatic way costs you a re-render of everything above you, and modals update often enough that the cost is constant.

What we do instead

We pivoted to an event-driven architecture. Data flows imperatively through a unified single context, which lets us pass data to non-hierarchical modals without triggering hierarchy-wide re-renders.

It isn’t standard React, and I want to be clear-eyed that this is a deliberate departure rather than an accident. But the standard approach was measured and rejected, not skipped.

Where it goes this year

The data model has grown to support educational programs and related user cohorts, and the architectural philosophy carries over unchanged.

Each UI card now combines program and user data, and each card can launch a modal presenting independently managed content. Those modals call APIs to fetch their specific program and user data, which means handling payloads and references efficiently rather than passing everything down.

The new pressure is volume. Presenting this much data on screen needs predictable ordering and real attention to render performance — the same concerns as last year, just with more rows. The single-context event-driven approach is what makes it tractable: modal data stays cohesive without the hierarchy paying for every update.