2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-27 19:16:44 +00:00

chore: bump zustand (#9577)

* bump zustand to v5

* add missing shallow

* fix missing shallow

---------

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
This commit is contained in:
Matthias Mair 2025-04-25 01:39:08 +02:00 committed by GitHub
parent 71cf9f5452
commit c41760a500
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 175 additions and 127 deletions

View File

@ -101,7 +101,7 @@
"react-window": "1.8.10",
"recharts": "^2.15.0",
"styled-components": "^6.1.14",
"zustand": "^4.5.5"
"zustand": "^5.0.3"
},
"devDependencies": {
"@babel/core": "^7.26.10",

View File

@ -1,5 +1,6 @@
import { BackgroundImage } from '@mantine/core';
import { useEffect } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { generateUrl } from '../functions/urls';
import { useServerApiState } from '../states/ApiState';
@ -11,10 +12,9 @@ export default function SplashScreen({
}: Readonly<{
children: React.ReactNode;
}>) {
const [server, fetchServerApiState] = useServerApiState((state) => [
state.server,
state.fetchServerApiState
]);
const [server, fetchServerApiState] = useServerApiState(
useShallow((state) => [state.server, state.fetchServerApiState])
);
// Fetch server data on mount if no server data is present
useEffect(() => {

View File

@ -26,6 +26,7 @@ import {
IconFilter
} from '@tabler/icons-react';
import { useCallback, useState } from 'react';
import { useShallow } from 'zustand/react/shallow';
import type { CalendarState } from '../../hooks/UseCalendar';
import { useLocalState } from '../../states/LocalState';
import { FilterSelectDrawer } from '../../tables/FilterSelectDrawer';
@ -51,12 +52,12 @@ export default function Calendar({
filters,
state,
...calendarProps
}: InvenTreeCalendarProps) {
}: Readonly<InvenTreeCalendarProps>) {
const [monthSelectOpened, setMonthSelectOpened] = useState<boolean>(false);
const [filtersVisible, setFiltersVisible] = useState<boolean>(false);
const [locale] = useLocalState((s) => [s.language]);
const [locale] = useLocalState(useShallow((s) => [s.language]));
const selectMonth = useCallback(
(date: DateValue) => {

View File

@ -5,6 +5,7 @@ import { IconInfoCircle } from '@tabler/icons-react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { type Layout, Responsive, WidthProvider } from 'react-grid-layout';
import { useShallow } from 'zustand/react/shallow';
import { useDashboardItems } from '../../hooks/UseDashboardItems';
import { useLocalState } from '../../states/LocalState';
import DashboardMenu from './DashboardMenu';
@ -21,12 +22,14 @@ export default function DashboardLayout() {
// local/remote storage values for widget / layout
const [remoteWidgets, setRemoteWidgets, remoteLayouts, setRemoteLayouts] =
useLocalState((state) => [
state.widgets,
state.setWidgets,
state.layouts,
state.setLayouts
]);
useLocalState(
useShallow((state) => [
state.widgets,
state.setWidgets,
state.layouts,
state.setLayouts
])
);
const [editing, setEditing] = useDisclosure(false);
const [removing, setRemoving] = useDisclosure(false);

View File

@ -1,6 +1,7 @@
import { ActionIcon, Center, Group, Text, Tooltip } from '@mantine/core';
import { IconServer } from '@tabler/icons-react';
import { useShallow } from 'zustand/react/shallow';
import { useServerApiState } from '../../states/ApiState';
import { ColorToggle } from '../items/ColorToggle';
import { LanguageToggle } from '../items/LanguageToggle';
@ -12,7 +13,7 @@ export function AuthFormOptions({
hostname: string;
toggleHostEdit: () => void;
}>) {
const [server] = useServerApiState((state) => [state.server]);
const [server] = useServerApiState(useShallow((state) => [state.server]));
return (
<Center mx={'md'}>

View File

@ -18,6 +18,7 @@ import { useLocation, useNavigate } from 'react-router-dom';
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import { apiUrl } from '@lib/functions/Api';
import { showNotification } from '@mantine/notifications';
import { useShallow } from 'zustand/react/shallow';
import { api } from '../../App';
import {
doBasicLogin,
@ -37,11 +38,13 @@ export function AuthenticationForm() {
const simpleForm = useForm({ initialValues: { email: '' } });
const [classicLoginMode, setMode] = useDisclosure(true);
const [auth_config, sso_enabled, password_forgotten_enabled] =
useServerApiState((state) => [
state.auth_config,
state.sso_enabled,
state.password_forgotten_enabled
]);
useServerApiState(
useShallow((state) => [
state.auth_config,
state.sso_enabled,
state.password_forgotten_enabled
])
);
const navigate = useNavigate();
const location = useLocation();
const { isLoggedIn } = useUserState();
@ -207,11 +210,13 @@ export function RegistrationForm() {
});
const navigate = useNavigate();
const [auth_config, registration_enabled, sso_registration] =
useServerApiState((state) => [
state.auth_config,
state.registration_enabled,
state.sso_registration_enabled
]);
useServerApiState(
useShallow((state) => [
state.auth_config,
state.registration_enabled,
state.sso_registration_enabled
])
);
const [isRegistering, setIsRegistering] = useState<boolean>(false);
async function handleRegistration() {

View File

@ -21,6 +21,7 @@ import {
} from '@tabler/icons-react';
import type { HostList } from '@lib/types/Server';
import { useShallow } from 'zustand/react/shallow';
import { Wrapper } from '../../pages/Auth/Layout';
import { useServerApiState } from '../../states/ApiState';
import { useLocalState } from '../../states/LocalState';
@ -37,11 +38,9 @@ export function InstanceOptions({
setHostEdit: () => void;
}>) {
const [hostListEdit, setHostListEdit] = useToggle([false, true] as const);
const [setHost, setHostList, hostList] = useLocalState((state) => [
state.setHost,
state.setHostList,
state.hostList
]);
const [setHost, setHostList, hostList] = useLocalState(
useShallow((state) => [state.setHost, state.setHostList, state.hostList])
);
const hostListData = Object.keys(hostList).map((key) => ({
value: key,
label: hostList[key]?.name
@ -111,7 +110,7 @@ function ServerInfo({
hostList: HostList;
hostKey: string;
}>) {
const [server] = useServerApiState((state) => [state.server]);
const [server] = useServerApiState(useShallow((state) => [state.server]));
const items: any[] = [
{

View File

@ -22,6 +22,7 @@ import type { FieldValues, UseControllerReturn } from 'react-hook-form';
import { FixedSizeGrid as Grid } from 'react-window';
import type { ApiFormFieldType } from '@lib/types/Forms';
import { useShallow } from 'zustand/react/shallow';
import { useIconState } from '../../../states/IconState';
import { ApiIcon } from '../../items/ApiIcon';
@ -119,7 +120,7 @@ function ComboboxDropdown({
onChange: (newVal: string | null) => void;
open: boolean;
}>) {
const iconPacks = useIconState((s) => s.packages);
const iconPacks = useIconState(useShallow((s) => s.packages));
const icons = useMemo<RenderIconType[]>(() => {
return iconPacks.flatMap((pack) =>
Object.entries(pack.icons).flatMap(([name, icon]) =>

View File

@ -1,3 +1,4 @@
import { useShallow } from 'zustand/react/shallow';
import { useIconState } from '../../states/IconState';
import * as classes from './ApiIcon.css';
@ -9,7 +10,9 @@ type ApiIconProps = {
export const ApiIcon = ({ name: _name, size = 22 }: ApiIconProps) => {
const [iconPackage, name, variant] = _name.split(':');
const icon = useIconState(
(s) => s.packagesMap[iconPackage]?.icons[name]?.variants[variant]
useShallow(
(s) => s.packagesMap[iconPackage]?.icons[name]?.variants[variant]
)
);
const unicode = icon ? String.fromCodePoint(Number.parseInt(icon, 16)) : '';

View File

@ -1,15 +1,15 @@
import { Select } from '@mantine/core';
import { useEffect, useState } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { getSupportedLanguages } from '../../contexts/LanguageContext';
import { useLocalState } from '../../states/LocalState';
export function LanguageSelect({ width = 80 }: Readonly<{ width?: number }>) {
const [value, setValue] = useState<string | null>(null);
const [locale, setLanguage] = useLocalState((state) => [
state.language,
state.setLanguage
]);
const [locale, setLanguage] = useLocalState(
useShallow((state) => [state.language, state.setLanguage])
);
const [langOptions, setLangOptions] = useState<any[]>([]);
// change global language on change

View File

@ -1,9 +1,10 @@
import { Trans } from '@lingui/react/macro';
import { useShallow } from 'zustand/react/shallow';
import { useUserState } from '../../states/UserState';
export const OnlyStaff = ({ children }: { children: any }) => {
const [user] = useUserState((state) => [state.user]);
const [user] = useUserState(useShallow((state) => [state.user]));
if (user?.is_staff) return children;
return <Trans>This information is only available for staff users</Trans>;

View File

@ -16,6 +16,7 @@ import { useQuery } from '@tanstack/react-query';
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import { apiUrl } from '@lib/functions/Api';
import { useShallow } from 'zustand/react/shallow';
import { api } from '../../App';
import { generateUrl } from '../../functions/urls';
import { useServerApiState } from '../../states/ApiState';
@ -32,14 +33,14 @@ type AboutLookupRef = {
export function AboutInvenTreeModal({
context,
id
id,
innerProps
}: Readonly<
ContextModalProps<{
modalBody: string;
}>
>) {
const [user] = useUserState((state) => [state.user]);
const [server] = useServerApiState((state) => [state.server]);
const [user] = useUserState(useShallow((state) => [state.user]));
if (!user?.is_staff)
return (
@ -47,7 +48,18 @@ export function AboutInvenTreeModal({
<Trans>This information is only available for staff users</Trans>
</Text>
);
return <AboutContent context={context} id={id} innerProps={innerProps} />;
}
const AboutContent = ({
context,
id
}: Readonly<
ContextModalProps<{
modalBody: string;
}>
>) => {
const [server] = useServerApiState(useShallow((state) => [state.server]));
const { isLoading, data } = useQuery({
queryKey: ['version'],
queryFn: () => api.get(apiUrl(ApiEndpoints.version)).then((res) => res.data)
@ -185,7 +197,7 @@ export function AboutInvenTreeModal({
</Group>
</Stack>
);
}
};
function renderVersionBadge(data: any) {
const badgeType = () => {

View File

@ -2,6 +2,7 @@ import { Trans } from '@lingui/react/macro';
import { Badge, Button, Divider, Group, Stack, Table } from '@mantine/core';
import type { ContextModalProps } from '@mantine/modals';
import { useShallow } from 'zustand/react/shallow';
import { useServerApiState } from '../../states/ApiState';
import { OnlyStaff } from '../items/OnlyStaff';
import { StylishText } from '../items/StylishText';
@ -10,7 +11,7 @@ export function ServerInfoModal({
context,
id
}: ContextModalProps<{ modalBody: string }>) {
const [server] = useServerApiState((state) => [state.server]);
const [server] = useServerApiState(useShallow((state) => [state.server]));
return (
<Stack>

View File

@ -3,6 +3,7 @@ import { IconExclamationCircle } from '@tabler/icons-react';
import { useMemo, useState } from 'react';
import { t } from '@lingui/core/macro';
import { useShallow } from 'zustand/react/shallow';
import { docLinks } from '../../defaults/links';
import { useServerApiState } from '../../states/ApiState';
import { useGlobalSettingsState } from '../../states/SettingsState';
@ -26,7 +27,7 @@ interface AlertInfo {
*/
export function Alerts() {
const user = useUserState();
const [server] = useServerApiState((state) => [state.server]);
const [server] = useServerApiState(useShallow((state) => [state.server]));
const globalSettings = useGlobalSettingsState();
const [dismissed, setDismissed] = useState<string[]>([]);

View File

@ -5,6 +5,7 @@ import { Link, Route, Routes, useNavigate, useParams } from 'react-router-dom';
import type { To } from 'react-router-dom';
import type { UiSizeType } from '@lib/types/Core';
import { useShallow } from 'zustand/react/shallow';
import { useLocalState } from '../../states/LocalState';
import { StylishText } from '../items/StylishText';
import * as classes from './DetailDrawer.css';
@ -37,10 +38,9 @@ function DetailDrawerComponent({
const content = renderContent(id);
const opened = useMemo(() => !!id && !!content, [id, content]);
const [detailDrawerStack, addDetailDrawer] = useLocalState((state) => [
state.detailDrawerStack,
state.addDetailDrawer
]);
const [detailDrawerStack, addDetailDrawer] = useLocalState(
useShallow((state) => [state.detailDrawerStack, state.addDetailDrawer])
);
return (
<Drawer
@ -91,7 +91,9 @@ export function DetailDrawerLink({
to,
text
}: Readonly<{ to: To; text: string }>) {
const addDetailDrawer = useLocalState((state) => state.addDetailDrawer);
const addDetailDrawer = useLocalState(
useShallow((state) => state.addDetailDrawer)
);
const onNavigate = useCallback(() => {
addDetailDrawer(1);

View File

@ -1,7 +1,3 @@
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import { apiUrl } from '@lib/functions/Api';
import { navigateToLink } from '@lib/functions/Navigation';
import { t } from '@lingui/core/macro';
import {
ActionIcon,
Container,
@ -16,6 +12,12 @@ import { IconBell, IconSearch } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query';
import { type ReactNode, useEffect, useMemo, useState } from 'react';
import { useMatch, useNavigate } from 'react-router-dom';
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import { apiUrl } from '@lib/functions/Api';
import { navigateToLink } from '@lib/functions/Navigation';
import { t } from '@lingui/core/macro';
import { useShallow } from 'zustand/react/shallow';
import { api } from '../../App';
import type { NavigationUIFeature } from '../../components/plugins/PluginUIFeatureTypes';
import { getNavTabs } from '../../defaults/links';
@ -38,11 +40,10 @@ import { NotificationDrawer } from './NotificationDrawer';
import { SearchDrawer } from './SearchDrawer';
export function Header() {
const [setNavigationOpen, navigationOpen] = useLocalState((state) => [
state.setNavigationOpen,
state.navigationOpen
]);
const [server] = useServerApiState((state) => [state.server]);
const [setNavigationOpen, navigationOpen] = useLocalState(
useShallow((state) => [state.setNavigationOpen, state.navigationOpen])
);
const [server] = useServerApiState(useShallow((state) => [state.server]));
const [navDrawerOpened, { open: openNavDrawer, close: closeNavDrawer }] =
useDisclosure(navigationOpen);
const [

View File

@ -18,6 +18,7 @@ import {
} from '@tabler/icons-react';
import { Link, useNavigate } from 'react-router-dom';
import { useShallow } from 'zustand/react/shallow';
import { doLogout } from '../../functions/auth';
import * as classes from '../../main.css';
import { useUserState } from '../../states/UserState';
@ -25,10 +26,9 @@ import { vars } from '../../theme';
export function MainMenu() {
const navigate = useNavigate();
const [user, username] = useUserState((state) => [
state.user,
state.username
]);
const [user, username] = useUserState(
useShallow((state) => [state.user, state.username])
);
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
return (

View File

@ -31,6 +31,7 @@ import {
import type { ModelType } from '@lib/enums/ModelType';
import { cancelEvent } from '@lib/functions/Events';
import { navigateToLink } from '@lib/functions/Navigation';
import { useShallow } from 'zustand/react/shallow';
import { identifierString } from '../../functions/conversion';
import { usePluginPanels } from '../../hooks/UsePluginPanels';
import { useLocalState } from '../../states/LocalState';
@ -262,19 +263,21 @@ function IndexPanelComponent({
selectedPanel,
panels
}: Readonly<PanelProps>) {
const lastUsedPanel = useLocalState((state) => {
const panelName =
selectedPanel || state.lastUsedPanels[pageKey] || panels[0]?.name;
const lastUsedPanel = useLocalState(
useShallow((state) => {
const panelName =
selectedPanel || state.lastUsedPanels[pageKey] || panels[0]?.name;
const panel = panels.findIndex(
(p) => p.name === panelName && !p.disabled && !p.hidden
);
if (panel === -1) {
return panels.find((p) => !p.disabled && !p.hidden)?.name || '';
}
const panel = panels.findIndex(
(p) => p.name === panelName && !p.disabled && !p.hidden
);
if (panel === -1) {
return panels.find((p) => !p.disabled && !p.hidden)?.name || '';
}
return panelName;
});
return panelName;
})
);
return <Navigate to={lastUsedPanel} replace />;
}

View File

@ -1,7 +1,7 @@
import { useMantineColorScheme, useMantineTheme } from '@mantine/core';
import { useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { useShallow } from 'zustand/react/shallow';
import { api, queryClient } from '../../App';
import { useLocalState } from '../../states/LocalState';
import {
@ -24,7 +24,7 @@ import {
} from '../../hooks/UseForm';
export const useInvenTreeContext = () => {
const [locale, host] = useLocalState((s) => [s.language, s.host]);
const [locale, host] = useLocalState(useShallow((s) => [s.language, s.host]));
const navigate = useNavigate();
const user = useUserState();
const { colorScheme } = useMantineColorScheme();

View File

@ -4,6 +4,7 @@ import { I18nProvider } from '@lingui/react';
import { LoadingOverlay, Text } from '@mantine/core';
import { useEffect, useRef, useState } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { api } from '../App';
import { useServerApiState } from '../states/ApiState';
import { useLocalState } from '../states/LocalState';
@ -61,8 +62,8 @@ export const getSupportedLanguages = (): Record<string, string> => {
export function LanguageContext({
children
}: Readonly<{ children: JSX.Element }>) {
const [language] = useLocalState((state) => [state.language]);
const [server] = useServerApiState((state) => [state.server]);
const [language] = useLocalState(useShallow((state) => [state.language]));
const [server] = useServerApiState(useShallow((state) => [state.server]));
useEffect(() => {
activateLocale(defaultLocale);

View File

@ -4,6 +4,7 @@ import { MantineProvider, createTheme } from '@mantine/core';
import { ModalsProvider } from '@mantine/modals';
import { Notifications } from '@mantine/notifications';
import { ContextMenuProvider } from 'mantine-contextmenu';
import { useShallow } from 'zustand/react/shallow';
import { AboutInvenTreeModal } from '../components/modals/AboutInvenTreeModal';
import { LicenseModal } from '../components/modals/LicenseModal';
import { QrModal } from '../components/modals/QrModal';
@ -15,7 +16,7 @@ import { colorSchema } from './colorSchema';
export function ThemeContext({
children
}: Readonly<{ children: JSX.Element }>) {
const [userTheme] = useLocalState((state) => [state.userTheme]);
const [userTheme] = useLocalState(useShallow((state) => [state.userTheme]));
// Theme
const myTheme = createTheme({

View File

@ -4,6 +4,7 @@ import { IconBarcode, IconLink, IconPointer } from '@tabler/icons-react';
import type { NavigateFunction } from 'react-router-dom';
import { openContextModal } from '@mantine/modals';
import { useShallow } from 'zustand/react/shallow';
import { useLocalState } from '../states/LocalState';
import { useUserState } from '../states/UserState';
import { aboutInvenTree, docLinks, licenseInfo, serverInfo } from './links';
@ -16,7 +17,9 @@ export function openQrModal(navigate: NavigateFunction) {
}
export function getActions(navigate: NavigateFunction) {
const setNavigationOpen = useLocalState((state) => state.setNavigationOpen);
const setNavigationOpen = useLocalState(
useShallow((state) => state.setNavigationOpen)
);
const { user } = useUserState();
const actions: SpotlightActionData[] = [

View File

@ -500,15 +500,19 @@ function StockOperationsRow({
props.removeFn(props.idx);
};
const callChangeFn = (idx: number, key: string, value: any) => {
setTimeout(() => props.changeFn(idx, key, value), 0);
};
const [packagingOpen, packagingHandlers] = useDisclosure(false, {
onOpen: () => {
if (transfer) {
props.changeFn(props.idx, 'packaging', record?.packaging || undefined);
callChangeFn(props.idx, 'packaging', record?.packaging || undefined);
}
},
onClose: () => {
if (transfer) {
props.changeFn(props.idx, 'packaging', undefined);
callChangeFn(props.idx, 'packaging', undefined);
}
}
});
@ -520,7 +524,7 @@ function StockOperationsRow({
},
onClose: () => {
setStatus(undefined);
props.changeFn(props.idx, 'status', undefined);
callChangeFn(props.idx, 'status', undefined);
}
});

View File

@ -4,6 +4,8 @@ import { Anchor, Divider, Group, Loader, Text } from '@mantine/core';
import { useToggle } from '@mantine/hooks';
import { useEffect, useMemo, useState } from 'react';
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
import { useShallow } from 'zustand/react/shallow';
import { setApiDefaults } from '../../App';
import { AuthFormOptions } from '../../components/forms/AuthFormOptions';
import { AuthenticationForm } from '../../components/forms/AuthenticationForm';
@ -19,15 +21,12 @@ import { useLocalState } from '../../states/LocalState';
import { Wrapper } from './Layout';
export default function Login() {
const [hostKey, setHost, hostList] = useLocalState((state) => [
state.hostKey,
state.setHost,
state.hostList
]);
const [server, fetchServerApiState] = useServerApiState((state) => [
state.server,
state.fetchServerApiState
]);
const [hostKey, setHost, hostList] = useLocalState(
useShallow((state) => [state.hostKey, state.setHost, state.hostList])
);
const [server, fetchServerApiState] = useServerApiState(
useShallow((state) => [state.server, state.fetchServerApiState])
);
const [isLoggingIn, setIsLoggingIn] = useState<boolean>(false);
const hostname =
hostList[hostKey] === undefined ? t`No selection` : hostList[hostKey]?.name;
@ -36,7 +35,10 @@ export default function Login() {
const location = useLocation();
const [searchParams] = useSearchParams();
const [sso_registration, registration_enabled] = useServerApiState(
(state) => [state.sso_registration_enabled, state.registration_enabled]
useShallow((state) => [
state.sso_registration_enabled,
state.registration_enabled
])
);
const both_reg_enabled =
registration_enabled() || sso_registration() || false;

View File

@ -7,6 +7,7 @@ import { useMemo } from 'react';
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import type { ApiFormFieldSet } from '@lib/types/Forms';
import { useNavigate } from 'react-router-dom';
import { useShallow } from 'zustand/react/shallow';
import { ActionButton } from '../../../../components/buttons/ActionButton';
import { YesNoUndefinedButton } from '../../../../components/buttons/YesNoButton';
import { ActionDropdown } from '../../../../components/items/ActionDropdown';
@ -17,10 +18,9 @@ import { useUserState } from '../../../../states/UserState';
export function AccountDetailPanel() {
const navigate = useNavigate();
const [user, fetchUserState] = useUserState((state) => [
state.user,
state.fetchUserState
]);
const [user, fetchUserState] = useUserState(
useShallow((state) => [state.user, state.fetchUserState])
);
const userFields: ApiFormFieldSet = useMemo(() => {
return {

View File

@ -31,6 +31,7 @@ import {
} from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query';
import { useMemo, useState } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { api } from '../../../../App';
import { StylishText } from '../../../../components/items/StylishText';
import { ProviderLogin, authApi } from '../../../../functions/auth';
@ -40,10 +41,9 @@ import { QrRegistrationForm } from './QrRegistrationForm';
import { useReauth } from './useConfirm';
export function SecurityContent() {
const [auth_config, sso_enabled] = useServerApiState((state) => [
state.auth_config,
state.sso_enabled
]);
const [auth_config, sso_enabled] = useServerApiState(
useShallow((state) => [state.auth_config, state.sso_enabled])
);
return (
<Stack>
@ -510,7 +510,9 @@ function MfaAddSection({
refetch: () => void;
showRecoveryCodes: (codes: Recoverycodes) => void;
}>) {
const [auth_config] = useServerApiState((state) => [state.auth_config]);
const [auth_config] = useServerApiState(
useShallow((state) => [state.auth_config])
);
const [totpQrOpen, { open: openTotpQr, close: closeTotpQr }] =
useDisclosure(false);
const [totpQr, setTotpQr] = useState<{ totp_url: string; secret: string }>();

View File

@ -17,6 +17,7 @@ import {
import { IconRestore } from '@tabler/icons-react';
import { useState } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { ColorToggle } from '../../../../components/items/ColorToggle';
import { LanguageSelect } from '../../../../components/items/LanguageSelect';
import { StylishText } from '../../../../components/items/StylishText';
@ -34,11 +35,9 @@ const LOOKUP = Object.assign(
export function UserTheme({ height }: Readonly<{ height: number }>) {
const theme = useMantineTheme();
const [userTheme, setTheme, setLanguage] = useLocalState((state) => [
state.userTheme,
state.setTheme,
state.setLanguage
]);
const [userTheme, setTheme, setLanguage] = useLocalState(
useShallow((state) => [state.userTheme, state.setTheme, state.setLanguage])
);
// radius
function getMark(value: number) {

View File

@ -4,6 +4,7 @@ import { Accordion, Alert, Stack } from '@mantine/core';
import { IconInfoCircle } from '@tabler/icons-react';
import { lazy } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { StylishText } from '../../../../components/items/StylishText';
import { GlobalSettingList } from '../../../../components/settings/SettingList';
import { Loadable } from '../../../../functions/loading';
@ -20,7 +21,7 @@ const PluginErrorTable = Loadable(
export default function PluginManagementPanel() {
const pluginsEnabled = useServerApiState(
(state) => state.server.plugins_enabled
useShallow((state) => state.server.plugins_enabled)
);
const user = useUserState();

View File

@ -18,6 +18,7 @@ import {
} from '@tabler/icons-react';
import { useMemo } from 'react';
import { useShallow } from 'zustand/react/shallow';
import PermissionDenied from '../../../components/errors/PermissionDenied';
import PageTitle from '../../../components/nav/PageTitle';
import { SettingsHeader } from '../../../components/nav/SettingsHeader';
@ -309,7 +310,7 @@ export default function SystemSettings() {
const user = useUserState();
const [server] = useServerApiState((state) => [state.server]);
const [server] = useServerApiState(useShallow((state) => [state.server]));
if (!user.isLoggedIn()) {
return <Skeleton />;

View File

@ -10,6 +10,7 @@ import {
} from '@tabler/icons-react';
import { useMemo } from 'react';
import { useShallow } from 'zustand/react/shallow';
import PageTitle from '../../../components/nav/PageTitle';
import { SettingsHeader } from '../../../components/nav/SettingsHeader';
import type { PanelType } from '../../../components/panels/Panel';
@ -23,10 +24,9 @@ import { AccountContent } from './AccountSettings/UserPanel';
* User settings page
*/
export default function UserSettings() {
const [user, isLoggedIn] = useUserState((state) => [
state.user,
state.isLoggedIn
]);
const [user, isLoggedIn] = useUserState(
useShallow((state) => [state.user, state.isLoggedIn])
);
const userSettingsPanels: PanelType[] = useMemo(() => {
return [

View File

@ -11,6 +11,7 @@ import { apiUrl } from '@lib/functions/Api';
import type { TableFilter } from '@lib/types/Filters';
import { showNotification } from '@mantine/notifications';
import { useNavigate } from 'react-router-dom';
import { useShallow } from 'zustand/react/shallow';
import { api } from '../../App';
import { AddItemButton } from '../../components/buttons/AddItemButton';
import { EditApiForm } from '../../components/forms/ApiForm';
@ -63,8 +64,7 @@ export function UserDrawer({
throwError: true
});
const currentUserPk = useUserState((s) => s.user?.pk);
const currentUserPk = useUserState(useShallow((s) => s.user?.pk));
const isCurrentUser = useMemo(
() => currentUserPk === Number.parseInt(id, 10),
[currentUserPk, id]

View File

@ -2,6 +2,7 @@ import { useEffect } from 'react';
import { BrowserRouter } from 'react-router-dom';
import { getBaseUrl } from '@lib/functions/Navigation';
import { useShallow } from 'zustand/react/shallow';
import { api, queryClient } from '../App';
import { ApiProvider } from '../contexts/ApiContext';
import { ThemeContext } from '../contexts/ThemeContext';
@ -10,7 +11,7 @@ import { routes } from '../router';
import { useLocalState } from '../states/LocalState';
export default function DesktopAppView() {
const [hostList] = useLocalState((state) => [state.hostList]);
const [hostList] = useLocalState(useShallow((state) => [state.hostList]));
useEffect(() => {
if (Object.keys(hostList).length === 0) {

View File

@ -1,6 +1,7 @@
import '@mantine/core/styles.css';
import { useViewportSize } from '@mantine/hooks';
import { lazy, useEffect } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { setApiDefaults } from '../App';
import { Loadable } from '../functions/loading';
@ -17,7 +18,9 @@ const DesktopAppView = Loadable(lazy(() => import('./DesktopAppView')));
// Main App
export default function MainView() {
const [allowMobile] = useLocalState((state) => [state.allowMobile]);
const [allowMobile] = useLocalState(
useShallow((state) => [state.allowMobile])
);
// Set initial login status
useEffect(() => {
try {

View File

@ -1,13 +1,16 @@
import { Trans } from '@lingui/react/macro';
import { Anchor, Center, Container, Stack, Text, Title } from '@mantine/core';
import { useShallow } from 'zustand/react/shallow';
import { ThemeContext } from '../contexts/ThemeContext';
import { docLinks } from '../defaults/links';
import { IS_DEV } from '../main';
import { useLocalState } from '../states/LocalState';
export default function MobileAppView() {
const [setAllowMobile] = useLocalState((state) => [state.setAllowMobile]);
const [setAllowMobile] = useLocalState(
useShallow((state) => [state.setAllowMobile])
);
function ignore() {
setAllowMobile(true);

View File

@ -5465,11 +5465,6 @@ use-sidecar@^1.1.2:
detect-node-es "^1.1.0"
tslib "^2.0.0"
use-sync-external-store@1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9"
integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==
util-deprecate@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@ -5712,9 +5707,7 @@ zod@^3.22.4:
resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
zustand@^4.5.5:
version "4.5.5"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.5.tgz#f8c713041543715ec81a2adda0610e1dc82d4ad1"
integrity sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==
dependencies:
use-sync-external-store "1.2.2"
zustand@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.3.tgz#b323435b73d06b2512e93c77239634374b0e407f"
integrity sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==