Beyond the Hook: Engineering Fine-Grained Reactivity and Signals in Universal Expo Apps
An exploration into bypassing React's re-render cycle using Signals to optimize performance across Web, iOS, and Android. Learn how to implement fine-grained reactivity in high-performance Expo applications.

For years, my workflow in React and Expo has been defined by the hook. useState, useEffect, useMemo—these are the tools of our trade. But as I’ve pushed Expo apps into more complex, data-heavy territories (think real-time trading dashboards or collaborative whiteboards), I’ve started hitting a ceiling. The reconciliation process, while brilliant, becomes a bottleneck when you're managing hundreds of frequently changing data points across Web, iOS, and Android.
In my recent projects, I’ve been moving away from standard component-level state toward a fine-grained reactivity model using Signals. Here’s why this shift is a game-changer for universal engineering.
The Problem: The Re-render Tax
In a standard Expo app, when you update a piece of state in a parent component, React does its thing: it re-renders the component and its children, calculates the diff, and updates the native views. On a high-end iPhone, you might not notice. On a mid-range Android device or a low-spec browser, the frame drops are real.
We often try to fix this with memo, but memo has its own overhead. We’re essentially paying a 'tax' to keep our UI in sync with our state.
Enter Signals
Signals represent a different mental model. Instead of a value that triggers a component refresh, a Signal is an observable object that holds a value. When that value changes, only the specific part of the UI that subscribes to that signal updates. The component itself doesn't necessarily re-render.
In the Expo world, this is massive. By using a library like @legendapp/state or Preact Signals (adapted for React), we can update a text string or a view’s opacity without touching the React Fiber tree.
Engineering the Solution
Let’s look at a practical example. Imagine a real-time price feed in a universal app. Using standard hooks, every tick would re-render the entire row.
Now, look at the Signal-based approach using Legend-State, which I've found to be incredibly performant in Expo environments:
The Universal Breakthrough: Bypassing the Bridge
The real magic happens when we combine signals with react-native-reanimated. In universal apps, performance often dies at the bridge (the communication layer between JS and Native).
I’ve been experimenting with "Signal-to-Native" patterns where the signal directly drives a Shared Value. This allows us to keep the logic in a reactive, easy-to-read state machine while ensuring the UI thread handles the heavy lifting on mobile, and the DOM handles it efficiently on Web.
Why This Matters for Expo
- Lower Battery Consumption: Less CPU time spent on reconciliation means better battery life on mobile devices.
- Web Parity: Fine-grained reactivity is becoming the standard on the web (SolidJS, Qwik, Vue). Bringing this to Expo ensures our universal apps aren't left behind by 'Web-only' performance optimizations.
- State Management Sanity: Signals allow us to hoist state out of the component tree without the boilerplate of Redux or the context-hell of deeply nested providers.
The Shift in Mindset
Moving to signals isn't just about changing a function call; it's about thinking in Streams rather than Snapshots. Instead of React taking a snapshot of your state and figuring out what changed, you are explicitly defining the path of change from the data source to the UI node.
In my experience, the initial learning curve is offset almost immediately by the lack of 'why did this re-render?' debugging sessions. If you're building high-performance universal apps with Expo, it's time to look beyond the hook. The future is fine-grained.