chore(deps): upgrade react to 19.2.7 and react types to 19.x (#1154)

Bump react and react-dom 19.1.2 -> 19.2.7 and @types/react and
@types/react-dom from v18 to v19. Keeps babel-plugin-react-compiler,
which is a separate build-time tool, not part of the React 19 runtime.

The @types v19 upgrade surfaced 21 type-only errors, all fixed without
runtime behavior changes:

- useRef<T>() now requires an explicit argument: pass undefined
- DOM element refs are RefObject<T | null>: widen PostFormFields and
  AdvancedSettings ref prop types accordingly
- retype reply-modal onInput handler to FormEvent + currentTarget
- add a null guard for the boards-bar search input
- update test mocks for stricter cloneElement/forwardRef typings

Verified: type-check, lint, 1051 tests, and build pass; react-doctor
score unchanged at 54; browser smoke across Chromium/Firefox/WebKit
plus mobile viewport clean.
This commit is contained in:
Tommaso Casaburi
2026-06-05 18:43:28 +07:00
committed by GitHub
parent 70ffeb816e
commit 9f58bf7e22
13 changed files with 69 additions and 97 deletions
+1 -1
View File
@@ -60,7 +60,7 @@ const SearchBar = ({ setShowSearchBar }: { setShowSearchBar: (show: boolean) =>
const handleSearchSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const searchInput = searchInputRef.current?.value;
if (searchInput) {
if (searchInput && searchInputRef.current) {
searchInputRef.current.value = '';
navigate(`/${searchInput}`);
setShowSearchBar(false);
@@ -52,7 +52,15 @@ const testState = vi.hoisted(() => ({
}));
vi.mock('react-i18next', () => ({
Trans: ({ components, i18nKey, values }: { components?: Record<number, React.ReactElement>; i18nKey: string; values?: Record<string, unknown> }) =>
Trans: ({
components,
i18nKey,
values,
}: {
components?: Record<number, React.ReactElement<Record<string, unknown>>>;
i18nKey: string;
values?: Record<string, unknown>;
}) =>
createElement(
'span',
{ 'data-testid': `trans-${i18nKey}` },
@@ -34,7 +34,7 @@ const testState = vi.hoisted(() => ({
}));
vi.mock('react-i18next', () => ({
Trans: ({ components, i18nKey }: { components?: Record<number, React.ReactElement>; i18nKey: string }) =>
Trans: ({ components, i18nKey }: { components?: Record<number, React.ReactElement<Record<string, unknown>>>; i18nKey: string }) =>
createElement(
'span',
{ 'data-testid': `trans-${i18nKey}` },
+4 -4
View File
@@ -156,10 +156,10 @@ interface PostFormFieldsProps {
isBbcodePreviewing: boolean;
postCid: string;
subjectRef: React.Ref<HTMLInputElement>;
optionsRef: React.RefObject<HTMLInputElement>;
flagRef: React.RefObject<HTMLSelectElement>;
flashTagRef: React.RefObject<HTMLSelectElement>;
textRef: React.RefObject<HTMLTextAreaElement>;
optionsRef: React.RefObject<HTMLInputElement | null>;
flagRef: React.RefObject<HTMLSelectElement | null>;
flashTagRef: React.RefObject<HTMLSelectElement | null>;
textRef: React.RefObject<HTMLTextAreaElement | null>;
urlRef: React.Ref<HTMLInputElement>;
url: string;
lengthError: string | null;
+3 -3
View File
@@ -358,9 +358,9 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
}
}, [showReplyModal]);
const handleContentInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
lastSelectionStartRef.current = e.target.selectionStart ?? e.target.value.length;
lastSelectionEndRef.current = e.target.selectionEnd ?? lastSelectionStartRef.current;
const handleContentInput = (e: React.FormEvent<HTMLTextAreaElement>) => {
lastSelectionStartRef.current = e.currentTarget.selectionStart ?? e.currentTarget.value.length;
lastSelectionEndRef.current = e.currentTarget.selectionEnd ?? lastSelectionStartRef.current;
};
const handleContentValueChange = (content: string, selectionStart?: number, selectionEnd?: number, options = optionsRef.current?.value || '') => {
@@ -38,19 +38,8 @@ vi.mock('react-router-dom', async () => {
const ReactModule = await vi.importActual<typeof import('react')>('react');
return {
Link: ReactModule.forwardRef(
(
{
children,
to,
...props
}: {
children?: React.ReactNode;
to: string;
[key: string]: unknown;
},
ref,
) => ReactModule.createElement('a', { ...props, href: to, ref }, children),
Link: ReactModule.forwardRef<HTMLAnchorElement, { children?: React.ReactNode; to: string }>(({ children, to, ...props }, ref) =>
ReactModule.createElement('a', { ...props, href: to, ref }, children),
),
useLocation: () => ({
pathname: testState.locationPath,
@@ -6,16 +6,16 @@ import { canConfigureBrowserPureP2P, isBrowserPureP2PEnabled } from '../../../li
import styles from './advanced-settings.module.css';
interface SettingsProps {
ipfsGatewayUrlsRef?: RefObject<HTMLTextAreaElement>;
mediaIpfsGatewayUrlRef?: RefObject<HTMLInputElement>;
pubsubProvidersRef?: RefObject<HTMLTextAreaElement>;
httpRoutersRef?: RefObject<HTMLTextAreaElement>;
ethRpcRef?: RefObject<HTMLTextAreaElement>;
p2pRpcRef?: RefObject<HTMLInputElement>;
p2pDataPathRef?: RefObject<HTMLInputElement>;
ipfsGatewayUrlsRef?: RefObject<HTMLTextAreaElement | null>;
mediaIpfsGatewayUrlRef?: RefObject<HTMLInputElement | null>;
pubsubProvidersRef?: RefObject<HTMLTextAreaElement | null>;
httpRoutersRef?: RefObject<HTMLTextAreaElement | null>;
ethRpcRef?: RefObject<HTMLTextAreaElement | null>;
p2pRpcRef?: RefObject<HTMLInputElement | null>;
p2pDataPathRef?: RefObject<HTMLInputElement | null>;
onPureP2PBrowserChange?: (enabled: boolean) => void;
pureP2PBrowserEnabled?: boolean;
pureP2PBrowserRef?: RefObject<HTMLInputElement>;
pureP2PBrowserRef?: RefObject<HTMLInputElement | null>;
}
type AccountProtocolOptions = {