This error occurs when trying to wrap a non-component value with React.memo, such as class components, string literals, or other invalid types. React.memo is specifically designed for function components and expects a valid React component as its argument to enable memoization.
React.memo is a higher-order component (HOC) that memoizes a component to prevent unnecessary re-renders when props haven't changed. However, it only works with valid React function components and forwardRef-wrapped components. When you pass something that isn't a valid component—such as a string, object, class component, or any other value—React cannot determine what to memoize and throws this error. The error represents a type mismatch between what React.memo expects (a function component or forwardRef) and what you're actually providing. React.memo needs a callable function that returns JSX to wrap with its memoization logic. Class components should use React.PureComponent or shouldComponentUpdate instead, as they have different lifecycle mechanisms that don't work with the memoization pattern React.memo implements. Understanding this error is important because React.memo is a performance optimization tool, and knowing its constraints helps you apply memoization correctly throughout your codebase.
Ensure that what you're passing to React.memo is a valid function component. The component must be a function that returns JSX.
Bad - Wrapping a non-component value:
const MyValue = { name: 'test' };
const Memoized = React.memo(MyValue); // ❌ Error!Good - Wrapping a function component:
function MyComponent(props) {
return <div>{props.name}</div>;
}
const Memoized = React.memo(MyComponent); // ✓ CorrectAlso good - Arrow function component:
const MyComponent = (props) => {
return <div>{props.name}</div>;
};
const Memoized = React.memo(MyComponent); // ✓ CorrectReact.memo only works with function components. If you're trying to memoize a class component, use React.PureComponent or shouldComponentUpdate instead.
Bad - Trying to memo a class component:
class MyComponent extends React.Component {
render() {
return <div>{this.props.name}</div>;
}
}
const Memoized = React.memo(MyComponent); // ❌ Error!Good - Class component with PureComponent:
class MyComponent extends React.PureComponent {
render() {
return <div>{this.props.name}</div>;
}
}Good - Convert to function component with memo:
function MyComponent(props) {
return <div>{props.name}</div>;
}
const Memoized = React.memo(MyComponent); // ✓ CorrectGood - Class with custom shouldComponentUpdate:
class MyComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return !Object.is(this.props, nextProps) || !Object.is(this.state, nextState);
}
render() {
return <div>{this.props.name}</div>;
}
}Verify that you're exporting the component function itself, not a configuration object or other value.
Bad - Exporting wrong value:
const config = { component: MyComponent };
export const Memoized = React.memo(config); // ❌ Error!Good - Export the component:
function MyComponent(props) {
return <div>{props.name}</div>;
}
export const Memoized = React.memo(MyComponent); // ✓ CorrectGood - Default export:
function MyComponent(props) {
return <div>{props.name}</div>;
}
export default React.memo(MyComponent); // ✓ CorrectGood - Named export:
const MyComponent = (props) => <div>{props.name}</div>;
export const MemoizedComponent = React.memo(MyComponent); // ✓ CorrectIf you need to memoize a component that receives a ref prop, wrap it with forwardRef first, then memo.
Bad - Trying to memo component with ref:
const MyComponent = (props, ref) => {
return <input ref={ref} {...props} />;
};
const Memoized = React.memo(MyComponent); // ❌ Refs not forwardedGood - forwardRef first, then memo:
const MyComponent = React.forwardRef((props, ref) => {
return <input ref={ref} {...props} />;
});
const Memoized = React.memo(MyComponent); // ✓ CorrectGood - Single line:
const Memoized = React.memo(
React.forwardRef((props, ref) => (
<input ref={ref} {...props} />
))
);Ensure you're importing React correctly and using React.memo, not a misspelled or incorrectly imported version.
Bad - Incorrect import:
import { memo } from 'react';
const MyComponent = () => <div>Hello</div>;
const Memoized = memo(myConfig); // If myConfig isn't a componentGood - Using React.memo:
import React from 'react';
const MyComponent = () => <div>Hello</div>;
const Memoized = React.memo(MyComponent); // ✓ CorrectGood - Importing memo directly:
import { memo } from 'react';
const MyComponent = () => <div>Hello</div>;
const Memoized = memo(MyComponent); // ✓ CorrectReact.memo's second argument is an optional custom comparison function. If you provide it, it must be a function that receives two props objects and returns a boolean.
Bad - Invalid comparison argument:
const MyComponent = (props) => <div>{props.name}</div>;
const Memoized = React.memo(MyComponent, { custom: true }); // ❌ Must be a functionGood - Custom comparison function:
const MyComponent = (props) => <div>{props.name}</div>;
const Memoized = React.memo(MyComponent, (prevProps, nextProps) => {
return prevProps.id === nextProps.id; // Return true if props are equal
}); // ✓ CorrectGood - No custom comparison (default shallow comparison):
const MyComponent = (props) => <div>{props.name}</div>;
const Memoized = React.memo(MyComponent); // ✓ CorrectReact.memo only memoizes the component itself, not what's inside it. Don't try to memo children props or context values.
Bad - Trying to memo children:
const MyComponent = (props) => {
const MemoChild = React.memo(props.children); // ❌ Wrong!
return <div>{MemoChild}</div>;
};Good - Memoize the component that renders children:
const MyComponent = React.memo((props) => {
return <div>{props.children}</div>;
}); // ✓ Correct - Component is memoizedGood - Extract children as separate memoized component:
const ChildRenderer = React.memo(({ children }) => (
<div>{children}</div>
));
const MyComponent = (props) => {
return <ChildRenderer>{props.children}</ChildRenderer>;
}; // ✓ CorrectReact.memo implements shallow comparison of props by default. This means if you pass new object or function references, the component will re-render even with the same logical values. Use useCallback for function props and useMemo for object props to maintain reference equality.
TypeScript Considerations: When using TypeScript with React.memo, the generic type parameters might not be preserved correctly. If you need strict typing, use an explicit type annotation:
interface MyComponentProps {
name: string;
age: number;
}
const MyComponent: React.FC<MyComponentProps> = (props) => {
return <div>{props.name}</div>;
};
const Memoized = React.memo(MyComponent);Performance Perspective: Not all components benefit from memoization. Use React.memo when:
- The component is pure (same props produce same output)
- The component re-renders frequently with the same props
- The component has expensive render logic
If a component rarely re-renders or always receives new props, memoization adds unnecessary overhead.
Composition Pattern: Instead of memoizing everything, consider moving state management to a parent component and keeping child components simple. Often, better component structure is more effective than memoization.
Testing Note: When testing memoized components, ensure your test setup correctly handles React.memo. Some testing libraries may not automatically unwrap memoized components, affecting test behavior.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite