Add React hooks eslint

This commit is contained in:
Gregory Schier
2023-04-01 15:26:57 -07:00
parent 604254257d
commit b23e56c3af
23 changed files with 151 additions and 98 deletions

View File

@@ -2,6 +2,7 @@ module.exports = {
extends: [ extends: [
"eslint:recommended", "eslint:recommended",
"plugin:react/recommended", "plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:import/recommended", "plugin:import/recommended",
"plugin:jsx-a11y/recommended", "plugin:jsx-a11y/recommended",
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",

20
package-lock.json generated
View File

@@ -59,6 +59,7 @@
"eslint-plugin-import": "^2.27.5", "eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.32.2", "eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"postcss": "^8.4.21", "postcss": "^8.4.21",
"postcss-nesting": "^11.2.1", "postcss-nesting": "^11.2.1",
"prettier": "^2.8.4", "prettier": "^2.8.4",
@@ -3823,6 +3824,18 @@
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
} }
}, },
"node_modules/eslint-plugin-react-hooks": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
"integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
"dev": true,
"engines": {
"node": ">=10"
},
"peerDependencies": {
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
}
},
"node_modules/eslint-plugin-react/node_modules/doctrine": { "node_modules/eslint-plugin-react/node_modules/doctrine": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@@ -10191,6 +10204,13 @@
} }
} }
}, },
"eslint-plugin-react-hooks": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
"integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
"dev": true,
"requires": {}
},
"eslint-scope": { "eslint-scope": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",

View File

@@ -66,6 +66,7 @@
"eslint-plugin-import": "^2.27.5", "eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.32.2", "eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"postcss": "^8.4.21", "postcss": "^8.4.21",
"postcss-nesting": "^11.2.1", "postcss-nesting": "^11.2.1",
"prettier": "^2.8.4", "prettier": "^2.8.4",

View File

@@ -43,7 +43,7 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi
const handleChangeQuery = useCallback( const handleChangeQuery = useCallback(
(query: string) => handleChange({ query, variables }), (query: string) => handleChange({ query, variables }),
[handleChange], [handleChange, variables],
); );
const handleChangeVariables = useCallback( const handleChangeVariables = useCallback(
@@ -54,11 +54,12 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi
// Meh, not much we can do here // Meh, not much we can do here
} }
}, },
[handleChange], [handleChange, query],
); );
const editorViewRef = useRef<EditorView>(null); const editorViewRef = useRef<EditorView>(null);
// Refetch the schema when the URL changes
useEffect(() => { useEffect(() => {
let unmounted = false; let unmounted = false;
const body = JSON.stringify({ const body = JSON.stringify({
@@ -67,43 +68,44 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi
}); });
sendEphemeralRequest({ ...baseRequest, body }).then((response) => { sendEphemeralRequest({ ...baseRequest, body }).then((response) => {
if (unmounted) return; if (unmounted) return;
if (!editorViewRef.current) return;
try { try {
if (editorViewRef.current) { const { data } = JSON.parse(response.body);
const { data } = JSON.parse(response.body); const schema = buildClientSchema(data);
const schema = buildClientSchema(data); console.log('SET SCHEMA', schema, baseRequest.url);
updateSchema(editorViewRef.current, schema); updateSchema(editorViewRef.current, schema);
}
} catch (err) { } catch (err) {
console.log('Failed to parse introspection query', err); console.log('Failed to parse introspection query', err);
updateSchema(editorViewRef.current, undefined);
return; return;
} }
}); });
return () => { return () => {
unmounted = true; unmounted = true;
}; };
}, [baseRequest.url]); }, [baseRequest, baseRequest.url]);
return ( return (
<div className="pb-2 h-full grid grid-rows-[minmax(0,100%)_auto_auto_minmax(0,auto)]"> <div className="pb-2 h-full grid grid-rows-[minmax(0,100%)_auto_auto_minmax(0,auto)]">
<Editor <Editor
ref={editorViewRef}
heightMode="auto"
defaultValue={query ?? ''}
onChange={handleChangeQuery}
contentType="application/graphql" contentType="application/graphql"
placeholder="..." defaultValue={query ?? ''}
format={formatGraphQL} format={formatGraphQL}
heightMode="auto"
onChange={handleChangeQuery}
placeholder="..."
ref={editorViewRef}
{...extraEditorProps} {...extraEditorProps}
/> />
<Separator variant="primary" /> <Separator variant="primary" />
<p className="pt-1 text-gray-500 text-sm">Variables</p> <p className="pt-1 text-gray-500 text-sm">Variables</p>
<Editor <Editor
useTemplating
heightMode="auto"
placeholder="{}"
defaultValue={JSON.stringify(variables, null, 2)}
onChange={handleChangeVariables}
contentType="application/json" contentType="application/json"
defaultValue={JSON.stringify(variables, null, 2)}
heightMode="auto"
onChange={handleChangeVariables}
placeholder="{}"
useTemplating
{...extraEditorProps} {...extraEditorProps}
/> />
</div> </div>

View File

@@ -116,18 +116,16 @@ export const RequestPane = memo(function RequestPane({ style, fullHeight, classN
}, },
}, },
], ],
[ [activeRequest, updateRequest],
activeRequest?.bodyType,
activeRequest?.headers,
activeRequest?.authenticationType,
activeRequest?.authentication,
],
); );
const handleBodyChange = useCallback((body: string) => updateRequest.mutate({ body }), []); const handleBodyChange = useCallback(
(body: string) => updateRequest.mutate({ body }),
[updateRequest],
);
const handleHeadersChange = useCallback( const handleHeadersChange = useCallback(
(headers: HttpHeader[]) => updateRequest.mutate({ headers }), (headers: HttpHeader[]) => updateRequest.mutate({ headers }),
[], [updateRequest],
); );
return ( return (

View File

@@ -64,7 +64,7 @@ export const RequestResponse = memo(function RequestResponse({ style }: Props) {
const handleReset = useCallback( const handleReset = useCallback(
() => (vertical ? heightKv.set(DEFAULT) : widthKv.set(DEFAULT)), () => (vertical ? heightKv.set(DEFAULT) : widthKv.set(DEFAULT)),
[vertical], [heightKv, vertical, widthKv],
); );
const handleResizeStart = useCallback( const handleResizeStart = useCallback(
@@ -110,7 +110,7 @@ export const RequestResponse = memo(function RequestResponse({ style }: Props) {
document.documentElement.addEventListener('mouseup', moveState.current.up); document.documentElement.addEventListener('mouseup', moveState.current.up);
setIsResizing(true); setIsResizing(true);
}, },
[widthKv.value, heightKv.value, vertical], [width, height, vertical, heightKv, widthKv],
); );
return ( return (

View File

@@ -100,7 +100,7 @@ function SidebarItems({
updateRequest.mutate({ id: requestId, update }); updateRequest.mutate({ id: requestId, update });
} }
}, },
[hoveredIndex, requests], [hoveredIndex, requests, updateRequest],
); );
return ( return (
@@ -139,10 +139,13 @@ const _SidebarItem = forwardRef(function SidebarItem(
const updateRequest = useUpdateRequest(requestId); const updateRequest = useUpdateRequest(requestId);
const [editing, setEditing] = useState<boolean>(false); const [editing, setEditing] = useState<boolean>(false);
const handleSubmitNameEdit = useCallback(async (el: HTMLInputElement) => { const handleSubmitNameEdit = useCallback(
await updateRequest.mutate((r) => ({ ...r, name: el.value })); async (el: HTMLInputElement) => {
setEditing(false); await updateRequest.mutate((r) => ({ ...r, name: el.value }));
}, []); setEditing(false);
},
[updateRequest],
);
const handleFocus = useCallback((el: HTMLInputElement | null) => { const handleFocus = useCallback((el: HTMLInputElement | null) => {
el?.focus(); el?.focus();
@@ -171,7 +174,7 @@ const _SidebarItem = forwardRef(function SidebarItem(
break; break;
} }
}, },
[active], [handleSubmitNameEdit],
); );
return ( return (

View File

@@ -8,7 +8,7 @@ export const SidebarActions = memo(function SidebarDisplayToggle() {
const createRequest = useCreateRequest({ navigateAfter: true }); const createRequest = useCreateRequest({ navigateAfter: true });
const handleCreateRequest = useCallback(() => { const handleCreateRequest = useCallback(() => {
createRequest.mutate({ name: 'New Request' }); createRequest.mutate({ name: 'New Request' });
}, []); }, [createRequest]);
return ( return (
<> <>

View File

@@ -17,8 +17,14 @@ type Props = Pick<HttpRequest, 'id' | 'url' | 'method'> & {
export const UrlBar = memo(function UrlBar({ id: requestId, url, method, className }: Props) { export const UrlBar = memo(function UrlBar({ id: requestId, url, method, className }: Props) {
const sendRequest = useSendRequest(requestId); const sendRequest = useSendRequest(requestId);
const updateRequest = useUpdateRequest(requestId); const updateRequest = useUpdateRequest(requestId);
const handleMethodChange = useCallback((method: string) => updateRequest.mutate({ method }), []); const handleMethodChange = useCallback(
const handleUrlChange = useCallback((url: string) => updateRequest.mutate({ url }), []); (method: string) => updateRequest.mutate({ method }),
[updateRequest],
);
const handleUrlChange = useCallback(
(url: string) => updateRequest.mutate({ url }),
[updateRequest],
);
const loading = useIsResponseLoading(requestId); const loading = useIsResponseLoading(requestId);
const { updateKey } = useRequestUpdateKey(requestId); const { updateKey } = useRequestUpdateKey(requestId);

View File

@@ -42,7 +42,7 @@ export default function Workspace() {
setFloating(false); setFloating(false);
sidebar.show(); sidebar.show();
} }
}, [windowSize.width]); }, [sidebar, windowSize.width]);
const unsub = () => { const unsub = () => {
if (moveState.current !== null) { if (moveState.current !== null) {
@@ -73,7 +73,7 @@ export default function Workspace() {
document.documentElement.addEventListener('mouseup', moveState.current.up); document.documentElement.addEventListener('mouseup', moveState.current.up);
setIsResizing(true); setIsResizing(true);
}, },
[sidebar.width, sidebar.hidden], [sidebar],
); );
const sideWidth = sidebar.hidden ? 0 : sidebar.width; const sideWidth = sidebar.hidden ? 0 : sidebar.width;

View File

@@ -65,7 +65,15 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceDropdown({ classN
}, },
}, },
]; ];
}, [workspaces, activeWorkspaceId]); }, [
workspaces,
activeWorkspaceId,
routes,
createWorkspace,
confirm,
activeWorkspace?.name,
deleteWorkspace,
]);
return ( return (
<Dropdown items={items}> <Dropdown items={items}>

View File

@@ -54,7 +54,7 @@ export function Dropdown({ children, items }: DropdownProps) {
const handleClose = useCallback(() => { const handleClose = useCallback(() => {
setOpen(false); setOpen(false);
ref.current?.focus(); ref.current?.focus();
}, [ref.current]); }, []);
useEffect(() => { useEffect(() => {
ref.current?.setAttribute('aria-expanded', open.toString()); ref.current?.setAttribute('aria-expanded', open.toString());
@@ -63,7 +63,7 @@ export function Dropdown({ children, items }: DropdownProps) {
const triggerRect = useMemo(() => { const triggerRect = useMemo(() => {
if (!open) return null; if (!open) return null;
return ref.current?.getBoundingClientRect(); return ref.current?.getBoundingClientRect();
}, [ref.current, open]); }, [open]);
return ( return (
<> <>
@@ -83,8 +83,6 @@ interface MenuProps {
} }
function Menu({ className, items, onClose, triggerRect }: MenuProps) { function Menu({ className, items, onClose, triggerRect }: MenuProps) {
if (triggerRect === undefined) return null;
const containerRef = useRef<HTMLDivElement | null>(null); const containerRef = useRef<HTMLDivElement | null>(null);
const [menuStyles, setMenuStyles] = useState<CSSProperties>({}); const [menuStyles, setMenuStyles] = useState<CSSProperties>({});

View File

@@ -88,12 +88,14 @@ const _Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
const { view, languageCompartment } = cm.current; const { view, languageCompartment } = cm.current;
const ext = getLanguageExtension({ contentType, useTemplating, autocomplete }); const ext = getLanguageExtension({ contentType, useTemplating, autocomplete });
view.dispatch({ effects: languageCompartment.reconfigure(ext) }); view.dispatch({ effects: languageCompartment.reconfigure(ext) });
}, [contentType, autocomplete]); }, [contentType, autocomplete, useTemplating]);
useEffect(() => { useEffect(() => {
if (cm.current === null) return; if (cm.current === null) return;
const { view } = cm.current; const { view } = cm.current;
view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: defaultValue ?? '' } }); view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: defaultValue ?? '' } });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [forceUpdateKey]); }, [forceUpdateKey]);
// Initialize the editor when ref mounts // Initialize the editor when ref mounts
@@ -133,6 +135,8 @@ const _Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
} catch (e) { } catch (e) {
console.log('Failed to initialize Codemirror', e); console.log('Failed to initialize Codemirror', e);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const cmContainer = ( const cmContainer = (

View File

@@ -36,7 +36,7 @@ const _IconButton = forwardRef<HTMLButtonElement, Props>(function IconButton(
if (showConfirm) setConfirmed(); if (showConfirm) setConfirmed();
onClick?.(e); onClick?.(e);
}, },
[onClick], [onClick, setConfirmed, showConfirm],
); );
return ( return (
<Button <Button

View File

@@ -63,10 +63,13 @@ export function Input({
return true; return true;
}, [currentValue, validate, require]); }, [currentValue, validate, require]);
const handleChange = useCallback((value: string) => { const handleChange = useCallback(
setCurrentValue(value); (value: string) => {
onChange?.(value); setCurrentValue(value);
}, []); onChange?.(value);
},
[onChange],
);
return ( return (
<VStack className="w-full"> <VStack className="w-full">

View File

@@ -62,6 +62,8 @@ export const PairEditor = memo(function PairEditor({
const nonEmpty = originalPairs.filter((h) => !(h.name === '' && h.value === '')); const nonEmpty = originalPairs.filter((h) => !(h.name === '' && h.value === ''));
const pairs = nonEmpty.map((pair) => newPairContainer(pair)); const pairs = nonEmpty.map((pair) => newPairContainer(pair));
setPairs([...pairs, newPairContainer()]); setPairs([...pairs, newPairContainer()]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [forceUpdateKey]); }, [forceUpdateKey]);
const setPairsAndSave = useCallback( const setPairsAndSave = useCallback(
@@ -99,19 +101,19 @@ export const PairEditor = memo(function PairEditor({
return newPairs; return newPairs;
}); });
}, },
[hoveredIndex], [hoveredIndex, setPairsAndSave],
); );
const handleChange = useCallback( const handleChange = useCallback(
(pair: PairContainer) => (pair: PairContainer) =>
setPairsAndSave((pairs) => pairs.map((p) => (pair.id !== p.id ? p : pair))), setPairsAndSave((pairs) => pairs.map((p) => (pair.id !== p.id ? p : pair))),
[], [setPairsAndSave],
); );
const handleDelete = useCallback( const handleDelete = useCallback(
(pair: PairContainer) => (pair: PairContainer) =>
setPairsAndSave((oldPairs) => oldPairs.filter((p) => p.id !== pair.id)), setPairsAndSave((oldPairs) => oldPairs.filter((p) => p.id !== pair.id)),
[], [setPairsAndSave],
); );
const handleFocus = useCallback( const handleFocus = useCallback(
@@ -216,17 +218,17 @@ const FormRow = memo(function FormRow({
const handleChangeEnabled = useMemo( const handleChangeEnabled = useMemo(
() => (enabled: boolean) => onChange({ id, pair: { ...pairContainer.pair, enabled } }), () => (enabled: boolean) => onChange({ id, pair: { ...pairContainer.pair, enabled } }),
[onChange, pairContainer.pair.name, pairContainer.pair.value], [id, onChange, pairContainer.pair],
); );
const handleChangeName = useMemo( const handleChangeName = useMemo(
() => (name: string) => onChange({ id, pair: { ...pairContainer.pair, name } }), () => (name: string) => onChange({ id, pair: { ...pairContainer.pair, name } }),
[onChange, pairContainer.pair.value, pairContainer.pair.enabled], [onChange, id, pairContainer.pair],
); );
const handleChangeValue = useMemo( const handleChangeValue = useMemo(
() => (value: string) => onChange({ id, pair: { ...pairContainer.pair, value } }), () => (value: string) => onChange({ id, pair: { ...pairContainer.pair, value } }),
[onChange, pairContainer.pair.name, pairContainer.pair.enabled], [onChange, id, pairContainer.pair],
); );
const handleFocus = useCallback(() => onFocus?.(pairContainer), [onFocus, pairContainer]); const handleFocus = useCallback(() => onFocus?.(pairContainer), [onFocus, pairContainer]);

View File

@@ -39,7 +39,7 @@ export function RadioDropdown<T = string | null>({
}; };
} }
}), }),
[value, items], [items, value, onChange],
); );
return <Dropdown items={dropdownItems}>{children}</Dropdown>; return <Dropdown items={dropdownItems}>{children}</Dropdown>;

View File

@@ -1,6 +1,6 @@
import classnames from 'classnames'; import classnames from 'classnames';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { memo, useEffect, useRef } from 'react'; import { memo, useCallback, useEffect, useRef } from 'react';
import { Button } from '../Button'; import { Button } from '../Button';
import { Icon } from '../Icon'; import { Icon } from '../Icon';
import type { RadioDropdownProps } from '../RadioDropdown'; import type { RadioDropdownProps } from '../RadioDropdown';
@@ -38,28 +38,31 @@ export function Tabs({
}: Props) { }: Props) {
const ref = useRef<HTMLDivElement | null>(null); const ref = useRef<HTMLDivElement | null>(null);
const handleTabChange = (value: string) => { const handleTabChange = useCallback(
const tabs = ref.current?.querySelectorAll<HTMLDivElement>(`[data-tab]`); (value: string) => {
for (const tab of tabs ?? []) { const tabs = ref.current?.querySelectorAll<HTMLDivElement>(`[data-tab]`);
const v = tab.getAttribute('data-tab'); for (const tab of tabs ?? []) {
if (v === value) { const v = tab.getAttribute('data-tab');
tab.setAttribute('tabindex', '-1'); if (v === value) {
tab.setAttribute('data-state', 'active'); tab.setAttribute('tabindex', '-1');
tab.setAttribute('aria-hidden', 'false'); tab.setAttribute('data-state', 'active');
tab.style.display = 'block'; tab.setAttribute('aria-hidden', 'false');
} else { tab.style.display = 'block';
tab.setAttribute('data-state', 'inactive'); } else {
tab.setAttribute('aria-hidden', 'true'); tab.setAttribute('data-state', 'inactive');
tab.style.display = 'none'; tab.setAttribute('aria-hidden', 'true');
tab.style.display = 'none';
}
} }
} onChangeValue(value);
onChangeValue(value); },
}; [onChangeValue],
);
useEffect(() => { useEffect(() => {
if (value === undefined) return; if (value === undefined) return;
handleTabChange(value); handleTabChange(value);
}, [value]); }, [handleTabChange, value]);
return ( return (
<div <div

View File

@@ -13,7 +13,7 @@ export function Webview({ body, url, contentType }: Props) {
return body.replace(/<head>/gi, `<head><base href="${url}"/>`); return body.replace(/<head>/gi, `<head><base href="${url}"/>`);
} }
return body; return body;
}, [body, contentType]); }, [url, body, contentType]);
return ( return (
<div className="px-2 pb-2"> <div className="px-2 pb-2">

View File

@@ -1,5 +1,5 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useCallback } from 'react'; import { useCallback, useMemo } from 'react';
import { buildKeyValueKey, getKeyValue, setKeyValue } from '../lib/keyValueStore'; import { buildKeyValueKey, getKeyValue, setKeyValue } from '../lib/keyValueStore';
const DEFAULT_NAMESPACE = 'app'; const DEFAULT_NAMESPACE = 'app';
@@ -46,15 +46,18 @@ export function useKeyValue<T extends Object | null>({
mutate.mutate(value); mutate.mutate(value);
} }
}, },
[defaultValue], [defaultValue, key, mutate, namespace],
); );
const reset = useCallback(() => mutate.mutate(defaultValue), [defaultValue]); const reset = useCallback(() => mutate.mutate(defaultValue), [mutate, defaultValue]);
return { return useMemo(
value: query.data, () => ({
isLoading: query.isLoading, value: query.data,
set, isLoading: query.isLoading,
reset, set,
}; reset,
}),
[query.data, query.isLoading, reset, set],
);
} }

