1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-02-01 19:15:01 +02:00

Chore: Desktop: Fix eslint issues and strengthen types in NoteEditor.tsx (#10449)

This commit is contained in:
Henry Heino 2024-05-20 17:28:19 -07:00 committed by GitHub
parent c632ea5c48
commit 652add9af2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 162 additions and 118 deletions

View File

@ -322,6 +322,8 @@ packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.js
packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.js
packages/app-desktop/gui/NoteEditor/utils/useNoteSearchBar.js
packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
packages/app-desktop/gui/NoteEditor/utils/useScheduleSaveCallbacks.js
packages/app-desktop/gui/NoteEditor/utils/useScrollWhenReadyOptions.js
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
packages/app-desktop/gui/NoteEditor/utils/useWindowCommandHandler.js
packages/app-desktop/gui/NoteList/NoteList2.js

2
.gitignore vendored
View File

@ -301,6 +301,8 @@ packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.js
packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.js
packages/app-desktop/gui/NoteEditor/utils/useNoteSearchBar.js
packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
packages/app-desktop/gui/NoteEditor/utils/useScheduleSaveCallbacks.js
packages/app-desktop/gui/NoteEditor/utils/useScrollWhenReadyOptions.js
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
packages/app-desktop/gui/NoteEditor/utils/useWindowCommandHandler.js
packages/app-desktop/gui/NoteList/NoteList2.js

View File

@ -26,6 +26,10 @@ export interface AppStateDialog {
props: Record<string, any>;
}
export interface EditorScrollPercents {
[noteId: string]: number;
}
export interface AppState extends State {
route: AppStateRoute;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@ -34,8 +38,7 @@ export interface AppState extends State {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
windowContentSize: any;
watchedNoteFiles: string[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
lastEditorScrollPercents: any;
lastEditorScrollPercents: EditorScrollPercents;
devToolsVisible: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
visibleDialogs: any; // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.

View File

@ -3,19 +3,18 @@ import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import TinyMCE from './NoteBody/TinyMCE/TinyMCE';
import { connect } from 'react-redux';
import MultiNoteActions from '../MultiNoteActions';
import { htmlToMarkdown, formNoteToNote } from './utils';
import { htmlToMarkdown } from './utils';
import useSearchMarkers from './utils/useSearchMarkers';
import useNoteSearchBar from './utils/useNoteSearchBar';
import useMessageHandler from './utils/useMessageHandler';
import useWindowCommandHandler from './utils/useWindowCommandHandler';
import useDropHandler from './utils/useDropHandler';
import useMarkupToHtml from './utils/useMarkupToHtml';
import useFormNote, { OnLoadEvent } from './utils/useFormNote';
import useFormNote, { OnLoadEvent, SetFormNote } from './utils/useFormNote';
import useEffectiveNoteId from './utils/useEffectiveNoteId';
import useFolder from './utils/useFolder';
import styles_ from './styles';
import { NoteEditorProps, FormNote, ScrollOptions, ScrollOptionTypes, OnChangeEvent, NoteBodyEditorProps, AllAssetsOptions, NoteBodyEditorRef } from './utils/types';
import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher/index';
import { NoteEditorProps, FormNote, OnChangeEvent, NoteBodyEditorProps, AllAssetsOptions, NoteBodyEditorRef } from './utils/types';
import CommandService from '@joplin/lib/services/CommandService';
import ToolbarButton from '../ToolbarButton/ToolbarButton';
import Button, { ButtonLevel } from '../Button/Button';
@ -26,7 +25,6 @@ import { _, _n } from '@joplin/lib/locale';
import TagList from '../TagList';
import NoteTitleBar from './NoteTitle/NoteTitleBar';
import markupLanguageUtils from '../../utils/markupLanguageUtils';
import usePrevious from '../hooks/usePrevious';
import Setting from '@joplin/lib/models/Setting';
import stateToWhenClauseContext from '../../services/commands/stateToWhenClauseContext';
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
@ -37,7 +35,7 @@ import NoteSearchBar from '../NoteSearchBar';
import { reg } from '@joplin/lib/registry';
import Note from '@joplin/lib/models/Note';
import Folder from '@joplin/lib/models/Folder';
const bridge = require('@electron/remote').require('./bridge').default;
import bridge from '../../services/bridge';
import NoteRevisionViewer from '../NoteRevisionViewer';
import { parseShareCache } from '@joplin/lib/services/share/reducer';
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
@ -51,6 +49,8 @@ import CodeMirror5 from './NoteBody/CodeMirror/v5/CodeMirror';
import { openItemById } from './utils/contextMenu';
import getPluginSettingValue from '@joplin/lib/services/plugins/utils/getPluginSettingValue';
import { MarkupLanguage } from '@joplin/renderer';
import useScrollWhenReadyOptions from './utils/useScrollWhenReadyOptions';
import useScheduleSaveCallbacks from './utils/useScheduleSaveCallbacks';
const commands = [
require('./commands/showRevisions'),
@ -61,20 +61,21 @@ const toolbarButtonUtils = new ToolbarButtonUtils(CommandService.instance());
function NoteEditor(props: NoteEditorProps) {
const [showRevisions, setShowRevisions] = useState(false);
const [titleHasBeenManuallyChanged, setTitleHasBeenManuallyChanged] = useState(false);
const [scrollWhenReady, setScrollWhenReady] = useState<ScrollOptions>(null);
const [isReadOnly, setIsReadOnly] = useState<boolean>(false);
const editorRef = useRef<NoteBodyEditorRef>();
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const titleInputRef = useRef<any>();
const titleInputRef = useRef<HTMLInputElement>();
const isMountedRef = useRef(true);
const noteSearchBarRef = useRef(null);
const setFormNoteRef = useRef<SetFormNote>();
const { saveNoteIfWillChange, scheduleSaveNote } = useScheduleSaveCallbacks({
setFormNote: setFormNoteRef, dispatch: props.dispatch, editorRef,
});
const formNote_beforeLoad = useCallback(async (event: OnLoadEvent) => {
await saveNoteIfWillChange(event.formNote);
setShowRevisions(false);
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, []);
}, [saveNoteIfWillChange]);
const formNote_afterLoad = useCallback(async () => {
setTitleHasBeenManuallyChanged(false);
@ -92,6 +93,7 @@ function NoteEditor(props: NoteEditorProps) {
onBeforeLoad: formNote_beforeLoad,
onAfterLoad: formNote_afterLoad,
});
setFormNoteRef.current = setFormNote;
const formNoteRef = useRef<FormNote>();
formNoteRef.current = { ...formNote };
@ -116,53 +118,6 @@ function NoteEditor(props: NoteEditorProps) {
const styles = styles_(props);
function scheduleSaveNote(formNote: FormNote) {
if (!formNote.saveActionQueue) throw new Error('saveActionQueue is not set!!'); // Sanity check
// reg.logger().debug('Scheduling...', formNote);
const makeAction = (formNote: FormNote) => {
return async function() {
const note = await formNoteToNote(formNote);
reg.logger().debug('Saving note...', note);
const savedNote = await Note.save(note);
setFormNote((prev: FormNote) => {
return { ...prev, user_updated_time: savedNote.user_updated_time, hasChanged: false };
});
void ExternalEditWatcher.instance().updateNoteFile(savedNote);
props.dispatch({
type: 'EDITOR_NOTE_STATUS_REMOVE',
id: formNote.id,
});
eventManager.emit(EventName.NoteContentChange, { note: savedNote });
};
};
formNote.saveActionQueue.push(makeAction(formNote));
}
async function saveNoteIfWillChange(formNote: FormNote) {
if (!formNote.id || !formNote.bodyWillChangeId) return;
const body = await editorRef.current.content();
scheduleSaveNote({
...formNote,
body: body,
bodyWillChangeId: 0,
bodyChangeId: 0,
});
}
async function saveNoteAndWait(formNote: FormNote) {
await saveNoteIfWillChange(formNote);
return formNote.saveActionQueue.waitForAllDone();
}
const whiteBackgroundNoteRendering = formNote.markup_language === MarkupLanguage.Html;
const markupToHtml = useMarkupToHtml({
@ -201,26 +156,8 @@ function NoteEditor(props: NoteEditorProps) {
id: formNote.id,
});
}
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [props.isProvisional, formNote.id]);
}, [props.isProvisional, formNote.id, props.dispatch]);
const previousNoteId = usePrevious(formNote.id);
useEffect(() => {
if (formNote.id === previousNoteId) return;
if (editorRef.current) {
editorRef.current.resetScroll();
}
setScrollWhenReady({
type: props.selectedNoteHash ? ScrollOptionTypes.Hash : ScrollOptionTypes.Percent,
value: props.selectedNoteHash ? props.selectedNoteHash : props.lastEditorScrollPercents[formNote.id] || 0,
});
void ResourceEditWatcher.instance().stopWatchingAll();
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [formNote.id, previousNoteId]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const onFieldChange = useCallback((field: string, value: any, changeId = 0) => {
@ -263,19 +200,16 @@ function NoteEditor(props: NoteEditorProps) {
// The previously loaded note, that was modified, will be saved via saveNoteIfWillChange()
} else {
setFormNote(newNote);
scheduleSaveNote(newNote);
void scheduleSaveNote(newNote);
}
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [handleProvisionalFlag, formNote, isNewNote, titleHasBeenManuallyChanged]);
}, [handleProvisionalFlag, formNote, setFormNote, isNewNote, titleHasBeenManuallyChanged, scheduleSaveNote]);
useWindowCommandHandler({
dispatch: props.dispatch,
formNote,
setShowLocalSearch,
noteSearchBarRef,
editorRef,
titleInputRef,
saveNoteAndWait,
setFormNote,
});
@ -339,10 +273,15 @@ function NoteEditor(props: NoteEditorProps) {
id: formNote.id,
status: 'saving',
});
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [formNote, handleProvisionalFlag]);
}, [formNote, setFormNote, handleProvisionalFlag, props.dispatch]);
const onMessage = useMessageHandler(scrollWhenReady, setScrollWhenReady, editorRef, setLocalSearchResultCount, props.dispatch, formNote, htmlToMarkdown, markupToHtml);
const { scrollWhenReady, clearScrollWhenReady } = useScrollWhenReadyOptions({
noteId: formNote.id,
selectedNoteHash: props.selectedNoteHash,
lastEditorScrollPercents: props.lastEditorScrollPercents,
editorRef,
});
const onMessage = useMessageHandler(scrollWhenReady, clearScrollWhenReady, editorRef, setLocalSearchResultCount, props.dispatch, formNote, htmlToMarkdown, markupToHtml);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const externalEditWatcher_noteChange = useCallback((event: any) => {
@ -355,8 +294,7 @@ function NoteEditor(props: NoteEditorProps) {
setFormNote(newFormNote);
}
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [formNote]);
}, [formNote, setFormNote]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const onNotePropertyChange = useCallback((event: any) => {
@ -373,8 +311,7 @@ function NoteEditor(props: NoteEditorProps) {
return newFormNote;
});
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, []);
}, [setFormNote]);
useEffect(() => {
eventManager.on(EventName.AlarmChange, onNotePropertyChange);
@ -409,8 +346,7 @@ function NoteEditor(props: NoteEditorProps) {
});
}, [props.dispatch]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
function renderNoNotes(rootStyle: any) {
function renderNoNotes(rootStyle: React.CSSProperties) {
const emptyDivStyle = {
backgroundColor: 'black',
opacity: 0.1,
@ -493,7 +429,7 @@ function NoteEditor(props: NoteEditorProps) {
}
const onRichTextReadMoreLinkClick = useCallback(() => {
bridge().openExternal('https://joplinapp.org/help/apps/rich_text_editor');
void bridge().openExternal('https://joplinapp.org/help/apps/rich_text_editor');
}, []);
const onRichTextDismissLinkClick = useCallback(() => {
@ -521,8 +457,7 @@ function NoteEditor(props: NoteEditorProps) {
if (showRevisions) {
const theme = themeStyle(props.themeId);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const revStyle: any = {
const revStyle: React.CSSProperties = {
// ...props.style,
display: 'inline-flex',
padding: theme.margin,

View File

@ -36,8 +36,7 @@ interface Props {
noteTitle: string;
noteIsTodo: number;
isProvisional: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
titleInputRef: any;
titleInputRef: React.RefObject<HTMLInputElement>;
onTitleChange(event: ChangeEvent<HTMLInputElement>): void;
disabled: boolean;
}

View File

@ -1,4 +1,4 @@
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect, useCallback, RefObject } from 'react';
import { FormNote, defaultFormNote, ResourceInfos } from './types';
import { clearResourceCache, attachedResources } from './resourceHandling';
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
@ -25,14 +25,15 @@ export interface HookDependencies {
decryptionStarted: boolean;
noteId: string;
isProvisional: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
titleInputRef: any;
titleInputRef: RefObject<HTMLInputElement>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
editorRef: any;
onBeforeLoad(event: OnLoadEvent): void;
onAfterLoad(event: OnLoadEvent): void;
}
export type SetFormNote = ReturnType<typeof useState<FormNote>>[1];
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
function installResourceChangeHandler(onResourceChangeHandler: Function) {
ResourceFetcher.instance().on('downloadComplete', onResourceChangeHandler);

View File

@ -25,7 +25,6 @@ export default function useMarkupToHtml(deps: HookDependencies) {
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
customCss: customCss || '',
});
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [plugins, customCss]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@ -63,6 +62,5 @@ export default function useMarkupToHtml(deps: HookDependencies) {
});
return result;
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [themeId, customCss, markupToHtml, whiteBackgroundNoteRendering]);
}, [themeId, markupToHtml, whiteBackgroundNoteRendering, deps.settingValue]);
}

View File

@ -1,5 +1,5 @@
import { useCallback } from 'react';
import { FormNote, HtmlToMarkdownHandler, MarkupToHtmlHandler } from './types';
import { FormNote, HtmlToMarkdownHandler, MarkupToHtmlHandler, ScrollOptions } from './types';
import contextMenu from './contextMenu';
import CommandService from '@joplin/lib/services/CommandService';
import PostMessageService from '@joplin/lib/services/PostMessageService';
@ -8,7 +8,7 @@ import { reg } from '@joplin/lib/registry';
const bridge = require('@electron/remote').require('./bridge').default;
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any -- Old code before rule was applied, Old code before rule was applied
export default function useMessageHandler(scrollWhenReady: any, setScrollWhenReady: Function, editorRef: any, setLocalSearchResultCount: Function, dispatch: Function, formNote: FormNote, htmlToMd: HtmlToMarkdownHandler, mdToHtml: MarkupToHtmlHandler) {
export default function useMessageHandler(scrollWhenReady: ScrollOptions|null, clearScrollWhenReady: ()=> void, editorRef: any, setLocalSearchResultCount: Function, dispatch: Function, formNote: FormNote, htmlToMd: HtmlToMarkdownHandler, mdToHtml: MarkupToHtmlHandler) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
return useCallback(async (event: any) => {
const msg = event.channel ? event.channel : '';
@ -25,7 +25,7 @@ export default function useMessageHandler(scrollWhenReady: any, setScrollWhenRea
} else if (msg === 'noteRenderComplete') {
if (scrollWhenReady) {
const options = { ...scrollWhenReady };
setScrollWhenReady(null);
clearScrollWhenReady();
editorRef.current.scrollTo(options);
}
} else if (msg === 'setMarkerCount') {

View File

@ -0,0 +1,65 @@
import Logger from '@joplin/utils/Logger';
import { RefObject, useCallback } from 'react';
import { FormNote, NoteBodyEditorRef } from './types';
import { formNoteToNote } from '.';
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
import Note from '@joplin/lib/models/Note';
import type { Dispatch } from 'redux';
import eventManager, { EventName } from '@joplin/lib/eventManager';
import type { SetFormNote } from './useFormNote';
const logger = Logger.create('useScheduleSaveCallbacks');
interface Props {
setFormNote: RefObject<SetFormNote>;
dispatch: Dispatch;
editorRef: RefObject<NoteBodyEditorRef>;
}
const useScheduleSaveCallbacks = (props: Props) => {
const scheduleSaveNote = useCallback(async (formNote: FormNote) => {
if (!formNote.saveActionQueue) throw new Error('saveActionQueue is not set!!'); // Sanity check
// reg.logger().debug('Scheduling...', formNote);
const makeAction = (formNote: FormNote) => {
return async function() {
const note = await formNoteToNote(formNote);
logger.debug('Saving note...', note);
const savedNote = await Note.save(note);
props.setFormNote.current((prev: FormNote) => {
return { ...prev, user_updated_time: savedNote.user_updated_time, hasChanged: false };
});
void ExternalEditWatcher.instance().updateNoteFile(savedNote);
props.dispatch({
type: 'EDITOR_NOTE_STATUS_REMOVE',
id: formNote.id,
});
eventManager.emit(EventName.NoteContentChange, { note: savedNote });
};
};
formNote.saveActionQueue.push(makeAction(formNote));
}, [props.dispatch, props.setFormNote]);
const saveNoteIfWillChange = useCallback(async (formNote: FormNote) => {
if (!formNote.id || !formNote.bodyWillChangeId) return;
const body = await props.editorRef.current.content();
void scheduleSaveNote({
...formNote,
body: body,
bodyWillChangeId: 0,
bodyChangeId: 0,
});
}, [scheduleSaveNote, props.editorRef]);
return { saveNoteIfWillChange, scheduleSaveNote };
};
export default useScheduleSaveCallbacks;

View File

@ -0,0 +1,44 @@
import { RefObject, useCallback, useEffect, useRef, useState } from 'react';
import { NoteBodyEditorRef, ScrollOptions, ScrollOptionTypes } from './types';
import usePrevious from '@joplin/lib/hooks/usePrevious';
import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher';
import type { EditorScrollPercents } from '../../../app.reducer';
interface Props {
noteId: string;
selectedNoteHash: string;
lastEditorScrollPercents: EditorScrollPercents;
editorRef: RefObject<NoteBodyEditorRef>;
}
const useScrollWhenReadyOptions = ({ noteId, selectedNoteHash, lastEditorScrollPercents, editorRef }: Props) => {
const [scrollWhenReady, setScrollWhenReady] = useState<ScrollOptions|null>(null);
const previousNoteId = usePrevious(noteId);
const lastScrollPercentsRef = useRef<EditorScrollPercents>();
lastScrollPercentsRef.current = lastEditorScrollPercents;
useEffect(() => {
if (noteId === previousNoteId) return;
if (editorRef.current) {
editorRef.current.resetScroll();
}
const lastScrollPercent = lastScrollPercentsRef.current[noteId] || 0;
setScrollWhenReady({
type: selectedNoteHash ? ScrollOptionTypes.Hash : ScrollOptionTypes.Percent,
value: selectedNoteHash ? selectedNoteHash : lastScrollPercent,
});
void ResourceEditWatcher.instance().stopWatchingAll();
}, [noteId, previousNoteId, selectedNoteHash, editorRef]);
const clearScrollWhenReady = useCallback(() => {
setScrollWhenReady(null);
}, []);
return { scrollWhenReady, clearScrollWhenReady };
};
export default useScrollWhenReadyOptions;

View File

@ -15,7 +15,6 @@ const commandsWithDependencies = [
type SetFormNoteCallback = (callback: (prev: FormNote)=> FormNote)=> void;
interface HookDependencies {
formNote: FormNote;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
setShowLocalSearch: Function;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
@ -23,10 +22,7 @@ interface HookDependencies {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
noteSearchBarRef: any;
editorRef: RefObject<NoteBodyEditorRef>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
titleInputRef: any;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
saveNoteAndWait: Function;
titleInputRef: RefObject<HTMLInputElement>;
setFormNote: SetFormNoteCallback;
}
@ -109,6 +105,5 @@ export default function useWindowCommandHandler(dependencies: HookDependencies)
CommandService.instance().unregisterRuntime(command.declaration.name);
}
};
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [editorRef, setShowLocalSearch, noteSearchBarRef, titleInputRef]);
}, [editorRef, setShowLocalSearch, noteSearchBarRef, titleInputRef, setFormNote]);
}

View File

@ -83,7 +83,8 @@ const useRerenderHandler = (props: Props) => {
return accum;
}, {});
const onlyNoteBodyHasChanged = Object.keys(changedDeps).length === 1 && changedDeps[0];
const onlyCheckboxesHaveChanged = previousDeps[0] && changedDeps[0] && onlyCheckboxHasChangedHack(previousDeps[0], props.noteBody);
const previousBody = previousDeps[0] as string;
const onlyCheckboxesHaveChanged = previousDeps[0] && changedDeps[0] && onlyCheckboxHasChangedHack(previousBody, props.noteBody);
const previousHash = usePrevious(props.noteHash, '');
const hashChanged = previousHash !== props.noteHash;

View File

@ -2,9 +2,8 @@ import shim from '../shim';
const { useRef, useEffect } = shim.react();
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const usePrevious = (value: any, initialValue: any = null) => {
const ref = useRef(initialValue);
const usePrevious = <T> (value: T, initialValue: T = null) => {
const ref = useRef<T>(initialValue);
useEffect(() => {
ref.current = value;
});