React is a powerful library, but its unique rules (especially around JSX and Hooks) can trip up even experienced developers. Understanding the common pitfalls is the fastest way to master the framework.
Here is an expanded guide covering 15 of the most frequent errors you'll encounter in a React application, complete with the cause and, most importantly, the exact solution.
1. Invalid DOM Property (class
vs className
)
The Problem
You write standard HTML attribute names like class
or for
, but React throws a warning in the console:
Warning: Invalid DOM property
class
. Did you meanclassName
?
The Cause
React uses JSX, which is a syntax extension for JavaScript. Since class
is a reserved keyword in JavaScript (used for defining classes), React can't use it directly for HTML attributes. The same applies to for
, which must be written as htmlFor
when associated with a <label>
.
The Solution
Always use className
instead of class
for defining CSS classes on DOM elements within JSX. Use htmlFor
instead of for
for label association.
❌ Bad Code | ✅ Good Code |
---|---|
jsx<div class="header">Hello</div> |
jsx<div className="header">Hello</div> |
2. Cannot Read Property of Undefined
The Problem
You try to access a property or method (like map()
) on an object or array, but the application crashes with an error message:
TypeError: Cannot read property 'map' of undefined (or null)
The Cause
This error usually occurs when you are fetching data asynchronously (e.g., from an API) and your component tries to render the data before it has arrived. The state variable holding the data is often initialized to null
or undefined
.
The Solution
Use conditional rendering or optional chaining (?.
) to ensure you only access properties on an object/array once it has been populated.
❌ Bad Code | ✅ Good Code |
---|---|
jsxconst MyList = ({ items }) => { return ( <ul> {items.map(item => <li key={item.id}>{item.name}</li>)} </ul> );}; // If items is undefined, this crashes |
jsxconst MyList = ({ items }) => { // Use optional chaining or check for existence first: return ( <ul> {items?.map(item => <li key={item.id}>{item.name}</li>)} </ul> );}; |
3. Component is Not a Function
The Problem
When you try to render a component, the console throws a TypeError
indicating the component is not a function:
TypeError: Cannot read properties of undefined (reading 'call')
The Cause
The component was either not imported correctly or was defined using a lowercase name. In React, components must be named starting with an uppercase letter. React treats lowercase tags (like <mycomponent>
) as standard HTML elements (<div>
, <span>
).
The Solution
Ensure your component name starts with a capital letter and that your import
statements and file paths are correct.
❌ Bad Code | ✅ Good Code |
---|---|
jsx// mycomponent.jsfunction mycomponent() { /* ... */ } // In App.js:<mycomponent /> // React looks for an HTML element |
jsx// MyComponent.jsfunction MyComponent() { /* ... */ } // In App.js:<MyComponent /> // React looks for a component |
4. Objects Are Not Valid as a React Child
The Problem
You try to display a JavaScript object directly within JSX, and React throws a runtime error:
Error: Objects are not valid as a React child (found: object with keys {...}). If you meant to render a collection of children, use an array instead.
The Cause
JSX expressions can only render primitive values like strings, numbers, arrays, or React elements. They cannot render plain JavaScript objects because React doesn't know how to translate an object into a displayable DOM element.
The Solution
If you need to display an object for debugging, convert it to a string using JSON.stringify()
. If you need to display its properties, access them directly (e.g., user.name
).
❌ Bad Code | ✅ Good Code |
---|---|
jsxconst user = { name: "Alice" };<div>{user}</div> |
jsxconst user = { name: "Alice" };<div> {/* For debugging */} {JSON.stringify(user)} {/* To display a property */} <h1>Hello, {user.name}</h1></div> |
5. Maximum Update Depth Exceeded (Infinite Loop)
The Problem
Your component keeps re-rendering itself endlessly, crashing the application with a stack overflow error:
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls
setState
insidecomponentWillUpdate
orcomponentDidUpdate
.
The Cause
This is most often caused by calling a state setter function (setState
or useState
setter) directly within the main body of a functional component or inside a useEffect
hook without a proper dependency array.
The Solution
Only call state setters within event handlers, promise callbacks, or inside a useEffect
hook with a controlled dependency array (or an empty array []
for mounting effects).
❌ Bad Code | ✅ Good Code |
---|---|
jsxfunction Counter() { const [count, setCount] = useState(0); // ❌ Runs on every render, causing an infinite loop. setCount(count + 1); return <h1>{count}</h1>;} |
jsxfunction Counter() { const [count, setCount] = useState(0); // State update is now triggered by a user event. const handleClick = () => { setCount(prevCount => prevCount + 1); }; return ( <button onClick={handleClick}> Count: {count} </button> );} |
6. Expected a String But Got: undefined
The Problem
You pass a prop to a child component, but the prop's value is missing or undefined
, leading to a type warning:
Warning: Failed prop type: Invalid prop
text
of typeundefined
supplied toMyComponent
, expectedstring
.
The Cause
A prop that is expected to be a string or other displayable type is being passed as undefined
(often because a parent state or prop value is undefined
). React sees undefined
and assumes you made a mistake, as it generally expects a displayable value.
The Solution
Always provide a fallback value for props that might be missing, or use conditional rendering to prevent the component from being rendered if essential data is absent.
❌ Bad Code | ✅ Good Code |
---|---|
jsx// Assuming user.name is sometimes undefined<Greeting name={user.name} /> |
`jsx// Use OR ( |
7. React Hook "useEffect" is Called Conditionally
The Problem
Your code violates the fundamental Rules of Hooks, resulting in a critical error:
Error: React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render.
The Cause
You called a React Hook ({% raw %}useState
, useEffect
, etc.) inside a JavaScript control flow statement like an if
statement, a for
loop, or a nested function. React relies on a consistent, ordered list of Hook calls across every render.
The Solution
Hooks must always be called unconditionally at the top level of your functional component or custom hook. If you need conditional logic, put the condition inside the Hook's body.
❌ Bad Code | ✅ Good Code |
---|---|
jsxfunction MyComponent({ userId }) { if (userId) { // ❌ Error: Hook called conditionally useEffect(() => { fetchData(userId); }, [userId]); } // ...} |
jsxfunction MyComponent({ userId }) { useEffect(() => { // ✅ Condition is inside the hook's body if (userId) { fetchData(userId); } }, [userId]); // ...} |
8. Component's Children Not Recognized (Missing key)
The Problem
When rendering a list of elements using the map()
method, the console displays a warning:
Warning: Each child in a list should have a unique "key" prop.
The Cause
When React renders a list, it needs a stable identifier (key) for each item to track which items have changed, been added, or been removed. Without a unique key, React's reconciliation algorithm is inefficient, potentially leading to bugs, incorrect state, or performance issues.
The Solution
Assign a unique and stable identifier (like a database ID) to the outermost JSX element returned inside the map()
callback. Do not use the array index unless the list items are static and never change order.
❌ Bad Code | ✅ Good Code |
---|---|
jsx<ul> {items.map((item) => ( <li>{item.name}</li> // ❌ Missing key ))}</ul> |
jsx<ul> {items.map((item) => ( // ✅ Using a stable, unique item ID <li key={item.id}>{item.name}</li> ))}</ul> |
9. Invalid Hook Call Warning
The Problem
A generalized error that often indicates a problem with your project environment:
Warning: Invalid hook call. Hooks can only be called inside the body of a function component.
The Cause
This can occur for a few reasons:
- Multiple React Copies: Your project/bundle might contain two copies of the
react
library, confusing the Hook resolver. - Using Hooks outside Components: Calling a hook inside a regular JavaScript function or a class component method (must be in a functional component or custom hook).
The Solution
- Ensure you have one single installation of
react
andreact-dom
in yourpackage.json
. Deletenode_modules
and runnpm install
(oryarn
). - Only use Hooks directly within the body of a functional component or a custom hook (a function starting with
use
).
10. React.StrictMode Causing Side Effects Twice
The Problem
In development mode, you notice certain side effects—like state updates, console logs, or API calls—are executing twice when using useEffect
.
The Cause
This is intentional behavior of the React.StrictMode
component. Strict Mode runs renderers, state updates, and effects (including their cleanup functions) twice only in development to help you detect non-idempotent side effects (effects that produce different results when run multiple times).
The Solution
Treat this as a feature. Your goal is to make your effects idempotent. Always use a cleanup function (return () => {}
) inside useEffect
to tear down any subscriptions, intervals, or listeners when the component unmounts or before the next effect runs.
❌ Bad Code | ✅ Good Code |
---|---|
jsxuseEffect(() => { // ❌ Missing cleanup: interval runs twice const id = setInterval(() => { console.log('Ticking'); }, 1000);}); |
jsxuseEffect(() => { const id = setInterval(() => { console.log('Ticking'); }, 1000); // ✅ Cleanup function stops the first interval return () => { clearInterval(id); };}, []); |
11. Attempted to Import Relative Path Outside of Project
The Problem
During development or building, your bundler (like Webpack or Vite) throws an error related to file paths:
Error: Attempted to import
../../../../some/path/file.js
which is outside of the project root.
The Cause
You are using a relative path (../..//
) to import a file that is located outside the root directory of your React project (e.g., above the src/
folder). This is a security and configuration boundary enforced by the build tools.
The Solution
- Restructure: Move the imported file or module inside your project's root folder (
src/
). - Use absolute paths/aliases: Configure your build tool (e.g., using
jsconfig.json
ortsconfig.json
) to use path aliases. This allows you to import files using a custom root name, likeimport { utils } from '@utils/file'
.
12. State Updates Not Reflecting Immediately
The Problem
You call a state setter function, then immediately log the state, but the logged value is the old value:
setCount(c + 1);
console.log(count); // Still shows the OLD value
The Cause
React's state updates are asynchronous and are often batched for performance. When you call setCount(newState)
, React queues that update and doesn't immediately change the state variable in the current execution scope.
The Solution
- If you need the new state to compute the next state, use the functional update form of the setter:
setCount(prevCount => prevCount + 1)
. - If you need to perform an action after the state has definitely updated, use a
useEffect
hook that watches the state variable.
❌ Bad Code | ✅ Good Code |
---|---|
jsxconst handleClick = () => { setCount(count + 1); // ❌ Logs the old count console.log(count);}; |
jsx// ✅ Use useEffect to react to the changeuseEffect(() => { console.log('Count is now:', count);}, [count]); |
13. Uncontrolled to Controlled Input Warning
The Problem
You see a console warning when dealing with form inputs:
Warning: A component is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa).
The Cause
An input element starts as uncontrolled (when its value
prop is undefined
or null
), and then later its value
prop changes to a defined string. This transition confuses React's internal handling of the input state.
The Solution
Ensure that the value
prop of your controlled input is always defined, even if it's an empty string initially. If the initial data is asynchronous, set the state to a default empty string.
❌ Bad Code | ✅ Good Code |
---|---|
jsx// If user.name is undefined on first renderconst [user, setUser] = useState({}); <input value={user.name} onChange={handleChange} /> |
jsx// Initialize state with defined empty stringsconst [user, setUser] = useState({ name: '' }); // Value is always a string ('') or a name<input value={user.name} onChange={handleChange} /> |
14. Module Not Found / Import Path Error
The Problem
Your compiler or application fails to start with an error that a file or module cannot be found:
Error: Module not found: Error: Can't resolve './components/MyComponet' in '...'
The Cause
This is usually a simple typographical error, incorrect casing (file systems are case-sensitive!), or a wrong relative path in your import
statement.
The Solution
- Check Casing: Ensure the casing of the component name and the path (
MyComponent
vsMycomponent
) exactly matches the file system. - Verify Path: Use the correct relative path (
./
for current directory,../
for parent directory). - Check Imports/Exports: Make sure the name you import matches the name it was exported as (e.g.,
export default UserProfile
).
15. Memory Leak / Async State Update After Unmount
The Problem
The console throws a warning about setting state on an unmounted component, which is a classic symptom of a memory leak:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application.
The Cause
This occurs when an asynchronous operation (like a setTimeout
or an API fetch) finishes after the component that initiated it has been removed from the DOM, and the callback attempts to call a state setter.
The Solution
Use the useEffect
cleanup function to either cancel the ongoing async task (if possible) or use a local variable (often a ref
) to track the mounted state of the component.
❌ Bad Code | ✅ Good Code |
---|---|
jsxuseEffect(() => { // Assume fetchData takes time fetchData().then(data => { // ❌ Error if component unmounts before resolve setData(data); });}, []); |
jsxfunction MyComponent() { const [data, setData] = useState(null); const mounted = useRef(true); useEffect(() => { // Cleanup function sets the flag to false on unmount return () => { mounted.current = false; }; }, []); fetchData().then(data => { if (mounted.current) { // ✅ Only set state if still mounted setData(data); } });} |
Professional Insight: Mobile App Developers 👨💻
Mastering these 15 common errors is crucial for efficient React development. While many React developers focus on web applications, the same principles, state management practices, and error-handling techniques are directly applicable to building mobile apps using React Native.
The Connection to Mobile App Development
The errors discussed—from "Cannot Read Property of Undefined" (due to asynchronous data) to "Maximum Update Depth Exceeded" (infinite loops) and "Memory Leak / Async State Update After Unmount" (Error #15)—are highly relevant to React Native. In a mobile environment, a lack of cleanup or an infinite loop can lead to significant jank and app crashes.
Solution (Hiring Insight: Hire Mobile App Developers)
When you Hire React Mobile App Developers, look for those who demonstrate a deep understanding of these core React principles. A skilled React Native developer will:
- Prioritize Thread Safety: They know how to use Hooks correctly to avoid blocking the JavaScript thread, which is vital for smooth mobile UI interactions.
- Enforce Cleanups: They routinely use the
return
function inuseEffect
to clean up subscriptions and timers, preventing the memory leaks that are common in mobile environments. - Optimize List Rendering: They understand the critical performance impact of
key
props and efficient list components (FlatList
,SectionList
) in mobile viewports.
Hiring developers with strong foundational React knowledge ensures your mobile application is performant, stable, and maintainable.
Top comments (0)