Using Strict Mode in React 18: A Guide to Its New Behaviors

Understand the significant changes React 18 brought to Strict Mode, including the double-mounting behavior that helps catch bugs before they reach production.

React Strict Mode has been a valuable development tool since its introduction, but React 18 brought significant changes to how it operates. Understanding these changes is essential for any React developer working with modern web applications.

Strict Mode is not a UI component itself but a wrapper that enables additional development-time checks and warnings for your entire application. When Strict Mode is active, React performs extra validations and simulations to help identify potential problems before they reach production.

The core philosophy behind Strict Mode is to catch issues early in the development process when they are easiest to diagnose and fix. By simulating various scenarios and edge cases, Strict Mode helps developers identify code that might work in simple cases but could fail under more complex conditions. This proactive approach to debugging leads to more robust applications and reduces the likelihood of mysterious bugs appearing after deployment.

What Strict Mode Does in React 18

Double-Mount Simulation

Components are mounted, unmounted, and remounted to catch issues with effects and state initialization that only appear under repeated mount cycles.

Unsafe Lifecycle Warnings

Strict Mode warns about deprecated lifecycle methods that may not work correctly with concurrent features in newer React versions.

Side Effect Detection

Code that performs side effects directly in render or outside proper effect cleanup is flagged to prevent unexpected behavior.

Legacy API Alerts

Warnings about deprecated APIs and patterns that will be removed in future React versions help with long-term maintainability.

The Double-Mounting Behavior Explained

The most notable change in React 18 Strict Mode is the intentional double-mounting of components during development. When a component is wrapped in Strict Mode, React mounts it, then unmounts it, then mounts it again immediately. This happens during the initial render and whenever Hot Module Replacement updates the component during development.

Why Double Mount?

In production, components can be mounted, unmounted, and remounted for many reasons: navigation changes, conditional rendering, error boundary recovery, or concurrent rendering interruptions. If a component works correctly when mounted once but fails when mounted multiple times, it represents a latent bug that might appear in production.

The double-mount simulation helps identify:

  • Components without proper effect cleanup
  • State initialization issues under repeated mounts
  • Subscription accumulation without proper unsubscription
  • Timer duplication without proper cleanup

This behavior only occurs in development mode, so production builds are not affected by this extra processing.

Understanding that this behavior only occurs in development mode is crucial. Production builds do not perform this double-mount simulation, so the performance cost is limited to the development experience. The goal is to surface issues during development when they are easy to identify and fix.

How Double Invocation Affects Hooks and Effects

useState Behavior

The useState hook is initialized twice during Strict Mode's double-mount. This is normal--the second call receives the same initial value as the first. However, if code relies on useState being called exactly once for initialization, it may need restructuring.

useEffect Changes

The useEffect hook sees more significant changes in React 18 development:

  1. Effect function runs on mount
  2. Cleanup function runs during unmount simulation
  3. Effect function runs again on remount

This means effects with empty dependency arrays run twice during initial render, revealing missing cleanup functions.

Missing Cleanup Detection

If an effect sets up subscriptions, timers, or DOM modifications without returning cleanup, Strict Mode will reveal the issue. The first setup remains active during the second setup, creating duplicate resources that only partially get cleaned up.

According to the official React documentation on StrictMode, this behavior helps identify effects that are not properly handling cleanup operations.

