Skip to content

Commit c6cb5ab

Browse files
cursoragentchargome
andcommitted
feat: Optimize ErrorBoundary with React.memo
Co-authored-by: charly.gomez <[email protected]>
1 parent b9849a2 commit c6cb5ab

File tree

2 files changed

+71
-2
lines changed

2 files changed

+71
-2
lines changed

packages/react/src/errorboundary.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,11 @@ function withErrorBoundary<P extends Record<string, any>>(
222222
): React.FC<P> {
223223
const componentDisplayName = WrappedComponent.displayName || WrappedComponent.name || UNKNOWN_COMPONENT;
224224

225-
const Wrapped: React.FC<P> = (props: P) => (
225+
const Wrapped: React.FC<P> = React.memo((props: P) => (
226226
<ErrorBoundary {...errorBoundaryOptions}>
227227
<WrappedComponent {...props} />
228228
</ErrorBoundary>
229-
);
229+
));
230230

231231
Wrapped.displayName = `errorBoundary(${componentDisplayName})`;
232232

packages/react/test/errorboundary.test.tsx

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,75 @@ describe('withErrorBoundary', () => {
9696
const Component = withErrorBoundary(() => <h1>Hello World</h1>, { fallback: <h1>fallback</h1> });
9797
expect(Component.displayName).toBe(`errorBoundary(${UNKNOWN_COMPONENT})`);
9898
});
99+
100+
it('should not rerender when props are unchanged', () => {
101+
let renderCount = 0;
102+
const TestComponent = (props: { value: number }) => {
103+
renderCount++;
104+
return <div data-testid="test-component">Value: {props.value}</div>;
105+
};
106+
107+
const WrappedComponent = withErrorBoundary(TestComponent, { fallback: <h1>fallback</h1> });
108+
109+
// Create a parent component that can trigger rerenders
110+
const ParentComponent = () => {
111+
const [parentState, setParentState] = useState(0);
112+
return (
113+
<div>
114+
<button onClick={() => setParentState(prev => prev + 1)}>Parent Rerender</button>
115+
<div>Parent State: {parentState}</div>
116+
<WrappedComponent value={42} />
117+
</div>
118+
);
119+
};
120+
121+
render(<ParentComponent />);
122+
123+
// Initial render should have rendered the test component
124+
expect(renderCount).toBe(1);
125+
expect(screen.getByTestId('test-component')).toHaveTextContent('Value: 42');
126+
127+
// Trigger parent rerender
128+
fireEvent.click(screen.getByText('Parent Rerender'));
129+
130+
// The wrapped component should not have rerendered since props didn't change
131+
expect(renderCount).toBe(1);
132+
expect(screen.getByTestId('test-component')).toHaveTextContent('Value: 42');
133+
});
134+
135+
it('should rerender when props change', () => {
136+
let renderCount = 0;
137+
const TestComponent = (props: { value: number }) => {
138+
renderCount++;
139+
return <div data-testid="test-component">Value: {props.value}</div>;
140+
};
141+
142+
const WrappedComponent = withErrorBoundary(TestComponent, { fallback: <h1>fallback</h1> });
143+
144+
// Create a parent component that can change the props
145+
const ParentComponent = () => {
146+
const [value, setValue] = useState(42);
147+
return (
148+
<div>
149+
<button onClick={() => setValue(prev => prev + 1)}>Change Value</button>
150+
<WrappedComponent value={value} />
151+
</div>
152+
);
153+
};
154+
155+
render(<ParentComponent />);
156+
157+
// Initial render
158+
expect(renderCount).toBe(1);
159+
expect(screen.getByTestId('test-component')).toHaveTextContent('Value: 42');
160+
161+
// Change the prop value
162+
fireEvent.click(screen.getByText('Change Value'));
163+
164+
// The wrapped component should have rerendered since props changed
165+
expect(renderCount).toBe(2);
166+
expect(screen.getByTestId('test-component')).toHaveTextContent('Value: 43');
167+
});
99168
});
100169

101170
describe('ErrorBoundary', () => {

0 commit comments

Comments
 (0)