From 9f58bf7e226ab46175f49d8d36cc65f843364d88 Mon Sep 17 00:00:00 2001 From: Tommaso Casaburi Date: Fri, 5 Jun 2026 18:43:28 +0700 Subject: [PATCH] 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() now requires an explicit argument: pass undefined - DOM element refs are RefObject: 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. --- package.json | 8 +- src/components/boards-bar/boards-bar.tsx | 2 +- .../__tests__/comment-content.test.tsx | 10 +- .../edit-menu/__tests__/edit-menu.test.tsx | 2 +- src/components/post-form/post-form.tsx | 8 +- src/components/reply-modal/reply-modal.tsx | 6 +- .../__tests__/reply-quote-preview.test.tsx | 15 +-- .../advanced-settings/advanced-settings.tsx | 16 ++-- src/hooks/use-delete-failed-post.ts | 2 +- src/hooks/use-publish-post.ts | 2 +- src/hooks/use-publish-reply.ts | 2 +- src/views/post/post.tsx | 2 +- yarn.lock | 91 +++++++------------ 13 files changed, 69 insertions(+), 97 deletions(-) diff --git a/package.json b/package.json index 58be8c84..7279848f 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,8 @@ "@react-spring/web": "10.0.3", "@ruffle-rs/ruffle": "0.2.0", "@types/node": "20.19.37", - "@types/react": "18.2.25", - "@types/react-dom": "18.2.10", + "@types/react": "19.2.16", + "@types/react-dom": "19.2.3", "@use-gesture/react": "10.3.1", "@vercel/analytics": "^1.6.1", "ace-builds": "1.41.0", @@ -37,9 +37,9 @@ "json-stringify-pretty-compact": "4.0.0", "lodash": "4.18.0", "memoizee": "0.4.15", - "react": "19.1.2", + "react": "19.2.7", "react-ace": "14.0.1", - "react-dom": "19.1.2", + "react-dom": "19.2.7", "react-i18next": "16.6.6", "react-router-dom": "6.30.2", "react-router-hash-link": "2.4.3", diff --git a/src/components/boards-bar/boards-bar.tsx b/src/components/boards-bar/boards-bar.tsx index d6b733bc..6d20b138 100644 --- a/src/components/boards-bar/boards-bar.tsx +++ b/src/components/boards-bar/boards-bar.tsx @@ -60,7 +60,7 @@ const SearchBar = ({ setShowSearchBar }: { setShowSearchBar: (show: boolean) => const handleSearchSubmit = (event: React.FormEvent) => { event.preventDefault(); const searchInput = searchInputRef.current?.value; - if (searchInput) { + if (searchInput && searchInputRef.current) { searchInputRef.current.value = ''; navigate(`/${searchInput}`); setShowSearchBar(false); diff --git a/src/components/comment-content/__tests__/comment-content.test.tsx b/src/components/comment-content/__tests__/comment-content.test.tsx index 8bd59817..cf4b94b5 100644 --- a/src/components/comment-content/__tests__/comment-content.test.tsx +++ b/src/components/comment-content/__tests__/comment-content.test.tsx @@ -52,7 +52,15 @@ const testState = vi.hoisted(() => ({ })); vi.mock('react-i18next', () => ({ - Trans: ({ components, i18nKey, values }: { components?: Record; i18nKey: string; values?: Record }) => + Trans: ({ + components, + i18nKey, + values, + }: { + components?: Record>>; + i18nKey: string; + values?: Record; + }) => createElement( 'span', { 'data-testid': `trans-${i18nKey}` }, diff --git a/src/components/edit-menu/__tests__/edit-menu.test.tsx b/src/components/edit-menu/__tests__/edit-menu.test.tsx index 277663cb..915cac32 100644 --- a/src/components/edit-menu/__tests__/edit-menu.test.tsx +++ b/src/components/edit-menu/__tests__/edit-menu.test.tsx @@ -34,7 +34,7 @@ const testState = vi.hoisted(() => ({ })); vi.mock('react-i18next', () => ({ - Trans: ({ components, i18nKey }: { components?: Record; i18nKey: string }) => + Trans: ({ components, i18nKey }: { components?: Record>>; i18nKey: string }) => createElement( 'span', { 'data-testid': `trans-${i18nKey}` }, diff --git a/src/components/post-form/post-form.tsx b/src/components/post-form/post-form.tsx index 684bc855..efc6d2fa 100644 --- a/src/components/post-form/post-form.tsx +++ b/src/components/post-form/post-form.tsx @@ -156,10 +156,10 @@ interface PostFormFieldsProps { isBbcodePreviewing: boolean; postCid: string; subjectRef: React.Ref; - optionsRef: React.RefObject; - flagRef: React.RefObject; - flashTagRef: React.RefObject; - textRef: React.RefObject; + optionsRef: React.RefObject; + flagRef: React.RefObject; + flashTagRef: React.RefObject; + textRef: React.RefObject; urlRef: React.Ref; url: string; lengthError: string | null; diff --git a/src/components/reply-modal/reply-modal.tsx b/src/components/reply-modal/reply-modal.tsx index 1fe64090..2cd6f191 100644 --- a/src/components/reply-modal/reply-modal.tsx +++ b/src/components/reply-modal/reply-modal.tsx @@ -358,9 +358,9 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa } }, [showReplyModal]); - const handleContentInput = (e: React.ChangeEvent) => { - lastSelectionStartRef.current = e.target.selectionStart ?? e.target.value.length; - lastSelectionEndRef.current = e.target.selectionEnd ?? lastSelectionStartRef.current; + const handleContentInput = (e: React.FormEvent) => { + 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 || '') => { diff --git a/src/components/reply-quote-preview/__tests__/reply-quote-preview.test.tsx b/src/components/reply-quote-preview/__tests__/reply-quote-preview.test.tsx index 38312f3b..aabc6569 100644 --- a/src/components/reply-quote-preview/__tests__/reply-quote-preview.test.tsx +++ b/src/components/reply-quote-preview/__tests__/reply-quote-preview.test.tsx @@ -38,19 +38,8 @@ vi.mock('react-router-dom', async () => { const ReactModule = await vi.importActual('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(({ children, to, ...props }, ref) => + ReactModule.createElement('a', { ...props, href: to, ref }, children), ), useLocation: () => ({ pathname: testState.locationPath, diff --git a/src/components/settings-modal/advanced-settings/advanced-settings.tsx b/src/components/settings-modal/advanced-settings/advanced-settings.tsx index 1d3ca81e..20402051 100644 --- a/src/components/settings-modal/advanced-settings/advanced-settings.tsx +++ b/src/components/settings-modal/advanced-settings/advanced-settings.tsx @@ -6,16 +6,16 @@ import { canConfigureBrowserPureP2P, isBrowserPureP2PEnabled } from '../../../li import styles from './advanced-settings.module.css'; interface SettingsProps { - ipfsGatewayUrlsRef?: RefObject; - mediaIpfsGatewayUrlRef?: RefObject; - pubsubProvidersRef?: RefObject; - httpRoutersRef?: RefObject; - ethRpcRef?: RefObject; - p2pRpcRef?: RefObject; - p2pDataPathRef?: RefObject; + ipfsGatewayUrlsRef?: RefObject; + mediaIpfsGatewayUrlRef?: RefObject; + pubsubProvidersRef?: RefObject; + httpRoutersRef?: RefObject; + ethRpcRef?: RefObject; + p2pRpcRef?: RefObject; + p2pDataPathRef?: RefObject; onPureP2PBrowserChange?: (enabled: boolean) => void; pureP2PBrowserEnabled?: boolean; - pureP2PBrowserRef?: RefObject; + pureP2PBrowserRef?: RefObject; } type AccountProtocolOptions = { diff --git a/src/hooks/use-delete-failed-post.ts b/src/hooks/use-delete-failed-post.ts index 901426ec..48342194 100644 --- a/src/hooks/use-delete-failed-post.ts +++ b/src/hooks/use-delete-failed-post.ts @@ -74,7 +74,7 @@ const useDeleteFailedPost = (post?: FailedPost, deleteRedirectPath?: string) => const [isRetryRedirectPending, setIsRetryRedirectPending] = useState(false); const addChallenge = useChallengesStore((state) => state.addChallenge); const navigate = useNavigate(); - const abandonPublishRef = useRef<(() => Promise) | undefined>(); + const abandonPublishRef = useRef<(() => Promise) | undefined>(undefined); const abandonCurrentPublish = useCallback(async () => { await abandonPublishRef.current?.(); }, []); diff --git a/src/hooks/use-publish-post.ts b/src/hooks/use-publish-post.ts index 73dd4330..620f121e 100644 --- a/src/hooks/use-publish-post.ts +++ b/src/hooks/use-publish-post.ts @@ -25,7 +25,7 @@ const usePublishPost = ({ communityAddress }: UsePublishPostOptions) => { const setPublishPostStore = usePublishPostStore((state) => state.setPublishPostStore); const resetPublishPostStore = usePublishPostStore((state) => state.resetPublishPostStore); const addChallenge = useChallengesStore((state) => state.addChallenge); - const abandonPublishRef = useRef<(() => Promise) | undefined>(); + const abandonPublishRef = useRef<(() => Promise) | undefined>(undefined); const [publishPostError, setPublishPostError] = useState(null); const [pendingPublishRequestId, setPendingPublishRequestId] = useState(0); const startedPublishRequestIdRef = useRef(0); diff --git a/src/hooks/use-publish-reply.ts b/src/hooks/use-publish-reply.ts index 132acca3..ea2a12b1 100644 --- a/src/hooks/use-publish-reply.ts +++ b/src/hooks/use-publish-reply.ts @@ -38,7 +38,7 @@ const usePublishReply = ({ cid, communityAddress, postCid }: UsePublishReplyOpti const resetPublishReplyStore = usePublishReplyStore((state) => state.resetPublishReplyStore); const addChallenge = useChallengesStore((state) => state.addChallenge); const { blockedReason } = usePublishAuthorDomainGuard(); - const abandonPublishRef = useRef<(() => Promise) | undefined>(); + const abandonPublishRef = useRef<(() => Promise) | undefined>(undefined); const startedPublishRequestIdRef = useRef(0); const [resolvedExternalQuotedCids, setResolvedExternalQuotedCids] = useState(); const [pendingPublishRequestId, setPendingPublishRequestId] = useState(0); diff --git a/src/views/post/post.tsx b/src/views/post/post.tsx index 185cf3f0..cfb82d11 100644 --- a/src/views/post/post.tsx +++ b/src/views/post/post.tsx @@ -298,7 +298,7 @@ const PostPage = () => { const communityAddress = resolvedCommunityAddress ?? commentCommunityAddress; const communityIdentifier = useCommunityIdentifier(communityAddress); const consumedThreadTopScrollRef = useRef(null); - const previousThreadCidRef = useRef(); + const previousThreadCidRef = useRef(undefined); const lastProcessedUpdateRequestIdRef = useRef(0); const navigate = useNavigate(); diff --git a/yarn.lock b/yarn.lock index db2110df..b8888094 100644 --- a/yarn.lock +++ b/yarn.lock @@ -32,8 +32,8 @@ __metadata: "@types/lodash": "npm:4.17.24" "@types/memoizee": "npm:0.4.9" "@types/node": "npm:20.19.37" - "@types/react": "npm:18.2.25" - "@types/react-dom": "npm:18.2.10" + "@types/react": "npm:19.2.16" + "@types/react-dom": "npm:19.2.3" "@typescript/native-preview": "npm:^7.0.0-dev.20251226.1" "@use-gesture/react": "npm:10.3.1" "@vercel/analytics": "npm:^1.6.1" @@ -74,10 +74,10 @@ __metadata: playwright: "npm:1.56.1" portless: "npm:0.11.1" progress: "npm:2.0.3" - react: "npm:19.1.2" + react: "npm:19.2.7" react-ace: "npm:14.0.1" react-doctor: "npm:0.4.0" - react-dom: "npm:19.1.2" + react-dom: "npm:19.2.7" react-grab: "npm:0.1.37" react-i18next: "npm:16.6.6" react-router-dom: "npm:6.30.2" @@ -6864,19 +6864,12 @@ __metadata: languageName: node linkType: hard -"@types/prop-types@npm:*": - version: 15.7.15 - resolution: "@types/prop-types@npm:15.7.15" - checksum: 10c0/b59aad1ad19bf1733cf524fd4e618196c6c7690f48ee70a327eb450a42aab8e8a063fbe59ca0a5701aebe2d92d582292c0fb845ea57474f6a15f6994b0e260b2 - languageName: node - linkType: hard - -"@types/react-dom@npm:18.2.10": - version: 18.2.10 - resolution: "@types/react-dom@npm:18.2.10" - dependencies: - "@types/react": "npm:*" - checksum: 10c0/ac4056c3b8ba5461a58a7933e1f98d772e877f275a1b4169a936391900aec8f3c8e6125ccbbda530842cd9d108a2be8c994c7f48bfd801f89702e2de9064d834 +"@types/react-dom@npm:19.2.3": + version: 19.2.3 + resolution: "@types/react-dom@npm:19.2.3" + peerDependencies: + "@types/react": ^19.2.0 + checksum: 10c0/b486ebe0f4e2fb35e2e108df1d8fc0927ca5d6002d5771e8a739de11239fe62d0e207c50886185253c99eb9dedfeeb956ea7429e5ba17f6693c7acb4c02f8cd1 languageName: node linkType: hard @@ -6889,23 +6882,12 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*": - version: 19.2.2 - resolution: "@types/react@npm:19.2.2" +"@types/react@npm:19.2.16": + version: 19.2.16 + resolution: "@types/react@npm:19.2.16" dependencies: - csstype: "npm:^3.0.2" - checksum: 10c0/f830b1204aca4634ce3c6cb3477b5d3d066b80a4dd832a4ee0069acb504b6debd2416548a43a11c1407c12bc60e2dc6cf362934a18fe75fe06a69c0a98cba8ab - languageName: node - linkType: hard - -"@types/react@npm:18.2.25": - version: 18.2.25 - resolution: "@types/react@npm:18.2.25" - dependencies: - "@types/prop-types": "npm:*" - "@types/scheduler": "npm:*" - csstype: "npm:^3.0.2" - checksum: 10c0/17559ab8e9a3347b466eb782e21fe45f841e0e39d3f656e4711693cb6d193f948a9913c9e96b64bc4700eb24b34c47f15adbddcc39c596b4b3b79c99619b83bf + csstype: "npm:^3.2.2" + checksum: 10c0/96f2850b67c5aa4a695bc32eb06c7609acdda1c0c597ace21ad24a5b9e2ccbccd97c0643253579fd629789a422a3694b4bbcb26e5fc6aec5f0357820bf0ee81c languageName: node linkType: hard @@ -6925,13 +6907,6 @@ __metadata: languageName: node linkType: hard -"@types/scheduler@npm:*": - version: 0.26.0 - resolution: "@types/scheduler@npm:0.26.0" - checksum: 10c0/84626b06551ab7e1247412a2588430da5cd75263a353f1fd70593ca7331d43797937b89fe587089c6b3613d0658986087c5f0b2debef5bae831cdc1104a432ef - languageName: node - linkType: hard - "@types/slice-ansi@npm:^4.0.0": version: 4.0.0 resolution: "@types/slice-ansi@npm:4.0.0" @@ -9239,10 +9214,10 @@ __metadata: languageName: node linkType: hard -"csstype@npm:^3.0.2": - version: 3.1.3 - resolution: "csstype@npm:3.1.3" - checksum: 10c0/80c089d6f7e0c5b2bd83cf0539ab41474198579584fa10d86d0cafe0642202343cbc119e076a0b1aece191989477081415d66c9fefbf3c957fc2fc4b7009f248 +"csstype@npm:^3.2.2": + version: 3.2.3 + resolution: "csstype@npm:3.2.3" + checksum: 10c0/cd29c51e70fa822f1cecd8641a1445bed7063697469d35633b516e60fe8c1bde04b08f6c5b6022136bb669b64c63d4173af54864510fbb4ee23281801841a3ce languageName: node linkType: hard @@ -17051,14 +17026,14 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:19.1.2": - version: 19.1.2 - resolution: "react-dom@npm:19.1.2" +"react-dom@npm:19.2.7": + version: 19.2.7 + resolution: "react-dom@npm:19.2.7" dependencies: - scheduler: "npm:^0.26.0" + scheduler: "npm:^0.27.0" peerDependencies: - react: ^19.1.2 - checksum: 10c0/67d5f574eecd1f7a89df3c94936122d4df361d3e78ea19d5879c560439ef05f154af9b9281c1f5c92f79d13c39364bfa60136916d00bdbbccd019b8b3471de10 + react: ^19.2.7 + checksum: 10c0/970ff600f6e80d47d39e2f226f12f226173b3cba3382efc97c5f0cd663de9af38c7a4c11c213fb936094faeac83060d660247accaa96b752180d5b951b9cfecb languageName: node linkType: hard @@ -17197,10 +17172,10 @@ __metadata: languageName: node linkType: hard -"react@npm:19.1.2": - version: 19.1.2 - resolution: "react@npm:19.1.2" - checksum: 10c0/dfbe1dee96547ad2d6d6872b6052cbd5bd7841b6f57a7d6cda62b27e1a7df31d47beecb6d11e24abaff0c5ea347459a0a3c60917b41f5a0ee0b507436b3c50a6 +"react@npm:19.2.7": + version: 19.2.7 + resolution: "react@npm:19.2.7" + checksum: 10c0/0bd0e2f1bbd4ba97561c6597bf8a5fec05e6476fe61e165c1065598d16668efc6715205599c94d3ddd49d36cb0f21cbf1b9bcc18ee840b805ce222c3e8d558ac languageName: node linkType: hard @@ -17910,10 +17885,10 @@ __metadata: languageName: node linkType: hard -"scheduler@npm:^0.26.0": - version: 0.26.0 - resolution: "scheduler@npm:0.26.0" - checksum: 10c0/5b8d5bfddaae3513410eda54f2268e98a376a429931921a81b5c3a2873aab7ca4d775a8caac5498f8cbc7d0daeab947cf923dbd8e215d61671f9f4e392d34356 +"scheduler@npm:^0.27.0": + version: 0.27.0 + resolution: "scheduler@npm:0.27.0" + checksum: 10c0/4f03048cb05a3c8fddc45813052251eca00688f413a3cee236d984a161da28db28ba71bd11e7a3dd02f7af84ab28d39fb311431d3b3772fed557945beb00c452 languageName: node linkType: hard