See the order of hook-function calls in an interactive log, and inspect a function-component's props, state & refs inside its rendering.

Overview

React-hook-tracer Npm version Build status

The react-hook-tracer package traces function components to reveal the order of hook-function calls and lifecycle events in an interactive trace-log component. It also provides a live view of a component's props, state, and refs directly inside its renderering. The functionality is similar to what react-lifecycle-visualizer does for class components.

The demo below shows a traced UserList component that uses an effect to load two User components, which each have local state to keep track of button clicks. Newly added users get an index that is kept in the newUserId ref. The purple panels in the components and the trace log on the right-hand side are created by the package.

User-list demo screen capture

To trace a function component, simply import the hooks from 'react-hook-tracer' instead of 'react', and call useTracer() at the start of the function. The useTracer hook returns a TracePanel component that can be included in the rendering to show the component's hooks, as well as the current values for its state, props, and refs. A global TraceLog component will show the trace messages, and when hovered over will highlight the traced hook in the corresponding TracePanel. The package currently supports tracing for useCallback, useContext, useEffect, useInsertionEffect, useLayoutEffect, useMemo, useReducer, useRef, and useState.

Note that even though tracing is disabled on production builds, it is not advisable to use react-hook-tracer on production.

Demo

The demo above is live on a CodeSandbox playground, and can be run locally with:

> git clone [email protected]:Oblosys/react-hook-tracer
> cd react-hook-tracer
> yarn install
> yarn build-lib
> yarn start

Setup

Follow these steps to add tracing to a project.

Installation

Install the package with npm (or yarn):

> npm install react-hook-tracer

Include TraceLog component

The optional TraceLog component can be included anywhere in the application, but it probably makes the most sense to keep it near the root.

import { TraceLog } from 'react-hook-tracer'
..
export const App = (): JSX.Element => (
  <div className="app">
    <RootComponent />
    <TraceLog />
  </div>
)

If the TraceLog is omitted, traces will get logged to the console instead (see Tracing to the browser console).

Tracing a component

To illustrate how to trace a component, consider this simple Counter component:

import { useState } from 'react'

const Counter = ({ title }: { title: string }) => {
  const [n, setN] = useState(0)
  return (
    <div className="counter">
      <b>{title}</b>
      <span>
        Value of n: {n}
        <input type="button" value="Inc n" onClick={() => setN((prev) => prev + 1)} />
      </span>
    </div>
  )
}

Rendering the component with <Counter title="Trace test" /> yields:

Counter component

To trace this component, import any hook functions (here only useState) from 'react-hook-tracer', together with the useTracer hook, and insert const { TracePanel } = useTracer() at the start of the component function. Traced hooks accept an optional argument to add a custom label, so we will also pass { label: 'n' } to useState. The TracePanel component returned by useTracer is included in the rendering:

import { useState, useTracer } from 'react-hook-tracer' // Update import

const Counter = ({ title }: { title: string }) => {
  const { TracePanel } = useTracer() // Call useTracer at the start
  const [n, setN] = useState(0, { label: 'n' }) // Add custom label (optional)
  return (
    <div className="counter">
      <b>{title}</b>
      <span>
        Value of n: {n}
        <input type="button" value="Inc n" onClick={() => setN((prev) => prev + 1)} />
      </span>
      <TracePanel /> {/* Include TracePanel in rendering */}
    </div>
  )
}

Now the rendering of <Counter title="Trace test" /> together with the trace log will look like this:

Traced Counter component

To experiment with this example, open the CodeSandbox playground at /src/demos/Counter.tsx and select 'Counter' instead of 'Demo' in the running app.

Note that traces are generated only by hooks imported from 'react-hook-tracer', and only for the calls that follow a useTracer call. Regular React hook calls following useTracer call do not generate traces, and neither do traced-hook calls in components without a useTracer call.

Besides TracePanel, useTracer also returns a function trace: (message: string) => void, which can be used to log custom trace messages.

Alternative import

Instead of using a named import, 'react-hook-tracer' can also be imported as a variable, e.g. traced. Hooks can then be traced by prefixing each one with traced.:

import { useTracer } from 'react-hook-tracer'
import * as traced from 'react-hook-tracer'

const Counter = ({ title }: { title: string }) => {
  const { TracePanel } = useTracer()
  const [n, setN] = traced.useState(0, { label: 'n' })
  return (
    ..
  )
}

React strict mode

You may want to temporarily disable React strict mode by removing the <React.StrictMode> tags (typically in the root index.tsx or index.jsx file). In development builds, strict mode executes each component render twice, and also mounts components twice, which makes the log harder to read.

Tracing to the browser console

To enable tracing to the browser console, leave out the TraceLog component, or call setTracerConfig anywhere in your project:

setTracerConfig({ shouldTraceToConsole: true })

Instead of a string representation, console traces show the actual object values for props, state, and refs, which means they can be expanded to inspect properties:

Console traces

