Reactotron

repository·master·Indexed 12 days ago

https://github.com/infinitered/reactotron

An open-source desktop debugger for React and React Native developers to monitor state, network traffic, and performance in real-time. It includes a plugin system for extensibility, an ESLint plugin (eslint-plugin-reactotron) to prevent production leaks, and specialized integrations like reactotron-apisauce for network logging and reactotron-react-native-mmkv for storage monitoring.

Tokens
41.9K
Snippets
150
Records
194
Agent score
93%

What's inside Reactotron

  1. What is Reactotron?

    master

    Reactotron is a powerful debugger designed for React and React Native applications. It provides a desktop interface to monitor application state, network requests, and performance metrics. It is designed to be used as a development dependency, ensuring it adds nothing to your production build size.

    Key capabilities include:

    • State Monitoring: View application state, subscribe to specific parts of the state, and hot-swap state using Redux or mobx-state-tree.
    • Network Inspection: Show API requests and responses.
    • Logging & Debugging: Display messages (similar to console.log) and track global errors with source-mapped stack traces (including saga stack traces).
    • Performance: Perform quick performance benchmarks.
    • React Native Specifics: Show image overlays and track Async Storage.
    • Action Dispatching: Dispatch actions directly to the application.
    • Extensibility: A powerful plugin system allows for custom enhancements.
  2. Understand the Command and Payload structure

    master

    The contract defines how messages are structured between the client and server.

    CommandType Enum

    Use the CommandType object to access constant strings for all available command types (e.g., CommandType.Log, CommandType.ApiResponse).

    Payload Types

    Each command has a corresponding payload type (e.g., LogPayload, ApiResponsePayload) that defines its expected structure.

    Command Interface

    All messages follow the Command interface:

    • type: The CommandTypeKey.
    • connectionId: Unique connection identifier.
    • clientId: Optional client identifier.
    • date: Timestamp of the command.
    • deltaTime: Time difference.
    • important: Boolean flag for priority.
    • messageId: Unique message identifier.
    • payload: The typed data associated with the command.
    • diff: Optional diff object.
    import type { Command, CommandTypeKey } from "reactotron-core-contract"
    
    interface Command<Type extends CommandTypeKey, Payload> {
      type: CommandTypeKey
      connectionId: number
      clientId?: string
      date: Date
      deltaTime: number
      important: boolean
      messageId: number
      payload: Payload
      diff?: any
    }
  3. Reactotron Superpowers and Features

    master

    Reactotron provides several debugging capabilities, including:

    • State Monitoring: View application state, subscribe to parts of it, and hot swap state using Redux or mobx-state-tree.
    • Networking: Show API requests and responses.
    • Performance: Perform quick performance benchmarks.
    • Logging: Display messages similar to console.log and track global logs.
    • Error Tracking: Track global errors with source-mapped stack traces (including saga stack traces).
    • React Native Specifics: Track Async Storage, show image overlays, and integrate with React Native MMKV.
    • Plugin Support: Includes support for apisauce, mst, redux, storybook, and more.
  4. How the Reactotron MCP server architecture works

    master

    The Model Context Protocol (MCP) server runs directly inside the Reactotron desktop application as a separate package (reactotron-mcp). It functions by reading directly from the relay server's connections and event stream.

    Key architectural benefits:

    • No Proxy Required: It does not sit between your app and the relay.
    • No Separate Process: It is integrated into the Reactotron desktop app.
    • Zero App Changes: You do not need to modify your React Native application to enable MCP support.

    Data flow:

    1. Your React Native app sends data via WebSocket (default port 9090).
    2. Reactotron Desktop receives this via the relay server (reactotron-core-server).
    3. The MCP server (reactotron-mcp) reads from the relay server and exposes an HTTP interface for tools like Claude Code to consume.
    React Native app
        | WebSocket (port 9090, unchanged)
        v
    Reactotron Desktop
        ├── relay server (reactotron-core-server)
        └── MCP server (reactotron-mcp, HTTP on configurable port)
              ↑
    Claude Code
  5. What can Claude Code do with the Reactotron MCP server?

    master

    The MCP server provides Claude Code with two main capabilities:

    1. Read debug data

    Claude can access several resources on demand:

    • Timeline: Summarized debug events (type, timestamp, preview).
    • Timeline by Type: Full event data filtered by command type (e.g., api.response, log).
    • App State: The latest cached Redux/MST state snapshot.
    • Network Log: HTTP requests and responses with truncated body previews.
    • Connected Apps: List of connected apps, platforms, and versions.
    • Benchmarks: Performance benchmark results.
    • State Subscriptions: Values at subscribed state paths.
    • AsyncStorage: All AsyncStorage mutations (setItem, removeItem, etc.).

    2. Send commands

    Claude can interact with your running app via these tools:

    • Dispatch Redux actions: e.g., "dispatch a RESET action".
    • Explore state keys: List keys at a state path without fetching values.
    • Request a fresh state snapshot: Fetch current state at a specific path.
    • Replace app state: Hot-swap the entire state tree.
    • Send custom commands: Trigger custom commands registered in your app.
    • Show image overlay: Overlay a design mockup (PNG, JPEG, GIF) on the app.
    • Subscribe to state paths: Watch specific parts of state for changes.
    • Clear the event buffer: Reset the timeline to focus on new interactions.
  6. Create a Reactotron plugin

    master

    Reactotron is extensible via a plugin system using the client.use() method. A plugin is a higher-order function that follows this structure:

    1. Configuration Function: The outer function used to pass configuration to the plugin.
    2. Implementation Function: Receives the reactotron instance. It must return an object containing hooks.

    Plugin Hooks:

    • onCommand: Triggered when the server sends a command.
    • onConnect / onDisconnect: Lifecycle hooks for the connection.
    • onPlugin: Called once when the plugin is attached.
    • features: An object where keys become new methods on the Reactotron client instance (mixins).
    // Example plugin implementation
    export default (config) => (reactotron) => {
      return {
        onCommand: (command) => {
          const { type, payload } = command
          console.log("Received:", type)
        },
        features: {
          // This adds 'Reactotron.log()' to the client
          log: (message) => reactotron.send('log', { level: 'debug', message }),
        }
      }
    }
    
    // Usage
    client.use(myPlugin({ someConfig: true }))
  7. Configure the reactotron-mst plugin

    master

    To enable the plugin, import mst from reactotron-mst and pass it to your Reactotron instance using .use().

    import { mst } from "reactotron-mst"
    
    // Tell Reactotron to use this plugin
    Reactotron.use(mst())
    import { mst } from "reactotron-mst"
    
    Reactotron.use(mst())
  8. Connect Reactotron to Redux createStore

    master

    For standard Redux createStore usage, pass Reactotron.createEnhancer() as an argument.

    Note: Passing the enhancer as the last argument requires redux@>=3.1.0.

    import { createStore } from 'redux'
    import Reactotron from './ReactotronConfig'
    
    // Basic usage
    const store = createStore(rootReducer, Reactotron.createEnhancer())
    
    // With preloaded state
    const store = createStore(rootReducer, preloadedState, Reactotron.createEnhancer())
    
    // If using middleware with compose
    const store = createStore(rootReducer, compose(middleware, Reactotron.createEnhancer()))