View File

@@ -1,4 +1,4 @@
import { useCallback } from 'react'; import { useCallback, useMemo } from 'react';
import { NAMESPACE_NO_SYNC } from '../lib/keyValueStore'; import { NAMESPACE_NO_SYNC } from '../lib/keyValueStore';
import { useKeyValue } from './useKeyValue'; import { useKeyValue } from './useKeyValue';
@@ -28,12 +28,15 @@ export function useSidebarDisplay() {
const hidden = width < COLLAPSE_WIDTH; const hidden = width < COLLAPSE_WIDTH;
display.set({ hidden, width: Math.max(MIN_WIDTH, width) }); display.set({ hidden, width: Math.max(MIN_WIDTH, width) });
}, },
[display.set], [display],
); );
const hide = useCallback(() => display.set((v) => ({ ...v, hidden: true })), []); const hide = useCallback(() => display.set((v) => ({ ...v, hidden: true })), [display]);
const show = useCallback(() => display.set((v) => ({ ...v, hidden: false })), []); const show = useCallback(() => display.set((v) => ({ ...v, hidden: false })), [display]);
const toggle = useCallback(() => display.set((v) => ({ ...v, hidden: !v.hidden })), []); const toggle = useCallback(() => display.set((v) => ({ ...v, hidden: !v.hidden })), [display]);
const reset = display.reset; const reset = display.reset;
return { width, hidden, set, reset, hide, show, toggle }; return useMemo(
() => ({ width, hidden, set, reset, hide, show, toggle }),
[hidden, hide, reset, set, show, toggle, width],
);
} }

