Fix oxlint react next-line suppressions - #1711
Conversation
Remove all oxlint-disable-next-line suppressions for react rules by fixing the underlying React Compiler and hooks violations across plugins (effects that sync state, refs during render, nested components, memoization mismatches, and exhaustive-deps). Notable: workflow DocumentList no longer uses TanStack Virtual, which is incompatible with React Compiler memoization.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
There was a problem hiding this comment.
Pull request overview
This PR removes oxlint-disable-next-line suppressions for React Compiler and hooks rules across multiple plugins by restructuring components/hooks to avoid effect-driven state sync, ref reads during render, nested component definitions, and unstable hook dependencies—keeping the codebase React Compiler–safe without per-line escapes.
Changes:
- Refactors multiple hooks/components to avoid React Compiler violations (e.g., deferring state updates, deriving UI state, stabilizing callbacks/deps, and removing nested component definitions used as props).
- Updates
sanity-plugin-workflow’sDocumentListto render cards directly (dropping@tanstack/react-virtual) to avoid compiler incompatibilities. - Adds per-package patch changesets for all affected published plugins.
Reviewed changes
Copilot reviewed 55 out of 56 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Updates lockfile for dependency graph changes (incl. removal of @tanstack/react-virtual from workflow). |
| plugins/sanity-plugin-workflow/src/hooks/useWorkflowDocuments.tsx | Reworks local state syncing to avoid effect-driven setState. |
| plugins/sanity-plugin-workflow/src/components/DocumentList.tsx | Removes virtualization and renders filtered document cards directly. |
| plugins/sanity-plugin-workflow/package.json | Drops @tanstack/react-virtual dependency. |
| plugins/sanity-plugin-utils/src/hooks/useListeningQuery.tsx | Fixes RxJS error handling and avoids synchronous state updates in effect error paths. |
| plugins/sanity-plugin-mux-input/src/hooks/useMuxAssets.ts | Introduces ref-backed state access and fixes effect deps for asset pagination fetching. |
| plugins/sanity-plugin-mux-input/src/hooks/useMediaMetadata.ts | Avoids synchronous state updates and adds cancellation guard for async metadata work. |
| plugins/sanity-plugin-mux-input/src/hooks/useFetchFileSize.ts | Refactors URL file size fetching into a keyed state machine to avoid effect-driven sync issues. |
| plugins/sanity-plugin-mux-input/src/components/VideoThumbnail.tsx | Makes suspense/error handling memo-safe by returning structured results instead of calling setters during render. |
| plugins/sanity-plugin-mux-input/src/components/VideoPlayer.tsx | Refactors playback/poster/src/token derivation to avoid render-time state updates; snapshots current time via events. |
| plugins/sanity-plugin-mux-input/src/components/VideoDetails/DeleteDialog.tsx | Derives dialog state instead of syncing via effect-driven setState. |
| plugins/sanity-plugin-mux-input/src/components/Uploader.tsx | Stabilizes cancel observable creation and restructures upload starter logic to satisfy hooks/compiler rules. |
| plugins/sanity-plugin-mux-input/src/components/UploadConfiguration.tsx | Moves validation to pure derivation and avoids ref reads during render by seeding immutable defaults via lazy state. |
| plugins/sanity-plugin-mux-input/src/components/TextTracksManager.tsx | Replaces effect-setState merge logic with memo-derived track ID set. |
| plugins/sanity-plugin-mux-input/src/components/PlayerActionsMenu.tsx | Closes menu via derived render logic instead of effect-driven syncing. |
| plugins/sanity-plugin-mux-input/src/components/Player.tsx | Replaces memos with render-time derivation to avoid compiler issues and ref reads. |
| plugins/sanity-plugin-mux-input/src/components/EditCaptionDialog.tsx | Replaces effect-driven prop sync with guarded render-time updates and extracted helper. |
| plugins/sanity-plugin-mux-input/src/components/DraggableWatermark.tsx | Extracts video content-box measurement, adds observer-driven sizing, and avoids render-time ref reads. |
| plugins/sanity-plugin-media/src/contexts/ToolOptionsContext.tsx | Adjusts memoization strategy for tool options context to satisfy React Compiler. |
| plugins/sanity-plugin-media/src/components/DialogTagEdit/index.tsx | Inlines dialog footer JSX to avoid nested component definitions. |
| plugins/sanity-plugin-media/src/components/DialogTagCreate/index.tsx | Inlines dialog footer JSX to avoid nested component definitions. |
| plugins/sanity-plugin-media/src/components/DialogConfirm/index.tsx | Inlines dialog header/footer JSX to avoid nested component definitions. |
| plugins/sanity-plugin-media/src/components/DialogAssetEdit/index.tsx | Inlines dialog footer JSX to avoid nested component definitions. |
| plugins/sanity-plugin-hotspot-array/src/useResizeObserver.ts | Fixes exhaustive-deps by updating effect dependencies. |
| plugins/sanity-plugin-hotspot-array/src/useDebouncedCallback.ts | Adjusts internal debounced-callback implementation to satisfy hooks rules. |
| plugins/sanity-plugin-hotspot-array/src/ImageHotspotArray.tsx | Updates resize observer wiring to use the revised debounced callback usage. |
| plugins/@sanity/studio-secrets/src/Settings.tsx | Replaces effect-based secrets sync with guarded render-time sync. |
| plugins/@sanity/personalization-plugin/src/launchDarkly/components/Secrets.tsx | Derives settings visibility and simplifies secret propagation. |
| plugins/@sanity/personalization-plugin/src/growthbook/Components/Secrets.tsx | Derives settings visibility and simplifies secret propagation. |
| plugins/@sanity/embeddings-index-ui/src/referenceInput/SemanticSearchReferenceInput.tsx | Prevents ref reads during render by seeding one-time focus behavior via ref. |
| plugins/@sanity/embeddings-index-ui/src/embeddingsIndexDashboard/IndexEditor.tsx | Reworks selected/default index syncing and fixes list keying. |
| plugins/@sanity/embeddings-index-ui/src/embeddingsIndexDashboard/EmbeddingsIndexTool.tsx | Simplifies selection state and defers initial fetch to avoid compiler violations. |
| plugins/@sanity/cross-dataset-duplicator/src/components/Duplicator.tsx | Reworks payload initialization/status fetching to avoid EffectSetState patterns. |
| plugins/@sanity/color-input/src/react-color/EditableInput.tsx | Moves controlled-value syncing to getDerivedStateFromProps + explicit focus tracking. |
| plugins/@sanity/block-insert-picker/src/blockInsertPicker.tsx | Derives cursor rect without effect-driven state syncing. |
| plugins/@sanity/block-insert-picker/src/blockInsertPicker.test.tsx | Adds a provider wrapper to avoid constructed context values and satisfy lint. |
| plugins/@sanity/assist/src/fieldActions/customFieldActions.tsx | Stabilizes config derivation and hook deps for custom field actions. |
| plugins/@sanity/assist/src/components/ImageContext.tsx | Replaces state-based assetRef syncing with ref tracking to avoid compiler issues. |
| plugins/@sanity/assist/src/assistLayout/RunInstructionProvider.tsx | Fixes context memo deps (includes getUserInput) without suppressions. |
| plugins/@sanity/assist/src/assistInspector/AssistInspector.tsx | Avoids ref reads during render by mirroring boundary element via callback ref. |
| plugins/@sanity/assist/src/assistDocument/RequestRunInstructionProvider.tsx | Refactors queued task handling using refs/microtasks to avoid effect setState patterns. |
| plugins/@sanity/assist/src/assistDocument/hooks/useAssistDocumentContextValue.tsx | Restructures synthetic task state to reset per document without effect syncing. |
| plugins/@sanity/assist/src/assistDocument/components/instruction/PromptInput.tsx | Fixes exhaustive-deps by making the “inline blocks” normalization track value/onChange. |
| plugins/@sanity/assist/src/assistConnectors/AssistConnectorsOverlay.tsx | Defers initial redraw via requestAnimationFrame with cleanup. |
| .changeset/workflow-react-compiler.md | Patch changeset for workflow compiler-related fixes (incl. virtualization removal). |
| .changeset/utils-react-compiler.md | Patch changeset for utils useListeningQuery error handling change. |
| .changeset/studio-secrets-react-compiler.md | Patch changeset for studio-secrets state sync refactor. |
| .changeset/personalization-plugin-react-compiler.md | Patch changeset for personalization derived settings behavior. |
| .changeset/mux-input-react-compiler.md | Patch changeset for mux-input compiler/hook fixes. |
| .changeset/media-react-compiler.md | Patch changeset for media dialog/footer inlining + context memoization changes. |
| .changeset/hotspot-array-react-compiler.md | Patch changeset for hotspot-array hook dependency fixes. |
| .changeset/embeddings-index-ui-react-compiler.md | Patch changeset for embeddings-index-ui compiler/hook fixes. |
| .changeset/cross-dataset-duplicator-react-compiler.md | Patch changeset for duplicator EffectSetState fix. |
| .changeset/color-input-react-compiler.md | Patch changeset for color-input controlled state refactor. |
| .changeset/block-insert-picker-react-compiler.md | Patch changeset for block-insert-picker compiler/hook fixes. |
| .changeset/assist-react-compiler.md | Patch changeset for assist compiler/hook fixes. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| options?.directUploads, | ||
| options?.locales, | ||
| ]) | ||
| }, [options]) |
| onTimeUpdate={(event) => { | ||
| const target = event.currentTarget as EventTarget & {currentTime?: number} | ||
| if (typeof target.currentTime === 'number') setCurrentTime(target.currentTime) | ||
| }} |
🦋 Changeset detectedLatest commit: ef4d310 The changes in this PR will be included in the next version bump. This PR includes changesets to release 19 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Prefer startTransition over requestAnimationFrame for the non-urgent initial redraw force in AssistConnectorsOverlay.
Track whether PromptInput has already rewritten legacy blocks so onChange runs once on load instead of on every value change.
| catchError((err) => { | ||
| console.error(err) | ||
| setError(err) | ||
| setLoading(false) | ||
| setData(null) | ||
| queueMicrotask(() => { | ||
| setError(err) | ||
| setLoading(false) | ||
| setData(null) | ||
| }) | ||
|
|
||
| return err | ||
| return EMPTY |
| const fetchFileSize = async () => { | ||
| setUrlState((prev) => | ||
| prev.url === url | ||
| ? { | ||
| url, | ||
| fileSize: null, | ||
| isLoadingFileSize: true, | ||
| canSkipFileSizeValidation: false, | ||
| } | ||
| : prev, | ||
| ) |
Merge the inspector boundary element state into the container ref with useImperativeHandle instead of a decorating callback ref.
Pin the imperative handle generics so HTMLDivElement | null is accepted.
Capture the caret rect in useLayoutEffect again, wrapping setCursorRect in startTransition to satisfy React Compiler EffectSetState.
Leave EditableInput alone — class-component refactor is handled in another PR.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 53 out of 54 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (1)
plugins/sanity-plugin-mux-input/src/components/VideoPlayer.tsx:193
onTimeUpdatefires frequently during playback; updating React state on every event will cause the entireVideoPlayersubtree to re-render continuously. SincecurrentTimeis only used for the “edit thumbnail” dialog, consider only recording time updates while that dialog is open (or otherwise avoid per-frame state updates).
| // oxlint-disable-next-line no-unsafe-type-assertion | ||
| const data = _data as SanityDocumentWithMetadata[] | ||
|
|
||
| const [localDocuments, setLocalDocuments] = useState<SanityDocumentWithMetadata[]>([]) | ||
| const [localDocuments, setLocalDocuments] = useState<SanityDocumentWithMetadata[]>(() => data) | ||
| const [previousData, setPreviousData] = useState(data) | ||
|
|
||
| useEffect(() => { | ||
| if (data) { | ||
| // Fix this later | ||
| // oxlint-disable-next-line react/react-compiler | ||
| setLocalDocuments(data) | ||
| } | ||
| }, [data]) | ||
| if (data !== previousData) { | ||
| setPreviousData(data) | ||
| setLocalDocuments(data) | ||
| } |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ef4d310. Configure here.
| } | ||
| const blockRect = editor.dom.getStartBlockElement(snapshot)?.getBoundingClientRect() | ||
| if (blockRect) setCursorRect(blockRect) | ||
| if (blockRect) startTransition(() => setCursorRect(blockRect)) |
There was a problem hiding this comment.
Deferred popover anchor update
Medium Severity
setCursorRect in useLayoutEffect is wrapped in startTransition, so the anchor update can be deferred past paint. cursorElement returns null until cursorRect is set, so the insert picker can miss its first paint and open late or with a visible flicker.
Reviewed by Cursor Bugbot for commit ef4d310. Configure here.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 53 out of 54 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (1)
plugins/sanity-plugin-mux-input/src/components/VideoPlayer.tsx:193
onTimeUpdateupdates React state on every timeupdate event, which can trigger frequent re-renders during playback and degrade performance even when the edit-thumbnail dialog isn't open. Consider only trackingcurrentTimewhile the thumbnail dialog is open (or otherwise throttling/storing it in a ref) so normal playback doesn't cause continuous re-renders.


What
Remove all
oxlint-disable-next-linesuppressions for React rules by fixing the underlying violations (mostly React Compiler + hooks).Why
These suppressions were papering over real React Compiler / hooks issues (
EffectSetState, refs during render, nested components during render,PreserveManualMemo, exhaustive-deps, etc.). Fixing them keeps plugins compiler-safe without per-line escapes.Approach
setStateinto async callbacks.currentreads in renderDocumentList: drop@tanstack/react-virtual(incompatible with React Compiler memoization); render filtered cards directly. Kanban columns typically stay small enough that this is acceptable; can revisit if TanStack Virtual becomes compiler-compatible.Packages
Separate patch changesets for:
@sanity/assist,@sanity/block-insert-picker,@sanity/color-input,@sanity/cross-dataset-duplicator,@sanity/embeddings-index-ui,@sanity/personalization-plugin,@sanity/studio-secrets,sanity-plugin-hotspot-array,sanity-plugin-media,sanity-plugin-mux-input,sanity-plugin-utils,sanity-plugin-workflowVerification
pnpm formatpnpm lintpnpm knippnpm buildpnpm test run— 184 files / 1097 testsNote: one block-level
oxlint-disable react/react-compilerremains insanity-plugin-hotspot-array'suseDebouncedCallback(not a next-line suppression).