mirror of
https://github.com/zadam/trilium.git
synced 2025-09-07 07:06:21 +08:00
refactor(react): fix a few more rules of hooks violations
This commit is contained in:
parent
733ec2c145
commit
5a54dd666f
6 changed files with 27 additions and 18 deletions
|
@ -1,6 +1,6 @@
|
|||
import type { RefObject } from "preact";
|
||||
import type { CSSProperties } from "preact/compat";
|
||||
import { useRef, useMemo } from "preact/hooks";
|
||||
import { useMemo } from "preact/hooks";
|
||||
import { memo } from "preact/compat";
|
||||
import { CommandNames } from "../../components/app_context";
|
||||
|
||||
|
@ -22,7 +22,7 @@ export interface ButtonProps {
|
|||
title?: string;
|
||||
}
|
||||
|
||||
const Button = memo(({ name, buttonRef: _buttonRef, className, text, onClick, keyboardShortcut, icon, primary, disabled, size, style, triggerCommand, ...restProps }: ButtonProps) => {
|
||||
const Button = memo(({ name, buttonRef, className, text, onClick, keyboardShortcut, icon, primary, disabled, size, style, triggerCommand, ...restProps }: ButtonProps) => {
|
||||
// Memoize classes array to prevent recreation
|
||||
const classes = useMemo(() => {
|
||||
const classList: string[] = ["btn"];
|
||||
|
@ -42,8 +42,6 @@ const Button = memo(({ name, buttonRef: _buttonRef, className, text, onClick, ke
|
|||
return classList.join(" ");
|
||||
}, [primary, className, size]);
|
||||
|
||||
const buttonRef = _buttonRef ?? useRef<HTMLButtonElement>(null);
|
||||
|
||||
// Memoize keyboard shortcut rendering
|
||||
const shortcutElements = useMemo(() => {
|
||||
if (!keyboardShortcut) return null;
|
||||
|
|
|
@ -6,8 +6,7 @@ import { CSSProperties, memo } from "preact/compat";
|
|||
import { useUniqueName } from "./hooks";
|
||||
|
||||
interface FormCheckboxProps {
|
||||
id?: string;
|
||||
name?: string;
|
||||
name: string;
|
||||
label: string | ComponentChildren;
|
||||
/**
|
||||
* If set, the checkbox label will be underlined and dotted, indicating a hint. When hovered, it will show the hint text.
|
||||
|
@ -19,9 +18,9 @@ interface FormCheckboxProps {
|
|||
containerStyle?: CSSProperties;
|
||||
}
|
||||
|
||||
const FormCheckbox = memo(({ name, id: _id, disabled, label, currentValue, onChange, hint, containerStyle }: FormCheckboxProps) => {
|
||||
const id = _id ?? useUniqueName(name);
|
||||
const FormCheckbox = memo(({ name, disabled, label, currentValue, onChange, hint, containerStyle }: FormCheckboxProps) => {
|
||||
const labelRef = useRef<HTMLLabelElement>(null);
|
||||
const id = useUniqueName(name);
|
||||
|
||||
// Fix: Move useEffect outside conditional
|
||||
useEffect(() => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useContext, useEffect, useRef, useMemo, useCallback } from "preact/hooks";
|
||||
import { useContext, useEffect, useRef, useMemo } from "preact/hooks";
|
||||
import { t } from "../../services/i18n";
|
||||
import { ComponentChildren } from "preact";
|
||||
import type { CSSProperties, RefObject } from "preact/compat";
|
||||
|
@ -6,6 +6,7 @@ import { openDialog } from "../../services/dialog";
|
|||
import { ParentComponent } from "./react_utils";
|
||||
import { Modal as BootstrapModal } from "bootstrap";
|
||||
import { memo } from "preact/compat";
|
||||
import { useSyncedRef } from "./hooks";
|
||||
|
||||
interface ModalProps {
|
||||
className: string;
|
||||
|
@ -64,10 +65,9 @@ interface ModalProps {
|
|||
stackable?: boolean;
|
||||
}
|
||||
|
||||
export default function Modal({ children, className, size, title, header, footer, footerStyle, footerAlignment, onShown, onSubmit, helpPageId, minWidth, maxWidth, zIndex, scrollable, onHidden: onHidden, modalRef: _modalRef, formRef: _formRef, bodyStyle, show, stackable }: ModalProps) {
|
||||
const modalRef = _modalRef ?? useRef<HTMLDivElement>(null);
|
||||
export default function Modal({ children, className, size, title, header, footer, footerStyle, footerAlignment, onShown, onSubmit, helpPageId, minWidth, maxWidth, zIndex, scrollable, onHidden: onHidden, modalRef: externalModalRef, formRef, bodyStyle, show, stackable }: ModalProps) {
|
||||
const modalRef = useSyncedRef<HTMLDivElement>(externalModalRef);
|
||||
const modalInstanceRef = useRef<BootstrapModal>();
|
||||
const formRef = _formRef ?? useRef<HTMLFormElement>(null);
|
||||
const parentWidget = useContext(ParentComponent);
|
||||
const elementToFocus = useRef<Element | null>();
|
||||
|
||||
|
@ -145,10 +145,10 @@ export default function Modal({ children, className, size, title, header, footer
|
|||
</div>
|
||||
|
||||
{onSubmit ? (
|
||||
<form ref={formRef} onSubmit={useCallback((e) => {
|
||||
<form ref={formRef} onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
onSubmit();
|
||||
}, [onSubmit])}>
|
||||
}}>
|
||||
<ModalInner footer={footer} bodyStyle={bodyStyle} footerStyle={footerStyle} footerAlignment={footerAlignment}>{children}</ModalInner>
|
||||
</form>
|
||||
) : (
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { useRef } from "preact/hooks";
|
||||
import { t } from "../../services/i18n";
|
||||
import { useEffect } from "preact/hooks";
|
||||
import note_autocomplete, { Options, type Suggestion } from "../../services/note_autocomplete";
|
||||
import type { RefObject } from "preact";
|
||||
import type { CSSProperties } from "preact/compat";
|
||||
import { useSyncedRef } from "./hooks";
|
||||
|
||||
interface NoteAutocompleteProps {
|
||||
id?: string;
|
||||
|
@ -19,8 +19,8 @@ interface NoteAutocompleteProps {
|
|||
noteId?: string;
|
||||
}
|
||||
|
||||
export default function NoteAutocomplete({ id, inputRef: _ref, text, placeholder, onChange, onTextChange, container, containerStyle, opts, noteId, noteIdChanged }: NoteAutocompleteProps) {
|
||||
const ref = _ref ?? useRef<HTMLInputElement>(null);
|
||||
export default function NoteAutocomplete({ id, inputRef: externalInputRef, text, placeholder, onChange, onTextChange, container, containerStyle, opts, noteId, noteIdChanged }: NoteAutocompleteProps) {
|
||||
const ref = useSyncedRef<HTMLInputElement>(externalInputRef);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current) return;
|
||||
|
|
|
@ -509,4 +509,16 @@ export function useLegacyImperativeHandlers(handlers: Record<string, Function>)
|
|||
useEffect(() => {
|
||||
Object.assign(parentComponent as never, handlers);
|
||||
}, [ handlers ]);
|
||||
}
|
||||
|
||||
export function useSyncedRef<T>(externalRef?: RefObject<T>, initialValue: T | null = null): RefObject<T> {
|
||||
const ref = useRef<T>(initialValue);
|
||||
|
||||
useEffect(() => {
|
||||
if (externalRef) {
|
||||
externalRef.current = ref.current;
|
||||
}
|
||||
}, [ ref, externalRef ]);
|
||||
|
||||
return ref;
|
||||
}
|
|
@ -132,7 +132,7 @@ interface SingleProviderSettingsProps {
|
|||
}
|
||||
|
||||
function SingleProviderSettings({ provider, title, apiKeyDescription, baseUrlDescription, modelDescription, validationErrorMessage, apiKeyOption, baseUrlOption, modelOption }: SingleProviderSettingsProps) {
|
||||
const [ apiKey, setApiKey ] = apiKeyOption ? useTriliumOption(apiKeyOption) : [];
|
||||
const [ apiKey, setApiKey ] = useTriliumOption(apiKeyOption ?? baseUrlOption);
|
||||
const [ baseUrl, setBaseUrl ] = useTriliumOption(baseUrlOption);
|
||||
const isValid = (apiKeyOption ? !!apiKey : !!baseUrl);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue