In a real app
The rule that keeps Fast Refresh reliable is simple: one file, one exported component, nothing else. Constants, utility hooks, and helpers go in their own files. A component defined in a file that also exports a config object or a function loses its state on every edit, and an anonymous export default (export default () => …) prevents React from identifying it. To force a remount on every save, say on a screen whose initialization state matters, the // @refresh reset directive at the top of the file does it.
When “it stopped refreshing”, the usual causes in order: a syntax error still present (the redbox goes away once fixed), a module that threw during initialization, an edited file imported by code outside the React tree (a store created at module level, a navigation config) which forces a full reload, or a saturated Metro watcher on a large monorepo (Watchman helps on macOS).
Fast Refresh only exists in debug with Metro connected; a development build loading a published update does not have it. And it only covers JavaScript: a native change still needs a recompile.
Related terms
Metro
React Native’s JavaScript bundler: it resolves, transforms, and serializes your code into a bundle, and runs the dev server.
React Native DevTools
React Native’s built-in debugger since 0.76, based on Chrome DevTools: console, breakpoints, network, profiling, React tree.
Development build
A debug build of your own app that includes expo-dev-client: your native modules, plus a launcher to connect to Metro.
Expo Go
Expo’s sandbox app, with a fixed set of native modules: for learning and prototyping, not for shipping.