Refactor to use MutableRefObject instead of RefObject

Replaces all instances of React.RefObject with React.MutableRefObject for refs that are mutated directly throughout the codebase. This change clarifies intent and improves type safety for refs that are updated outside of React's ref assignment.
This commit is contained in:
Ludy87 2025-09-26 11:58:03 +02:00
parent 3fa0535246
commit 57b2fcaf2c
No known key found for this signature in database
GPG Key ID: 92696155E0220F94
9 changed files with 30 additions and 30 deletions

View File

@ -23,7 +23,7 @@ interface PageThumbnailProps {
selectionMode: boolean;
movingPage: number | null;
isAnimating: boolean;
pageRefs: React.RefObject<Map<string, HTMLDivElement>>;
pageRefs: React.MutableRefObject<Map<string, HTMLDivElement>>;
onReorderPages: (sourcePageNumber: number, targetIndex: number, selectedPageIds?: string[]) => void;
onTogglePage: (pageId: string) => void;
onAnimateReorder: () => void;

View File

@ -71,7 +71,7 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
[visibleSections]
);
const scrollTo = (ref: React.RefObject<HTMLDivElement | null>) => {
const scrollTo = (ref: React.MutableRefObject<HTMLDivElement | null>) => {
const container = scrollableRef.current;
const target = ref.current;
if (container && target) {

View File

@ -13,7 +13,7 @@ interface FileManagerContextValue {
searchTerm: string;
selectedFiles: StirlingFileStub[];
filteredFiles: StirlingFileStub[];
fileInputRef: React.RefObject<HTMLInputElement | null>;
fileInputRef: React.MutableRefObject<HTMLInputElement | null>;
selectedFilesSet: Set<FileId>;
expandedFileIds: Set<FileId>;
fileGroups: Map<FileId, StirlingFileStub[]>;

View File

@ -165,8 +165,8 @@ interface AddFileOptions {
*/
export async function addFiles(
options: AddFileOptions,
stateRef: React.RefObject<FileContextState>,
filesRef: React.RefObject<Map<FileId, File>>,
stateRef: React.MutableRefObject<FileContextState>,
filesRef: React.MutableRefObject<Map<FileId, File>>,
dispatch: React.Dispatch<FileContextAction>,
lifecycleManager: FileLifecycleManager,
enablePersistence: boolean = false
@ -278,7 +278,7 @@ export async function consumeFiles(
inputFileIds: FileId[],
outputStirlingFiles: StirlingFile[],
outputStirlingFileStubs: StirlingFileStub[],
filesRef: React.RefObject<Map<FileId, File>>,
filesRef: React.MutableRefObject<Map<FileId, File>>,
dispatch: React.Dispatch<FileContextAction>
): Promise<FileId[]> {
if (DEBUG) console.log(`📄 consumeFiles: Processing ${inputFileIds.length} input files, ${outputStirlingFiles.length} output files with pre-created stubs`);
@ -357,7 +357,7 @@ export async function consumeFiles(
async function restoreFilesAndCleanup(
filesToRestore: { file: File; record: StirlingFileStub }[],
fileIdsToRemove: FileId[],
filesRef: React.RefObject<Map<FileId, File>>,
filesRef: React.MutableRefObject<Map<FileId, File>>,
indexedDB?: { deleteFile: (fileId: FileId) => Promise<void> } | null
): Promise<void> {
// Remove files from filesRef
@ -406,7 +406,7 @@ export async function undoConsumeFiles(
inputFiles: File[],
inputStirlingFileStubs: StirlingFileStub[],
outputFileIds: FileId[],
filesRef: React.RefObject<Map<FileId, File>>,
filesRef: React.MutableRefObject<Map<FileId, File>>,
dispatch: React.Dispatch<FileContextAction>,
indexedDB?: { saveFile: (file: File, fileId: FileId, existingThumbnail?: string) => Promise<any>; deleteFile: (fileId: FileId) => Promise<void> } | null
): Promise<void> {
@ -468,8 +468,8 @@ export async function undoConsumeFiles(
export async function addStirlingFileStubs(
stirlingFileStubs: StirlingFileStub[],
options: { insertAfterPageId?: string; selectFiles?: boolean } = {},
stateRef: React.RefObject<FileContextState>,
filesRef: React.RefObject<Map<FileId, File>>,
stateRef: React.MutableRefObject<FileContextState>,
filesRef: React.MutableRefObject<Map<FileId, File>>,
dispatch: React.Dispatch<FileContextAction>,
_lifecycleManager: FileLifecycleManager
): Promise<StirlingFile[]> {

View File

@ -15,8 +15,8 @@ import {
* Create stable selectors using stateRef and filesRef
*/
export function createFileSelectors(
stateRef: React.RefObject<FileContextState>,
filesRef: React.RefObject<Map<FileId, File>>
stateRef: React.MutableRefObject<FileContextState>,
filesRef: React.MutableRefObject<Map<FileId, File>>
): FileContextSelectors {
return {
getFile: (id: FileId) => {
@ -125,8 +125,8 @@ export function buildQuickKeySetFromMetadata(metadata: { name: string; size: num
* Get primary file (first in list) - commonly used pattern
*/
export function getPrimaryFile(
stateRef: React.RefObject<FileContextState>,
filesRef: React.RefObject<Map<FileId, File>>
stateRef: React.MutableRefObject<FileContextState>,
filesRef: React.MutableRefObject<Map<FileId, File>>
): { file?: File; record?: StirlingFileStub } {
const primaryFileId = stateRef.current.files.ids[0];
if (!primaryFileId) return {};

View File

@ -16,7 +16,7 @@ export class FileLifecycleManager {
private fileGenerations = new Map<string, number>(); // Generation tokens to prevent stale cleanup
constructor(
private filesRef: React.RefObject<Map<FileId, File>>,
private filesRef: React.MutableRefObject<Map<FileId, File>>,
private dispatch: React.Dispatch<FileContextAction>
) {}
@ -34,7 +34,7 @@ export class FileLifecycleManager {
/**
* Clean up resources for a specific file (with stateRef access for complete cleanup)
*/
cleanupFile = (fileId: FileId, stateRef?: React.RefObject<any>): void => {
cleanupFile = (fileId: FileId, stateRef?: React.MutableRefObject<any>): void => {
// Use comprehensive cleanup (same as removeFiles)
this.cleanupAllResourcesForFile(fileId, stateRef);
@ -68,7 +68,7 @@ export class FileLifecycleManager {
/**
* Schedule delayed cleanup for a file with generation token to prevent stale cleanup
*/
scheduleCleanup = (fileId: FileId, delay: number = 30000, stateRef?: React.RefObject<any>): void => {
scheduleCleanup = (fileId: FileId, delay: number = 30000, stateRef?: React.MutableRefObject<any>): void => {
// Cancel existing timer
const existingTimer = this.cleanupTimers.get(fileId);
if (existingTimer) {
@ -101,7 +101,7 @@ export class FileLifecycleManager {
/**
* Remove a file immediately with complete resource cleanup
*/
removeFiles = (fileIds: FileId[], stateRef?: React.RefObject<any>): void => {
removeFiles = (fileIds: FileId[], stateRef?: React.MutableRefObject<any>): void => {
fileIds.forEach(fileId => {
// Clean up all resources for this file
this.cleanupAllResourcesForFile(fileId, stateRef);
@ -114,7 +114,7 @@ export class FileLifecycleManager {
/**
* Complete resource cleanup for a single file
*/
private cleanupAllResourcesForFile = (fileId: FileId, stateRef?: React.RefObject<any>): void => {
private cleanupAllResourcesForFile = (fileId: FileId, stateRef?: React.MutableRefObject<any>): void => {
// Remove from files ref
this.filesRef.current.delete(fileId);
@ -166,7 +166,7 @@ export class FileLifecycleManager {
/**
* Update file record with race condition guards
*/
updateStirlingFileStub = (fileId: FileId, updates: Partial<StirlingFileStub>, stateRef?: React.RefObject<any>): void => {
updateStirlingFileStub = (fileId: FileId, updates: Partial<StirlingFileStub>, stateRef?: React.MutableRefObject<any>): void => {
// Guard against updating removed files (race condition protection)
if (!this.filesRef.current.has(fileId)) {
if (DEBUG) console.warn(`🗂️ Attempted to update removed file (filesRef): ${fileId}`);

View File

@ -32,7 +32,7 @@ import * as React from 'react';
*/
export const useIsOverflowing = (ref: React.RefObject<HTMLElement | null>, callback?: (isOverflow: boolean) => void) => {
export const useIsOverflowing = (ref: React.MutableRefObject<HTMLElement | null>, callback?: (isOverflow: boolean) => void) => {
// State to track overflow status
const [isOverflow, setIsOverflow] = React.useState<boolean | undefined>(undefined);

View File

@ -60,8 +60,8 @@ export function useTooltipPosition({
sidebarTooltip: boolean;
position: Position;
gap: number;
triggerRef: React.RefObject<HTMLElement | null>;
tooltipRef: React.RefObject<HTMLDivElement | null>;
triggerRef: React.MutableRefObject<HTMLElement | null>;
tooltipRef: React.MutableRefObject<HTMLDivElement | null>;
sidebarRefs?: SidebarRefs;
sidebarState?: SidebarState;
}): PositionState {

View File

@ -5,8 +5,8 @@ export interface SidebarState {
}
export interface SidebarRefs {
quickAccessRef: React.RefObject<HTMLDivElement | null>;
toolPanelRef: React.RefObject<HTMLDivElement | null>;
quickAccessRef: React.MutableRefObject<HTMLDivElement | null>;
toolPanelRef: React.MutableRefObject<HTMLDivElement | null>;
}
export interface SidebarInfo {