React Forum
Blog & Guide Responses
Dec 10, 2022 · 04:00 AM

Escape Hatches - discussion (2022-12-10)

Started by Soren Malik
externalreact.devescape hatchesrefsimperativeinterop

I keep coming back to the idea that every real app ends up needing a few escape hatches, but the hard part is keeping them from becoming the default path for everything that feels difficult in React.

Read the article

Where do you draw the line between a pragmatic escape hatch and an interop hole that never gets closed? Do you treat imperative DOM reads and writes as a route posture (declared and visible), or as a local component detail? What has helped your team keep escape hatches contained as the app grows?

Comments (10)

Back to latest
Mina Calder
Dec 10, 2022 · 04:06 AM

The only thing that worked for us was making escape hatches visible in the UI in dev builds.

Dmitri Kline
Dec 10, 2022 · 04:14 AM

If you want them contained, you need a contract story for them.

We started treating them like route posture keys (even if the implementation is local), so the behavior is explainable.

Related: Testing and Debugging and Components and Composition.

Inez Park
Dec 10, 2022 · 04:22 AM

A concrete pattern that helped: consolidate all imperative reads into one hook and render a tiny evidence marker when it runs.

tsx
function useMeasureOnce(ref: React.RefObject<HTMLElement>) {
  const [size, setSize] = React.useState<{ w: number; h: number } | null>(null);
  React.useLayoutEffect(() => {
    const el = ref.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    setSize({ w: Math.round(r.width), h: Math.round(r.height) });
  }, []);
  return size;
}

If it stays centralized, it does not spread like mold through the codebase.

Keira Santos
Dec 10, 2022 · 04:31 AM

Counterpoint: turning escape hatches into a big governance thing can slow teams down.

Sometimes you just need to bridge a library and move on.

Soren Malik
Dec 10, 2022 · 04:36 AM

I agree with the spirit. I am not trying to ban them.

The issue is the quiet accumulation: a year later, nobody can tell which imperative bits are load-bearing and which were just expedient.

Keira Santos
Dec 10, 2022 · 04:40 AM

That is fair. The visible marker idea is a good middle ground.

Hana Saito
Dec 10, 2022 · 04:49 AM

We use an internal rule: escape hatches are allowed, but they must be wrapped in a component that reads like a real abstraction.

If you cannot name it, you probably should not do it.

Rowan Pierce
Dec 10, 2022 · 05:01 AM

One thing the react.dev piece does well is framing: the goal is not purity, it is keeping the model consistent.

If every screen is a snowflake of imperative glue, debugging gets impossible.

Avery Chen
Dec 10, 2022 · 05:12 AM

A misunderstanding I see a lot: people think escape hatch means put everything in refs.

The safer move is often the opposite: keep the state in the open, and only use the ref to talk to the outside world.

Mina Calder
Dec 10, 2022 · 05:18 AM

Yes. Refs as a back channel are fine; refs as a shadow store are where things get weird.

Priya Liao
Dec 10, 2022 · 05:27 AM

We did a simple lint-level convention: any imperative bridge exports a single function that is easy to grep for.

It sounds boring, but it keeps interop from spreading silently.

Benji Rios
Dec 10, 2022 · 05:38 AM

If you like the forum docs posture: treat escape hatches as a budgeted resource.

You can spend it, but you should be able to answer where you spent it.

Salma Qureshi
Dec 10, 2022 · 05:52 AM

Practical heuristic: if the escape hatch needs retries, timeouts, or lifecycle management, it is a route-level concern.

At that point, I want it declared as posture and rendered as evidence somewhere in the shell.