EmuLabZip logo EmuLabZip

React Native state

Last updated · Edit this page on GitHub

Frameworks → React Native → State and emulabzip rn state read the live state of a React Native development app: Redux, React Query, React context and the stores your own code registers. --watch follows Redux actions and prints what each one changed.

emulabzip rn state com.example.shop
emulabzip rn state com.example.shop --watch
emulabzip rn state com.example.shop --json

It needs what React Native already needs: a development build, running Metro, and one debugger at a time.

What is read#

SourceHow it is found
ReduxA Provider (react-redux) whose store prop has getState, dispatch and subscribe; the state is getState()
React Query (TanStack Query)A provider with a client: every query of its cache, with the key, status, fetch status, when it was updated, the error and the data
React contextContext providers on screen, by their displayName (or the component around them) and their value. Redux's and React Query's own contexts are not listed twice
Named storesAnything you register in globalThis.__EMULABZIP_STORES__

Zustand, MobX, Jotai and the rest#

Register the store by name; EmuLabZip calls getState() (or get()), or reads the object as it is:

import {create} from 'zustand';
const useSettings = create(() => ({units: 'metric'}));
globalThis.__EMULABZIP_STORES__ = {settings: useSettings};

Nothing is installed in the app, and nothing happens if the name is never read. In a release build the registration is the only thing that works at all: React keeps no DevTools hook there, so Redux, React Query and contexts are not found.

How values are shown#

The page script turns state into JSON, and says what it left out. Cycles are marked {"$":"circular"}, functions {"$":"function"}, and Dates, Maps, Sets, errors, bigints and typed arrays are marked with their kind. It stops at 8 levels, 200 keys or items per object, 2,000 characters per string and about 1 MB, and the cut list says where.

Following actions#

--watch (and Watch changes) wraps dispatch of each Redux store, from then on, and keeps the last 200 actions with their type and payload. Every second it prints the new actions and a path-level difference of the state:

action cart/add
  redux/cart/items/0/qty: 1 → 2

When you stop, dispatch is put back and the log is removed from the page. It is the one thing in this phase that changes the running app, and only for as long as you watch. A component that took dispatch before you started keeps the old one, so its actions are not in the log until it renders again.

Limits#

  • Flutter state (Provider, Riverpod, Bloc) is not read.
  • Redux is found through a react-redux Provider; a store used without one is found if you register it.
  • Values are a snapshot: a very large state is cut, and getState() of a store with side effects in its getters runs them.