π Before claiming this issue
Two quick steps before you open a PR:
- β Star the repository β low-friction signal that you'll follow through
- π¬ Comment
I'll take this (or similar) below β prevents two contributors racing on the same issue
Full policy: CONTRIBUTING.md β How to claim. If a claim is older than 7 days with no PR, you can reclaim it politely.
What
renderValue in src/panel/tabs/ReduxTab.tsx is called recursively for every visible node in the Redux state tree. Two hot-path issues:
Issue 1 β Line 298: JSON.stringify for path equality check
const isEditing = JSON.stringify(editingState.path) === JSON.stringify(pathArray);
This serializes two arrays on every recursive call. With 50+ visible nodes, that's 100+ JSON.stringify calls per render.
Issue 2 β Line 444: search filter inside renderValue
JSON.stringify(obj[key]).toLowerCase().includes(searchQuery.toLowerCase())
This stringifies the full value of every root-level key on every keystroke in the search box.
How to fix
Fix 1: Use a delimiter join instead of JSON.stringify
// BEFORE (line 298)
const isEditing = JSON.stringify(editingState.path) === JSON.stringify(pathArray);
// AFTER β \0 is safe because path segments are object keys or array indices
const isEditing = editingState?.path.join('\0') === pathArray.join('\0');
Or even better, lift the editing path into a precomputed string at the top of the render:
const editingPathKey = editingState?.path.join('\0') ?? null;
// inside renderValue:
const isEditing = editingPathKey === pathArray.join('\0');
Fix 2: Hoist search filter into useMemo
// AT TOP of the component, near the existing useState calls:
const filteredRootKeys = useMemo(() => {
if (!searchQuery) return Object.keys(state as object);
const q = searchQuery.toLowerCase();
return Object.keys(state as object).filter(key =>
key.toLowerCase().includes(q) ||
JSON.stringify(state[key]).toLowerCase().includes(q)
);
}, [state, searchQuery]);
// THEN β use filteredRootKeys instead of recomputing inside renderValue
This way, the filter runs once per keystroke instead of once per visible node.
Verify
- Open a real React app with Redux DevTools.
- Connect React Debugger, expand the state tree.
- Open Chrome DevTools Performance tab, record a 5-second trace while typing in the Redux search.
- Before fix: flamegraph shows
JSON.stringify taking ~5-20ms per keystroke.
- After fix:
JSON.stringify calls drop dramatically; keystroke latency falls.
Acceptance criteria
Scope: S (~15 lines changed) | Effort: ~1h including perf measurement | Risk: Lane Tiny
What
renderValueinsrc/panel/tabs/ReduxTab.tsxis called recursively for every visible node in the Redux state tree. Two hot-path issues:Issue 1 β Line 298:
JSON.stringifyfor path equality checkThis serializes two arrays on every recursive call. With 50+ visible nodes, that's 100+
JSON.stringifycalls per render.Issue 2 β Line 444: search filter inside renderValue
This stringifies the full value of every root-level key on every keystroke in the search box.
How to fix
Fix 1: Use a delimiter join instead of JSON.stringify
Or even better, lift the editing path into a precomputed string at the top of the render:
Fix 2: Hoist search filter into useMemo
This way, the filter runs once per keystroke instead of once per visible node.
Verify
JSON.stringifytaking ~5-20ms per keystroke.JSON.stringifycalls drop dramatically; keystroke latency falls.Acceptance criteria
JSON.stringify(editingState.path)removed fromrenderValueuseMemoScope: S (~15 lines changed) | Effort: ~1h including perf measurement | Risk: Lane Tiny