diff --git a/packages/react-router/test/client/react-exports.test.ts b/packages/react-router/test/client/react-exports.test.ts
index 02e5b970a21e..cae5ac56208a 100644
--- a/packages/react-router/test/client/react-exports.test.ts
+++ b/packages/react-router/test/client/react-exports.test.ts
@@ -76,7 +76,7 @@ describe('Re-exports from React SDK', () => {
});
expect(WrappedComponent).toBeDefined();
- expect(typeof WrappedComponent).toBe('function');
+ expect(typeof WrappedComponent).toBe('object');
expect(WrappedComponent.displayName).toBe('errorBoundary(TestComponent)');
const { getByText } = render(React.createElement(WrappedComponent));
diff --git a/packages/react/src/errorboundary.tsx b/packages/react/src/errorboundary.tsx
index e3f94b441ee6..f37afe961042 100644
--- a/packages/react/src/errorboundary.tsx
+++ b/packages/react/src/errorboundary.tsx
@@ -222,11 +222,11 @@ function withErrorBoundary
>(
): React.FC
{
const componentDisplayName = WrappedComponent.displayName || WrappedComponent.name || UNKNOWN_COMPONENT;
- const Wrapped: React.FC
= (props: P) => (
+ const Wrapped = React.memo((props: P) => (
- );
+ )) as unknown as React.FC
;
Wrapped.displayName = `errorBoundary(${componentDisplayName})`;
diff --git a/packages/react/test/errorboundary.test.tsx b/packages/react/test/errorboundary.test.tsx
index 275faa4e2079..5e731cc86b49 100644
--- a/packages/react/test/errorboundary.test.tsx
+++ b/packages/react/test/errorboundary.test.tsx
@@ -96,6 +96,122 @@ describe('withErrorBoundary', () => {
const Component = withErrorBoundary(() =>
Hello World
, { fallback: fallback
});
expect(Component.displayName).toBe(`errorBoundary(${UNKNOWN_COMPONENT})`);
});
+
+ it('does not rerender when props are identical', () => {
+ let renderCount = 0;
+ const TestComponent = ({ title }: { title: string }) => {
+ renderCount++;
+ return {title}
;
+ };
+
+ const WrappedComponent = withErrorBoundary(TestComponent, { fallback: fallback
});
+ const { rerender } = render();
+
+ expect(renderCount).toBe(1);
+
+ // Rerender with identical props - should not cause TestComponent to rerender
+ rerender();
+ expect(renderCount).toBe(1);
+
+ // Rerender with different props - should cause TestComponent to rerender
+ rerender();
+ expect(renderCount).toBe(2);
+ });
+
+ it('does not rerender when complex props are identical', () => {
+ let renderCount = 0;
+ const TestComponent = ({ data }: { data: { id: number; name: string } }) => {
+ renderCount++;
+ return {data.name}
;
+ };
+
+ const WrappedComponent = withErrorBoundary(TestComponent, { fallback: fallback
});
+ const props = { data: { id: 1, name: 'test' } };
+ const { rerender } = render();
+
+ expect(renderCount).toBe(1);
+
+ // Rerender with same object reference - should not cause TestComponent to rerender
+ rerender();
+ expect(renderCount).toBe(1);
+
+ // Rerender with different object but same values - should cause rerender
+ rerender();
+ expect(renderCount).toBe(2);
+
+ // Rerender with different values - should cause rerender
+ rerender();
+ expect(renderCount).toBe(3);
+ });
+
+ it('does not rerender when errorBoundaryOptions are the same', () => {
+ let renderCount = 0;
+ const TestComponent = ({ title }: { title: string }) => {
+ renderCount++;
+ return {title}
;
+ };
+
+ const errorBoundaryOptions = { fallback: fallback
};
+ const WrappedComponent = withErrorBoundary(TestComponent, errorBoundaryOptions);
+ const { rerender } = render();
+
+ expect(renderCount).toBe(1);
+
+ // Rerender with identical props - should not cause TestComponent to rerender
+ rerender();
+ expect(renderCount).toBe(1);
+ });
+
+ it('preserves function component behavior with React.memo', () => {
+ const TestComponent = ({ title }: { title: string }) => {title}
;
+ const WrappedComponent = withErrorBoundary(TestComponent, { fallback: fallback
});
+
+ expect(WrappedComponent).toBeDefined();
+ expect(typeof WrappedComponent).toBe('object');
+ expect(WrappedComponent.displayName).toBe('errorBoundary(TestComponent)');
+
+ const { container } = render();
+ expect(container.innerHTML).toContain('test');
+ });
+
+ it('does not rerender parent component unnecessarily', () => {
+ let parentRenderCount = 0;
+ let childRenderCount = 0;
+
+ const ChildComponent = ({ value }: { value: number }) => {
+ childRenderCount++;
+ return Child: {value}
;
+ };
+
+ const WrappedChild = withErrorBoundary(ChildComponent, { fallback: Error
});
+
+ const ParentComponent = ({ childValue, otherProp }: { childValue: number; otherProp: string }) => {
+ parentRenderCount++;
+ return (
+
+
Parent: {otherProp}
+
+
+ );
+ };
+
+ const { rerender } = render();
+
+ expect(parentRenderCount).toBe(1);
+ expect(childRenderCount).toBe(1);
+
+ // Change otherProp but keep childValue the same
+ rerender();
+
+ expect(parentRenderCount).toBe(2); // Parent should rerender
+ expect(childRenderCount).toBe(1); // Child should NOT rerender due to memo
+
+ // Change childValue
+ rerender();
+
+ expect(parentRenderCount).toBe(3); // Parent should rerender
+ expect(childRenderCount).toBe(2); // Child should rerender due to changed props
+ });
});
describe('ErrorBoundary', () => {