View File

@@ -87,7 +87,9 @@ export function useTauriListeners() {
: null; : null;
if (queryKey === null) { if (queryKey === null) {
console.log('Unrecognized updated model:', payload); if (payload.model) {
console.log('Unrecognized updated model:', payload);
}
return; return;
} }
@@ -154,5 +156,5 @@ export function useTauriListeners() {
unsub(); unsub();
} }
}; };
}, []); }, [queryClient, sidebarDisplay.toggle, wasUpdatedExternally]);
} }

View File

@@ -13,16 +13,12 @@ export function useTheme() {
defaultValue: getAppearance(), defaultValue: getAppearance(),
}); });
const themeChange = (appearance: Appearance) => {
appearanceKv.set(appearance);
};
const handleToggleAppearance = async () => { const handleToggleAppearance = async () => {
appearanceKv.set(appearanceKv.value === 'dark' ? 'light' : 'dark'); appearanceKv.set(appearanceKv.value === 'dark' ? 'light' : 'dark');
}; };
// Set appearance when preferred theme changes // Set appearance when preferred theme changes
useEffect(() => subscribeToPreferredAppearanceChange(themeChange), []); useEffect(() => subscribeToPreferredAppearanceChange(appearanceKv.set), [appearanceKv.set]);
// Sync appearance when k/v changes // Sync appearance when k/v changes
useEffect(() => setAppearance(appearanceKv.value), [appearanceKv.value]); useEffect(() => setAppearance(appearanceKv.value), [appearanceKv.value]);