Props in React might feel straightforward, but mishandling them can slow your app to a crawl. Over time, after coding and seeing some React projects, I’ve noticed ten props-related mistakes that keep popping up. These issues could be lurking in your code, too.
Don’t worry, though—we’re here to fix them together. Let’s dive in and make your React app faster and more efficient!
1} Stop Passing Entire Objects When Only Specific Properties Are Needed
Passing entire objects as props when your component only needs a couple of properties leads to unnecessary re-renders anytime that object updates—even if the values you're using remain the same.
// Not ideal
function UserProfile({ user }) {
// Only uses name and email
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
// Better
function UserProfile({ name, email }) {
return (
<div>
<h2>{name}</h2>
<p>{email}</p>
</div>
);
}
Only pass what’s necessary. By keeping props focused and minimal, you’ll reduce how often components re-render, giving your app a noticeable boost in performance.
2} Avoid Creating New Objects in Props on Every Render
Another hidden culprit is creating inline objects in props. When you do this, you create new object references every time your component renders. These new references force child components to re-render, even if the values are identical.
// Not efficient
function ParentComponent() {
return (
<ChildComponent
styles={{ margin: '20px', padding: '10px' }}
config={{ theme: 'dark', animate: true }}
/>
);
}
// Smarter approach
const styles = { margin: '20px', padding: '10px' };
const config = { theme: 'dark', animate: true };
function ParentComponent() {
return (
<ChildComponent
styles={styles}
config={config}
/>
);
}
Take object definitions outside your component or use useMemo for dynamically created ones. It’s a simple tweak, but it can dramatically reduce unnecessary rendering and keep everything running smoothly.
3} Avoid Unnecessarily Spreading Props
Using prop spreading (...props) might feel convenient, but it often does more harm than good. It passes down props you don’t need, makes your component harder to debug, and can even trigger unwanted re-renders.
// Inefficient
function Parent() {
const props = {
name: 'John',
age: 30,
email: 'john@email.com',
phone: '1234567890',
address: '123 Street'
};
return <UserCard {...props} />;
}
// A better way
function Parent() {
const props = {
name: 'John',
age: 30,
email: 'john@email.com',
phone: '1234567890',
address: '123 Street'
};
return <UserCard name={props.name} email={props.email} />;
}
By specifying only the props you need, you make your component cleaner and more predictable. This keeps your app faster and easier to maintain.
4} Always Memoize Callback Props
Unmemoized callback functions can silently hurt performance. Every time your component re-renders, a new function instance is created. This can break optimizations in child components using React.memo or cause unnecessary re-renders.
// Not optimal
function TodoList() {
const handleClick = (id) => {
// handle click
};
return <TodoItem onClick={handleClick} />;
}
// Optimal approach
function TodoList() {
const handleClick = useCallback((id) => {
// handle click
}, []); // Include dependencies if needed
return <TodoItem onClick={handleClick} />;
}
Wrap callback props with useCallback when passing them to memoized components or using them in useEffect. This ensures stable references and avoids unnecessary updates.
5} Stop Prop Drilling Through Multiple Levels
Passing props through several components that don’t even use them is a surefire way to create unnecessary re-renders and messy code. This is called prop drilling, and it can make your app harder to manage as it grows.
// Not ideal
function GrandParent({ user }) {
return <Parent user={user} />;
}
function Parent({ user }) {
return <Child user={user} />;
}
function Child({ user }) {
return <span>{user.name}</span>;
}
// Smarter solution
function App() {
const [user] = useState(userData);
return (
<UserContext.Provider value={user}>
<GrandParent />
</UserContext.Provider>
);
}
function Child() {
const user = useContext(UserContext);
return <span>{user.name}</span>;
}
Instead of passing props down through every layer, use tools like React Context or libraries like Zustand for managing deeply nested data. This approach keeps your code cleaner and avoids unnecessary renders.
6} Don’t Use Array Indexes as Keys
Using array indexes as keys might seem harmless, but it can cause subtle bugs and performance problems, especially in lists where items are reordered or removed.
// Problematic
function TodoList({ todos }) {
return (
<ul>
{todos.map((todo, index) => (
<TodoItem key={index} todo={todo} />
))}
</ul>
);
}
// Better approach
function TodoList({ todos }) {
return (
<ul>
{todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} />
))}
</ul>
);
}
Always use stable, unique identifiers as keys. This helps React track your components properly, ensuring smooth updates and maintaining state accurately.
7} Stop Passing Down Unused Props
Passing unnecessary props can bloat your components and trigger avoidable re-renders. Every extra prop adds to the overhead, even if it’s not being used in the component.
// Inefficient
function UserCard({ user, theme, analytics, permissions, ...otherProps }) {
// Only uses name and avatar
return (
<div>
<img src={user.avatar} alt={user.name} />
<h2>{user.name}</h2>
</div>
);
}
// Streamlined
function UserCard({ avatar, name }) {
return (
<div>
<img src={avatar} alt={name} />
<h2>{name}</h2>
</div>
);
}
Refactor your components regularly and remove any props that aren’t essential. A leaner component means fewer re-renders and a faster app.
8} Always Use Proper Prop Types
Skipping PropTypes or TypeScript is a common mistake that can lead to bugs and runtime errors. These tools help catch prop-related issues during development, making your app more robust and easier to debug.
// Risky approach
function Button(props) {
return <button onClick={props.onClick}>{props.children}</button>;
}
// Safer with PropTypes
Button.propTypes = {
onClick: PropTypes.func.isRequired,
children: PropTypes.node.isRequired,
};
// Even better with TypeScript
interface ButtonProps {
onClick: () => void;
children: React.ReactNode;
}
function Button({ onClick, children }: ButtonProps) {
return <button onClick={onClick}>{children}</button>;
}
Using TypeScript or PropTypes not only helps you spot problems early but also makes your components more predictable and maintainable.
9} Never Mutate Props Directly
Directly changing props goes against React's immutability principles, often leading to unexpected bugs and missed updates.
// Problematic approach
function TodoList({ todos }) {
const handleComplete = (index) => {
todos[index].completed = true; // Mutating the prop directly
};
return (
<ul>
{todos.map((todo, index) => (
<TodoItem
key={todo.id}
todo={todo}
onComplete={() => handleComplete(index)}
/>
))}
</ul>
);
}
// Correct method
function TodoList({ todos, updateTodo }) {
const handleComplete = (id) => {
updateTodo(id, { completed: true }); // Updating through props
};
return (
<ul>
{todos.map((todo) => (
<TodoItem
key={todo.id}
todo={todo}
onComplete={() => handleComplete(todo.id)}
/>
))}
</ul>
);
}
Keep props immutable by using functions or state updates instead. This ensures that React can track changes properly and re-render components when needed.
Conclusion
These prop mistakes might seem small, but they stack up to create serious performance issues over time. To keep your React app running smoothly:
- Only pass the props that are actually needed by components.
- Use useCallback to memoize functions and avoid unnecessary renders.
- Rely on state management tools like Context or Zustand instead of drilling props.
- Never mutate props directly—always work with immutable updates.
- Use TypeScript or PropTypes to enforce type safety and catch bugs earlier.
Tools to help you optimize:
- React DevTools Performance Tab: Pinpoint performance bottlenecks.
- why-did-you-render: Detect unnecessary renders automatically.
- ESLint React Hooks Plugin: Ensure proper hook usage.
- TypeScript: Add static typing for better reliability.
Fix these issues today, and you’ll notice your app feels faster, more responsive, and easier to maintain.
Happy coding!!
Top comments (0)