React 18.3: the warning shot before React 19

Reproduce a warning added in React 18.3, fix it without losing the fallback value, and test root migration beyond a quiet console.
Use the warning release as a checkpoint
React’s 18.3.0 release notes are dated 25 April 2024 and describe the release as equivalent to 18.2 with additional migration warnings. This historical explainer is for a maintainer already using React 18. It does not recommend starting a new application on that version.
The reproduction below runs the published entry point with React and React DOM 18.3.1. A separate matched 18.2.0 build checks whether a warning is actually new. The application is a synthetic counter with an event subscription. It supplies reproducible evidence, not a claim about a customer migration.
Reproduce a warning that 18.3 added
The fixture renders a Greeting function component that reads a name prop and sets Greeting.defaultProps to a Reader fallback. The development build with 18.2.0 renders without a defaultProps warning. The matched 18.3.1 build warns that support for function-component defaultProps will be removed and identifies Greeting in the component stack.
The fix moves the fallback into the function parameter: name defaults to Reader when the prop is absent. The corrected 18.3.1 case still renders Hello Reader and emits no defaultProps warning. Check output alongside diagnostics because the replacement must preserve the fallback as well as remove the warning.
This is one exercised warning path. It does not inventory every warning in an application or prove React 19 compatibility. I recommend recording how to reproduce each warning and who owns the fix. Keep its component stack alongside the behavior test. A dependency warning needs a dependency fix or an explicitly tracked exception; hiding the console output does neither.
Replace a legacy client root deliberately
The entry point below assumes an empty host element with ID root and a JSX-capable build. The bundle provides App.jsx and uses the automatic JSX transform. If the host element is missing, the tested guard throws Missing application root element.
The fixture also runs the old ReactDOM.render entry point and captures its legacy-root warning. That warning predates 18.3. It is a separate cleanup task, not the evidence for a newly added 18.3 diagnostic. If a framework owns your entry point, follow its integration path rather than replacing it with this standalone example.
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
const container = document.getElementById('root');
if (!container) {
throw new Error('Missing application root element');
}
const root = createRoot(container);
root.render(<StrictMode><App /></StrictMode>);
Check subscriptions and server DOM identity
App subscribes to a synthetic browser event inside an effect and removes the listener in cleanup. With StrictMode at the root, the development build records setup, cleanup, then setup again. One dispatched event increments the counter once. Removing the unsubscribe leaves two listeners, so the same event increments it twice.
The production build of the same entry point records one setup and no replay cleanup. It still handles one event once. These observations explain the development check without suggesting production intentionally repeats every effect. The counter is a small subscription test; it does not establish that a real modal restores focus or that a form cancellation flow works.
The server-rendering comparison starts from React-generated counter markup and retains a reference to its button. hydrateRoot preserves that node while attaching client behavior. createRoot replaces it even though the final text looks identical. Testing only the visible text would miss the difference.
A further failure case renders zero on the server and starts the client counter at one. React reports mismatched text and replacement of the server HTML. A page that eventually looks correct can therefore still have failed hydration. The fixture verifies this small component tree, not a framework’s streaming or data-loading integration.
Reproduce the checkpoint before the major upgrade
Download the source and tests, extract them, and run python3 frontend/scripts/verify-react-183.py from the archive root with Docker available. The bundle pins the dependency graph and browser image. Image construction needs network access; execution uses only a loopback server inside a container with external networking disabled. No application credentials or host-directory mounts are needed.
The eleven checks cover the warning comparison and fix, root guard, event handling, development versus production effects, and hydration identity and diagnostics. Results were captured with Node 24.20.0 and Chromium 153.0.8010.12. These modern test tools reproduce historical React behavior; the package set is not a recommendation for a new application.
Treat React 19 as a subsequent compatibility decision. Its upgrade guide requires the modern JSX transform and removes function-component defaultProps support while retaining class-component defaults. Those are sourced major-version requirements, not a React 19 runtime test in this bundle. Re-run your own application’s relevant flows after upgrading, including paths that were never opened during warning collection.
Reproduce the examples
Exact entry point, synthetic App, matched React versions, pinned browser and eleven checks including deliberate failures.
Download the source and tests (.zip)