Console traces may also be useful to diagnose infinite render loops, since the trace log will not update in that case as it is itself a React component. To see what the console traces look like, check out the CodeSandbox demo, which has a checkbox to control console tracing.

The useTracer hook

The useTracer hook should be called at the start of the traced component, and returns a record containing the TracePanel component and a trace function:

useTracer: (options?: { showProps?: ShowProps }) => { trace: (message: string) => void, TracePanel: () => JSX.Element }

The TracePanel component can be included in the rendering, and trace can be used to emit custom traces to the trace log.

To override how prop values are displayed in the trace log, useTracer takes an optional showProps: ShowProps argument:

type ShowProps<Props = Record<string, any>> = {
  [K in keyof Props]?: (propValue: Props[K]) => string
}

This can be useful for object prop values, which are stringified by default. For example, if we have a User component that takes these props:

interface UserProps {
  user: { name: string; color: string }
  count: number
}

The trace log will contain entries like render props: user={"name":"Stimpy","color":"red"} count=1, which could be made more concise by declaring an override for prop user:

const showProps: ShowProps<UserProps> = { user: ({ name, color }) => `<<${name}:${color}>>` }

and in the User component call useTracer({ showProps }).

Now the log will contain entries like this: render props: user=<<Stimpy:red>> count=1.

List of traced hooks

All traced hooks accept an optional configuration argument, which can be used to specify a custom label that will appear in the trace log and panels. For hooks that keep track of a value, a custom show function can be specified as well.

Hook Shorthand Optional configuration argument
useContext 'context' {label?: string, show?: (contextValue: T) => string}
useMemo 'memo' {label?: string, show?: (memoizedValue: T) => string}
useReducer 'reducer' See interface UseReducerTraceOptions below.
useRef 'ref' {label?: string, show?: (refValue: T) => string}
useState 'state' {label?: string, show?: (state: S) => string}
useCallback 'callback' {label?: string}
useEffect 'effect' {label?: string}
useInsertionEffect 'insertion' {label?: string}
useLayoutEffect 'layout' {label?: string}
interface UseReducerTraceOptions<S, A> {
  label?: string
  showState?: (state: S) => string
  showAction?: (state: A) => string
}

Trace-log message overview

Hooks have different phases in which traces are emitted. The overview below shows all possible phases for each hook.

Hooks with values

Hook Phase Appearance in trace log
useContext init On the first render, at the useContext call.
update Whenever the context value changes.
useMemo init On the first render, at the useMemo call.
refresh Whenever the memoized value is recomputed due to changes in the dependencies.
useReducer init On the first render, at the useReducer call.
action At the start of a reduction step, shows the dispatched action.
state Immediately after a reduction step, shows the updated state.
useRef init On the first render, at the useRef call.
set Whenever the ref value changes (even if no component re-renders).
useState init On the first render, at the useState call.
set When setting the state to a value.
update When setting the state with an update function (i.e setState(prevState => ..)).

Hooks without values

Hook Phase Appearance in trace log
useCallback init On the first render, at the useCallback call.
run When the callback gets called
refresh When a new callback is created due to changes in the dependencies.
useEffect init On the first render, at the useEffect call.
run Before the effect runs.
cleanup Before the effect's cleanup function gets called.
useInsertionEffect init On the first render, at the useInsertionEffect call.
run Before the effect runs.
useLayoutEffect init On the first render, at the useLayoutEffect call.
run Before the effect runs.

Lifecycle events & trace

Even though function components don't have a traditional lifecycle like class components, traced components will emit traces on certain lifecycle events.

Event Appearance in trace log
mounting Just before the component begins to mount.
mounted When the component has mounted.
render At the start of each render, also shows the current props.
trace When the custom trace function gets called.
unmount Just before the component unmounts.

Upcoming features

  • For hooks with dependencies, show which dependencies changed.
  • A configuration component to replace setTracerConfig.
  • JSDoc comments for exported hooks.
  • Maybe: Show render count in log and panel.
You might also like...
A React component that decorates its children with mouse and touch coordinates relative to itself.

react-cursor-position react-cursor-position is a primitive component for composing UI features that require notification of cursor or touch position c

A new way of Function Components without Hooks
A new way of Function Components without Hooks

React Split Components (RiC) A new way of Function Components without Hooks

📄 React hook for managing forms and inputs state
📄 React hook for managing forms and inputs state

react-use-form-state 📖 Table of Contents Motivation Getting Started Examples Basic Usage Initial State Global Handlers Advanced Input Options Custom

React Hook for accessing state and dispatch from a Redux store

redux-react-hook React hook for accessing mapped state and dispatch from a Redux store. Table of Contents Install Quick Start Usage StoreContext useMa

BitAboutState - Tiny and powerful React hook-based state management library
BitAboutState - Tiny and powerful React hook-based state management library

🚀 Tiny and powerful React hook-based state management library. 100% Idiomatic React.