Problem: Missing Effect Cleanup
1function UserProfile({ userId }) {2 const [user, setUser] = useState(null);3 4 useEffect(() => {5 fetch(`/api/users/${userId}`)6 .then(response => response.json())7 .then(data => setUser(data));8 // Missing cleanup - no return statement!9 }, [userId]);10 11 if (!user) return <Loading />;12 return <div>{user.name}</div>;13}

The Fix: Proper Cleanup

The solution is to return a cleanup function that prevents outdated responses from affecting state. For APIs that support cancellation, using AbortController is the preferred approach as it integrates well with React's effect cleanup pattern.

Solution: Proper Cleanup with AbortController
1function UserProfile({ userId }) {2 const [user, setUser] = useState(null);3 4 useEffect(() => {5 const controller = new AbortController();6 const { signal } = controller;7 8 fetch(`/api/users/${userId}`, { signal })9 .then(response => response.json())10 .then(data => setUser(data))11 .catch(error => {12 if (error.name !== 'AbortError') {13 console.error('Fetch error:', error);14 }15 });16 17 return () => controller.abort();18 }, [userId]);19 20 if (!user) return <Loading />;21 return <div>{user.name}</div>;22}

Common Issues Revealed by Strict Mode

Missing Subscription Cleanup

Subscriptions to external data sources are a common source of Strict Mode issues. Without cleanup, each mount creates a new subscription, and those subscriptions accumulate over time.

// Problem: Missing cleanup for subscription
useEffect(() => {
 const channel = dataSource.subscribe(matchId, handleData);
 // Missing: return () => channel.unsubscribe();
}, [matchId, handleData]);

The fix: Always return a cleanup function that undoes the setup.

// Solution: Proper subscription cleanup
useEffect(() => {
 const channel = dataSource.subscribe(matchId, handleData);
 return () => channel.unsubscribe();
}, [matchId, handleData]);

The cleanup function ensures that each subscription is properly closed when the effect re-runs or when the component unmounts. Under Strict Mode, the first subscription is cleaned up before the second is created, preventing duplicate active subscriptions that can lead to memory leaks and unexpected behavior.

Our React development services team specializes in building applications that follow these best practices, ensuring your components handle all lifecycle scenarios correctly.

Timer Management

Timers created with setInterval or setTimeout also need proper cleanup. Without it, each mount creates a new timer, and the old timers continue running.

The fix: Clear timers in the cleanup function.

useEffect(() => {
 const timer = setInterval(() => {
 refreshData().then(setData);
 }, intervalMs);

 return () => clearInterval(timer);
}, [intervalMs]);

Without cleanup, each mount creates a new interval, and the old intervals continue running. Under Strict Mode, this becomes immediately apparent because the component's mount-unmount-remount sequence creates two overlapping intervals before either is cleaned up.

As noted in the LogRocket guide on React 18 Strict Mode, this behavior helps identify timer-related issues that would otherwise be difficult to diagnose in production environments.

Best Practices for Strict Mode Compatibility

Always Provide Cleanup

Effects performing setup operations should return cleanup functions that undo the setup, including subscriptions, timers, and DOM modifications.

Use AbortController

For fetch operations, use AbortController to support request cancellation in cleanup. This is the standard mechanism for canceling requests.

Keep Effects Focused

Separate concerns into multiple effects with their own cleanup for better maintainability and easier debugging.

Avoid Side Effects in Render

Keep render functions pure--move side effects to proper effect hooks with cleanup. Side effects in render can cause confusing behavior.

When to Temporarily Disable Strict Mode

While Strict Mode is valuable for catching issues, there are rare situations where you might need to temporarily disable it: working with third-party libraries that have known Strict Mode incompatibilities, or debugging an issue and want to isolate whether Strict Mode's behavior is masking the real problem.

Disabling Strict Mode is straightforward but should be a temporary measure while you work on a proper fix:

// Disable Strict Mode for specific component subtree
function ThirdPartyComponent() {
 return (
 <React.StrictMode>
 <LegacyWrapper>
 <ThirdPartyLibraryComponent />
 </LegacyWrapper>
 </React.StrictMode>
 );
}

Important: Disabling Strict Mode hides potential issues rather than fixing them. The goal should be writing Strict Mode-compatible code, not working around it. According to the GeeksforGeeks coverage of React 18 Strict Mode, issues caught by Strict Mode are real issues that might manifest in production under different circumstances.

For teams building modern React applications, our frontend development services can help ensure your codebase follows Strict Mode best practices from the start.

The Future: Strict Mode and Concurrent Features

React 18's concurrent features change how components may be mounted, unmounted, and remounted. Concurrent rendering allows React to prepare multiple versions of the UI simultaneously and switch between them based on which finishes first. This capability enables better responsiveness in complex applications but also means that components may be mounted, unmounted, and remounted more frequently than in previous React versions.

Strict Mode's double-mount behavior is partly a preparation for this future. Components that work correctly with repeated mount cycles are better positioned to take advantage of concurrent features without unexpected behavior. As React continues to develop, the assumptions enforced by Strict Mode will become increasingly important for application stability.

The patterns that Strict Mode enforces--proper effect cleanup, pure render functions, and resilient state management--align with best practices for React development regardless of Strict Mode. Using Strict Mode from the start helps develop intuitions and habits that lead to better code overall.

Explore our comprehensive web development services to build applications that embrace these modern React patterns and take full advantage of concurrent features.

Conclusion

React 18's Strict Mode introduces behaviors that initially seem strange but serve an important purpose: helping developers write more robust code that catches bugs before they reach production.

By understanding why Strict Mode does what it does, recognizing common issues, and following best practices, you can not only pass Strict Mode's checks but also write better React code overall. The investment in writing Strict Mode-compatible code pays dividends throughout a project's lifetime.

Components that properly clean up effects, handle repeated mount cycles, and avoid side effects in render are more predictable, easier to maintain, and less likely to cause mysterious bugs. As React continues to evolve, these practices become increasingly valuable for maintaining application stability while adopting new features.

Need help building robust React applications that follow Strict Mode best practices? Our team specializes in web development services that prioritize code quality, maintainability, and production reliability.

Frequently Asked Questions

Need Help Building Robust React Applications?

Our team specializes in building maintainable, production-ready React applications that follow best practices for Strict Mode compatibility and beyond.