This warning appears when using componentWillReceiveProps in React class components. React deprecated this lifecycle method in version 16.3 and removed it in version 17 due to misuse patterns that caused bugs and performance issues.
The componentWillReceiveProps lifecycle method was deprecated because it frequently led to incorrect implementations that caused bugs, infinite loops, and performance problems. Developers commonly misused it by unconditionally calling setState every time props changed, which could trigger cascading updates and re-renders. React replaced this method with safer alternatives: getDerivedStateFromProps for deriving state from props, and componentDidUpdate for handling side effects after updates. The warning indicates your code is using the old lifecycle method that will not work in modern React versions. If you see UNSAFE_componentWillReceiveProps instead, React's codemod tool renamed it to indicate the method is unsafe for concurrent rendering and should be migrated to the new alternatives.
First, examine what logic your componentWillReceiveProps method contains:
// Old deprecated pattern
componentWillReceiveProps(nextProps) {
if (nextProps.userId !== this.props.userId) {
this.setState({ loading: true });
this.fetchUser(nextProps.userId);
}
}Categorize the logic:
- State derivation: Setting state based on prop values
- Side effects: Fetching data, subscriptions, DOM operations
- Memoization: Caching computed values
If your code performs side effects (data fetching, subscriptions, etc.), use componentDidUpdate instead:
// New recommended pattern
componentDidUpdate(prevProps) {
// Compare previous props to current props
if (prevProps.userId !== this.props.userId) {
this.setState({ loading: true });
this.fetchUser(this.props.userId);
}
}Key difference: componentDidUpdate receives *previous* props as an argument, while componentWillReceiveProps received *next* props. This makes the comparison logic clearer and prevents issues with async updates.
If you only need to update state based on prop changes, use the static getDerivedStateFromProps:
// For simple state updates based on props
static getDerivedStateFromProps(nextProps, prevState) {
// Return new state object or null
if (nextProps.value !== prevState.previousValue) {
return {
value: nextProps.value,
previousValue: nextProps.value
};
}
return null;
}Important: This is a static method, so it doesn't have access to this. It should be pure and only return state updates. Don't perform side effects here.
Often, derived state is unnecessary and can be eliminated:
// Instead of deriving state from props
class UserProfile extends React.Component {
state = { fullName: '' };
static getDerivedStateFromProps(props) {
return { fullName: `${props.firstName} ${props.lastName}` };
}
}
// Just compute during render
class UserProfile extends React.Component {
render() {
const fullName = `${this.props.firstName} ${this.props.lastName}`;
return <div>{fullName}</div>;
}
}Computing values during render is often simpler and less error-prone.
React provides an automated codemod to help with migration:
cd your-project
npx react-codemod rename-unsafe-lifecyclesThis will rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps, making it clear which methods need manual migration. The UNSAFE_ prefix indicates the method is unsafe for concurrent rendering.
After running the codemod, manually migrate each UNSAFE_ method to the appropriate alternative.
Modern React prefers functional components with hooks:
// Old class component
class UserProfile extends React.Component {
componentWillReceiveProps(nextProps) {
if (nextProps.userId !== this.props.userId) {
this.fetchUser(nextProps.userId);
}
}
}
// New functional component with useEffect
function UserProfile({ userId }) {
useEffect(() => {
fetchUser(userId);
}, [userId]); // Runs when userId changes
}The useEffect hook with dependencies replaces most componentWillReceiveProps use cases more elegantly.
Why componentWillReceiveProps was problematic:
The method was called before every render when props changed, making it tempting to:
1. Unconditionally call setState (causing infinite loops)
2. Perform multiple side effects (triggering duplicate API calls)
3. Mix state derivation with side effects (hard to debug)
Concurrent rendering concerns:
React's future concurrent rendering features make it possible for componentWillReceiveProps to be called multiple times for a single update, or for updates to be aborted. This makes side effects in "will" lifecycle methods unreliable. The "did" methods (componentDidUpdate) are only called once after the DOM update is committed.
Migration strategy for complex cases:
If componentWillReceiveProps contains complex logic mixing state updates and side effects:
1. Split state derivation into getDerivedStateFromProps
2. Move side effects to componentDidUpdate
3. Use instance variables to track previous prop values if needed
4. Consider whether the state can be computed directly from props during render
Third-party libraries:
If warnings come from dependencies, check for updates or consider alternative libraries. Many popular libraries have released updates removing deprecated lifecycle methods.
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