React Hook for state management with profunctor lenses

Profunctor State Hook React Hook for state management with Profunctor Optics A simple and small (2KB!) approach to state management in React using fun

useMedia React hook to track CSS media query state

use-media useMedia React sensor hook that tracks state of a CSS media query. Install You can install use-media with npm npm install --save use-media o

React Hook for managing state in URL query parameters with easy serialization.

useQueryParams A React Hook, HOC, and Render Props solution for managing state in URL query parameters with easy serialization. Works with React Route

🍙 Lightweight (600B minified + gzipped) React Hook to subscribe to a subset of your single app state.

🍙 useSubstate Lightweight (600B minified + gzipped) React Hook to subscribe to a subset of your single app state. function Component() { const [su

Comments
  • Documentation and Demo request

    Documentation and Demo request

    Thanks for the good work with the react-hook-tracer project, i really applaud you for that. I believe your projects specifically this one needs more traction, purposefully because of its ability to help programmers and developers understand how different components work in the hood. That's why i am requesting for a demo probably a worked out video for many people out there who would love to use this package.

    opened by theefreelancer 3
Releases(v1.3.1)
  • v1.3.1(Nov 17, 2022)

    Changes

    • Handle anonymous components and components with a displayName property.
    • Restore React DevTools inspection of traced components.
    • Update TypeScript to 4.9.3 & other dependencies to latest.
    Source code(tar.gz)
    Source code(zip)
  • v1.3.0(Nov 12, 2022)

    Changes

    • For useReducer, emit a 'dispatch' trace when dispatch gets called, rather than an 'action' trace when reduction starts.
    • useTracer now returns a stable TracePanel that won't remount on each render.
    • Throw an error when useTracer is not the first hook function called in a traced component.
    • Fix warning and possible crash when a traced ref is mutated during a render.
    • Custom show functions do not need to be stable anymore.
    • Clean up demo styles.
    Source code(tar.gz)
    Source code(zip)
  • v1.2.0(Nov 6, 2022)

    Changes

    • Automatically trace to console when TraceLog is omitted.
    • Warn when React.StrictMode is active. Muting the extra strict-mode traces is not feasible, unfortunately. (Note that on CodeSandbox playgrounds, the warning only appears when the app is running in its own window.)
    • Fix & simplify trace-log & trace-panel styling.
    Source code(tar.gz)
    Source code(zip)
  • v1.1.0(Nov 5, 2022)

    Changes

    • Add trace support for useReducer.
    • Restrict fixed-width font to values + Tweak styles.
    • Remove obsolete shift-up/down keyboard handler.
    • Rename Sample demo to Counter.
    • Transfer remaining images to https://github.com/Oblosys/asset-storage.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.1(Nov 4, 2022)

React Hook used to make API calls

query-hook React hook used to make API calls Install npm install --save query-hook Usage JavaScript import { useQuery, Status } from 'query-hook'; co

Jérémy Surieux 4 Dec 9, 2021
Hook, simplifying dealing with Promises inside of React components

promise-hook Installation Install it with yarn: yarn add promise-hook Or with npm: npm i promise-hook --save Demo The simplest way to start playing

null 55 Dec 3, 2022
React hook to dynamically load an external script and know when its loaded

react-script-hook React hook to dynamically load an external script and know when its loaded Install // with npm npm install react-script-hook // wit

Frank Hübner 116 Jan 2, 2023
Why you should use react-query instead of useEffects for API Calls?

Why you should use React Query instead of useEffect for API Calls? This repo, gives great examples of why you should use react-query library for fetch

Islem Maboud 26 Dec 15, 2022
React Class State Hook - Automatically generate CSS class names and modifiers based on your component's state

React Class State Hook Automatically generate CSS class names and modifiers based on your component's state.

Two-Cat Moon 1 Apr 14, 2022
🔥 React hooks for Socket.io, SEE, WebSockets and more to come

React hooks for handling server-push technologies: use-socketio for Socket.io use-server-sent-events for Server Sent Events use-websockets for Websock

Marvin Frachet 195 Dec 28, 2022
React hook to execute and watch async function

react-async-watcher React hook to execute and watch async function Introduction This package is made for handle asynchronous tasks, such as backend ap

Ralph 8 Apr 15, 2021
React hook for combining double-click function into click event, as well as repeatable double-click

useDoubleClick React hook for continuous double-clicks and combining click and double-click events See Repeatable double-click and hybrid clicks solut

Zattoo 17 Sep 29, 2022
React hook which gives a smooth scrolling function.

use-smooth-scroll React hook which gives a smooth scrolling function. Example (Codesandbox) const Example = () => { const ref = React.useRef() con

Mateusz Burzyński 41 Aug 23, 2022
😎 ♻️ A tiny React hook for rendering large datasets like a breeze.

?? ♻️ A tiny React hook for rendering large datasets like a breeze.

Welly 1.1k Jan 5, 2023