@moraisdeoliveirawesley/react-performance-guard
v0.0.3
Published
React performance library that automatically detects unnecessary re-renders, mutable props, heavy components, and more
Downloads
409
Maintainers
Readme
react-performance-guard
React performance library that automatically detects common issues and points to where they occur (file:line via stack trace). Use it in development to fix unnecessary re-renders, mutable props, heavy components, render storms, inline objects, large lists, setState after unmount, and more.
Features
| Detector | What it catches | Where it points |
|----------|------------------|------------------|
| Unnecessary re-render | Same props (shallow) but component re-rendered | Component that re-rendered (use React.memo or move state down) |
| Mutable props | Props object mutated after being passed | Component that received mutated props |
| Heavy component | Commit phase > threshold (e.g. 16ms) | Component (or tree) under <PerfGuardBoundary> |
| Render storm | Too many re-renders in a short window | Component that stormed |
| Possible setState in render | Synchronous re-renders (e.g. 5 in <5ms) | Component that likely calls setState during render |
| Inline objects/functions | New object/function references in props every render | Component receiving inline props (parent should use useMemo/useCallback) |
| Large list | Rendering many items without virtualization | Where useLargeListCheck(length) was called |
| setState after unmount | setState called after component unmounted | Component that used useSafeState and triggered after unmount |
Every reported issue includes optional stackTrace and location (parsed file, line, column) so you can jump straight to the problematic code.
Install
npm install react-perf-guard
# or
yarn add react-perf-guard
pnpm add react-perf-guardPeer dependency: React 16.8+ (hooks).
Quick start
1. Optional: global config (e.g. in app root)
import { configurePerfGuard } from 'react-perf-guard';
configurePerfGuard({
onIssue: (issue) => {
// issue.location => { file, line, column } — where the problem was detected
console.warn('[Perf]', issue.category, issue.message, issue.location);
},
});2. Per-component: hook (recommended)
import { usePerfGuard } from 'react-perf-guard';
function MyCard({ title, count }: { title: string; count: number }) {
usePerfGuard({ title, count }, { componentName: 'MyCard' });
return <div>{title}: {count}</div>;
}This enables: re-render, mutable props, render storm, and inline props detection. In the console you’ll see the location (file:line) when an issue is found.
3. Heavy components: <PerfGuardBoundary>
import { PerfGuardBoundary } from 'react-perf-guard';
<PerfGuardBoundary id="ProductList" heavyThresholdMs={16}>
<ProductList />
</PerfGuardBoundary>4. Large lists: useLargeListCheck
import { useLargeListCheck } from 'react-perf-guard';
function ProductList({ items }: { items: Product[] }) {
useLargeListCheck(items.length, { componentName: 'ProductList', threshold: 50 });
return (
<ul>
{items.map((p) => <li key={p.id}>{p.name}</li>)}
</ul>
);
}5. Avoid setState after unmount: useSafeState
import { useSafeState } from 'react-perf-guard';
function Search() {
const [query, setQuery] = useSafeState('', { componentName: 'Search' });
useEffect(() => {
const id = setTimeout(() => fetchResults(query).then(setResults), 300);
return () => clearTimeout(id);
}, [query]);
// If fetchResults resolves after unmount, setState would run — useSafeState warns and no-ops
return <input value={query} onChange={(e) => setQuery(e.target.value)} />;
}6. HOC: withPerfGuard
import { withPerfGuard } from 'react-perf-guard';
const MyCard = withPerfGuard(
function MyCard({ title }: { title: string }) {
return <div>{title}</div>;
},
{ componentName: 'MyCard' }
);Where is the problem?
Each PerfIssue can include:
location—{ file?, line?, column?, functionName?, raw? }— parsed from the stack (first frame that isn’t react-perf-guard or React).stackTrace— fullError.stackstring.
So in onIssue you can do:
onIssue: (issue) => {
if (issue.location?.file) {
console.warn(`${issue.category} at ${issue.location.file}:${issue.location.line}:${issue.location.column}`);
}
console.warn(issue.message, issue.details);
}Stack capture is on by default. To disable (e.g. in production): setCaptureStack(false).
API
Hooks
usePerfGuard(props, options?)— Re-render, mutable props, render storm, inline props. Pass props + optionalcomponentName,onIssue,disabled, or per-detector options.useReRenderDetection(props, options?)— Only re-render (same props, shallow).useMutablePropsDetection(props, options?)— Only mutable props.useRenderStormDetection(options?)— Only render storm (and possible setState-in-render when re-renders are synchronous).useInlinePropsDetection(props, options?)— Only new object/function references in props.useLargeListCheck(itemCount, options?)— Warns whenitemCount≥ threshold (default 50). Use in components that render long lists.useSafeState(initialState, options?)— LikeuseState; warns and no-ops if setState is called after unmount.useHeavyComponentReport(options?)— ReturnsonRenderfor<Profiler>. Used byPerfGuardBoundary.
Components
<PerfGuardBoundary id="..." heavyThresholdMs={16} onIssue={...}>— Wraps children inProfiler, reports when commit duration exceeds threshold.withPerfGuard(Component, options?)— HOC that injectsusePerfGuard.
Config / utils
configurePerfGuard(config)— GlobalonIssue, thresholds, etc.setOnIssue(cb)— Set or clear global issue callback.setCaptureStack(enabled)— Turn stack/location capture on or off (default on).report(issue)— Manually report aPerfIssue.createAndReport(issue)— Likereportbut enriches with stack/location (used by detectors).captureStackTrace()/parseStackLocation(stack)/captureLocation()/enrichWithLocation(issue)— Low-level stack/location helpers.
Types
PerfIssue—{ category, severity, message, componentName?, details?, timestamp, stackTrace?, location? }SourceLocation—{ file?, line?, column?, functionName?, raw? }IssueCategory—'unnecessary-re-render' | 'mutable-props' | 'heavy-component' | 'render-storm' | 'inline-objects' | 'large-list' | 'setState-after-unmount' | 'possible-setState-in-render'OnIssueCallback—(issue: PerfIssue) => void
Collecting issues for O1 / evidence
const issues: PerfIssue[] = [];
configurePerfGuard({
onIssue: (issue) => {
issues.push(issue);
if (process.env.NODE_ENV === 'development') {
console.warn('[react-perf-guard]', issue.category, issue.message, issue.location);
}
},
});
// Later: export or send `issues` (including location) for documentation/evidenceLicense
MIT.
