Skip to content

Commit a07f022

Browse files
justin808claude
andcommitted
Fix TypeScript compatibility with @types/react-dom@19
React 19's type definitions removed the deprecated hydrate, render, and unmountComponentAtNode APIs. Added a LegacyReactDOM interface to provide types for these methods that still exist at runtime for React 16/17 compatibility. Also removes unnecessary eslint-disable directives and fixes ReactElement type parameter warnings. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f36c4ff commit a07f022

File tree

3 files changed

+19
-11
lines changed

3 files changed

+19
-11
lines changed

packages/react-on-rails/src/ClientRenderer.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,6 @@ function unmountAllComponents(): void {
174174
root.unmount();
175175
} else {
176176
// React 16-17 legacy API
177-
// eslint-disable-next-line @typescript-eslint/no-deprecated
178177
unmountComponentAtNode(domNode);
179178
}
180179
} catch (error) {

packages/react-on-rails/src/reactApis.cts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ import * as ReactDOM from 'react-dom';
44
import type { ReactElement } from 'react';
55
import type { RenderReturnType } from './types/index.ts' with { 'resolution-mode': 'import' };
66

7+
// Type for legacy React DOM APIs (React 16/17) that were removed from @types/react-dom@19
8+
// These are only used at runtime when supportsRootApi is false
9+
interface LegacyReactDOM {
10+
hydrate(element: ReactElement, container: Element): void;
11+
render(element: ReactElement, container: Element): RenderReturnType;
12+
unmountComponentAtNode(container: Element): boolean;
13+
}
14+
715
const reactMajorVersion = Number(ReactDOM.version?.split('.')[0]) || 16;
816

917
// TODO: once we require React 18, we can remove this and inline everything guarded by it.
@@ -29,12 +37,14 @@ if (supportsRootApi) {
2937

3038
type HydrateOrRenderType = (domNode: Element, reactElement: ReactElement) => RenderReturnType;
3139

32-
/* eslint-disable @typescript-eslint/no-deprecated,@typescript-eslint/no-non-null-assertion,react/no-deprecated --
33-
* while we need to support React 16
34-
*/
40+
// Cast ReactDOM to include legacy APIs for React 16/17 compatibility
41+
// These methods exist at runtime but are removed from @types/react-dom@19
42+
const legacyReactDOM = ReactDOM as unknown as LegacyReactDOM;
43+
44+
/* eslint-disable @typescript-eslint/no-non-null-assertion -- reactDomClient is always defined when supportsRootApi is true */
3545
export const reactHydrate: HydrateOrRenderType = supportsRootApi
3646
? reactDomClient!.hydrateRoot
37-
: (domNode, reactElement) => ReactDOM.hydrate(reactElement, domNode);
47+
: (domNode, reactElement) => legacyReactDOM.hydrate(reactElement, domNode);
3848

3949
export function reactRender(domNode: Element, reactElement: ReactElement): RenderReturnType {
4050
if (supportsRootApi) {
@@ -43,14 +53,13 @@ export function reactRender(domNode: Element, reactElement: ReactElement): Rende
4353
return root;
4454
}
4555

46-
// eslint-disable-next-line react/no-render-return-value
47-
return ReactDOM.render(reactElement, domNode);
56+
return legacyReactDOM.render(reactElement, domNode);
4857
}
4958

50-
export const unmountComponentAtNode: typeof ReactDOM.unmountComponentAtNode = supportsRootApi
59+
export const unmountComponentAtNode = supportsRootApi
5160
? // not used if we use root API
5261
() => false
53-
: ReactDOM.unmountComponentAtNode;
62+
: (container: Element) => legacyReactDOM.unmountComponentAtNode(container);
5463

5564
export const ensureReactUseAvailable = () => {
5665
if (!('use' in React) || typeof React.use !== 'function') {

packages/react-on-rails/src/types/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,9 @@ interface ServerRenderResult {
125125
error?: Error;
126126
}
127127

128-
type CreateReactOutputSyncResult = ServerRenderResult | ReactElement<unknown>;
128+
type CreateReactOutputSyncResult = ServerRenderResult | ReactElement;
129129

130-
type CreateReactOutputAsyncResult = Promise<string | ServerRenderHashRenderedHtml | ReactElement<unknown>>;
130+
type CreateReactOutputAsyncResult = Promise<string | ServerRenderHashRenderedHtml | ReactElement>;
131131

132132
type CreateReactOutputResult = CreateReactOutputSyncResult | CreateReactOutputAsyncResult;
133133

0 commit comments

Comments
 (0)