fix(striker-ui): remove protected state

According to breaking changes in react 18, hidden components cannot
trigger the useEffect hook.
See https://github.com/facebook/react/pull/22114

Since the warning is no longer triggered, we can safely remove the
workaround.
main
Tsu-ba-me 12 months ago
parent 32ed298d27
commit 83a0210ffa
  1. 6
      striker-ui/components/Files/AddFileForm.tsx
  2. 9
      striker-ui/components/Files/ManageFilePanel.tsx
  3. 11
      striker-ui/components/GateForm.tsx
  4. 10
      striker-ui/components/IconWithIndicator.tsx
  5. 3
      striker-ui/components/JobSummary.tsx
  6. 11
      striker-ui/components/ManageFence/ManageFencePanel.tsx
  7. 23
      striker-ui/components/ManageManifest/ManageManifestPanel.tsx
  8. 8
      striker-ui/components/ManageUps/ManageUpsPanel.tsx
  9. 7
      striker-ui/components/Network/Network.tsx
  10. 13
      striker-ui/components/PrepareHostForm.tsx
  11. 19
      striker-ui/components/PrepareNetworkForm.tsx
  12. 3
      striker-ui/components/StrikerConfig/AddPeerDialog.tsx
  13. 20
      striker-ui/components/StrikerConfig/ConfigPeersForm.tsx
  14. 17
      striker-ui/components/StrikerConfig/ManageChangedSSHKeysForm.tsx
  15. 7
      striker-ui/components/StrikerConfig/ManageUsersForm.tsx
  16. 11
      striker-ui/components/StrikerConfig/SimpleOperationsPanel.tsx
  17. 5
      striker-ui/components/StrikerInitForm.tsx
  18. 5
      striker-ui/hooks/useActiveFetch.ts
  19. 3
      striker-ui/hooks/useFormUtils.ts
  20. 30
      striker-ui/hooks/useProtect.ts
  21. 34
      striker-ui/hooks/useProtectedState.ts
  22. 11
      striker-ui/pages/config/index.tsx
  23. 8
      striker-ui/pages/manage-element/index.tsx

@ -6,6 +6,7 @@ import {
useCallback,
useMemo,
useRef,
useState,
} from 'react';
import { v4 as uuidv4 } from 'uuid';
@ -20,7 +21,6 @@ import MessageBox from '../MessageBox';
import MessageGroup from '../MessageGroup';
import fileListSchema from './schema';
import UploadFileProgress from './UploadFileProgress';
import useProtectedState from '../../hooks/useProtectedState';
const REQUEST_INCOMPLETE_UPLOAD_LIMIT = 99;
@ -41,9 +41,7 @@ const AddFileForm: FC<AddFileFormProps> = (props) => {
const filePickerRef = useRef<HTMLInputElement>(null);
const [uploads, setUploads] = useProtectedState<UploadFiles | undefined>(
undefined,
);
const [uploads, setUploads] = useState<UploadFiles | undefined>();
const formik = useFormik<FileFormikValues>({
initialValues: {},

@ -22,7 +22,6 @@ import useActiveFetch from '../../hooks/useActiveFetch';
import useChecklist from '../../hooks/useChecklist';
import useConfirmDialogProps from '../../hooks/useConfirmDialogProps';
import useFetch from '../../hooks/useFetch';
import useProtectedState from '../../hooks/useProtectedState';
const toFileOverviewList = (rows: string[][]) =>
rows.reduce<APIFileOverviewList>((previous, row) => {
@ -115,12 +114,8 @@ const ManageFilePanel: FC = () => {
const [confirmDialogProps, setConfirmDialogProps] = useConfirmDialogProps();
const [edit, setEdit] = useState<boolean>(false);
const [file, setFile] = useProtectedState<APIFileDetail | undefined>(
undefined,
);
const [files, setFiles] = useProtectedState<APIFileOverviewList | undefined>(
undefined,
);
const [file, setFile] = useState<APIFileDetail | undefined>();
const [files, setFiles] = useState<APIFileOverviewList | undefined>();
const { isLoading: loadingFilesPeriodic } = periodicFetch<string[][]>(
`${API_BASE_URL}/file`,

@ -1,5 +1,11 @@
import { Box, BoxProps, SxProps, Theme } from '@mui/material';
import { forwardRef, useImperativeHandle, useMemo, useRef } from 'react';
import {
forwardRef,
useImperativeHandle,
useMemo,
useRef,
useState,
} from 'react';
import INPUT_TYPES from '../lib/consts/INPUT_TYPES';
@ -12,7 +18,6 @@ import OutlinedInputWithLabel from './OutlinedInputWithLabel';
import Spinner from './Spinner';
import { buildPeacefulStringTestBatch } from '../lib/test_input';
import useFormUtils from '../hooks/useFormUtils';
import useProtectedState from '../hooks/useProtectedState';
const INPUT_ROOT_SX: SxProps<Theme> = { width: '100%' };
@ -67,7 +72,7 @@ const GateForm = forwardRef<GateFormForwardedRefContent, GateFormProps>(
const inputPassphraseRef = useRef<InputForwardedRefContent<'string'>>({});
const messageGroupRef = useRef<MessageGroupForwardedRefContent>({});
const [isSubmitting, setIsSubmitting] = useProtectedState<boolean>(false);
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
const formUtils = useFormUtils(
[INPUT_ID_GATE_ID, INPUT_ID_GATE_PASSPHRASE],

@ -11,14 +11,13 @@ import {
useCallback,
useImperativeHandle,
useMemo,
useState,
} from 'react';
import { BLACK, BLUE } from '../lib/consts/DEFAULT_THEME';
import FlexBox, { FlexBoxProps } from './FlexBox';
import { BodyText, BodyTextProps } from './Text';
import useProtect from '../hooks/useProtect';
import useProtectedState from '../hooks/useProtectedState';
type IndicatorValue = boolean | number;
@ -79,10 +78,9 @@ const IconWithIndicator = forwardRef<
},
ref,
) => {
const { protect } = useProtect();
const [indicatorValue, setIndicatorValue] = useProtectedState<
boolean | number
>(initialIndicatorValue, protect);
const [indicatorValue, setIndicatorValue] = useState<boolean | number>(
initialIndicatorValue,
);
const buildIndicator = useCallback(
(

@ -8,7 +8,6 @@ import FlexBox from './FlexBox';
import List from './List';
import periodicFetch from '../lib/fetchers/periodicFetch';
import { BodyText } from './Text';
import useProtectedState from '../hooks/useProtectedState';
type AnvilJobs = {
[jobUUID: string]: {
@ -58,7 +57,7 @@ const JobSummary = forwardRef<JobSummaryForwardedRefContent, JobSummaryProps>(
},
ref,
) => {
const [anvilJobs, setAnvilJobs] = useProtectedState<AnvilJobs>({});
const [anvilJobs, setAnvilJobs] = useState<AnvilJobs>({});
const [isOpenJobSummary, setIsOpenJobSummary] =
useState<boolean>(openInitially);
const [menuAnchorElement, setMenuAnchorElement] = useState<

@ -29,7 +29,6 @@ import useChecklist from '../../hooks/useChecklist';
import useConfirmDialogProps from '../../hooks/useConfirmDialogProps';
import useFormUtils from '../../hooks/useFormUtils';
import useIsFirstRender from '../../hooks/useIsFirstRender';
import useProtectedState from '../../hooks/useProtectedState';
type FenceFormData = {
agent: string;
@ -130,15 +129,15 @@ const ManageFencePanel: FC = () => {
const [confirmDialogProps, setConfirmDialogProps] = useConfirmDialogProps();
const [formDialogProps, setFormDialogProps] = useConfirmDialogProps();
const [fenceOverviews, setFenceOverviews] = useProtectedState<
const [fenceOverviews, setFenceOverviews] = useState<
APIFenceOverview | undefined
>(undefined);
const [fenceTemplate, setFenceTemplate] = useProtectedState<
>();
const [fenceTemplate, setFenceTemplate] = useState<
APIFenceTemplate | undefined
>(undefined);
>();
const [isEditFences, setIsEditFences] = useState<boolean>(false);
const [isLoadingFenceTemplate, setIsLoadingFenceTemplate] =
useProtectedState<boolean>(true);
useState<boolean>(true);
const { isLoading: isFenceOverviewsLoading } =
periodicFetch<APIFenceOverview>(`${API_BASE_URL}/fence`, {

@ -45,7 +45,6 @@ import useChecklist from '../../hooks/useChecklist';
import useConfirmDialogProps from '../../hooks/useConfirmDialogProps';
import useFormUtils from '../../hooks/useFormUtils';
import useIsFirstRender from '../../hooks/useIsFirstRender';
import useProtectedState from '../../hooks/useProtectedState';
const REQ_BODY_MAX_DEPTH = 6;
@ -155,25 +154,25 @@ const ManageManifestPanel: FC = () => {
const [confirmDialogProps, setConfirmDialogProps] = useConfirmDialogProps();
const [hostOverviews, setHostOverviews] = useProtectedState<
const [hostOverviews, setHostOverviews] = useState<
APIHostOverviewList | undefined
>(undefined);
>();
const [isEditManifests, setIsEditManifests] = useState<boolean>(false);
const [isLoadingHostOverviews, setIsLoadingHostOverviews] =
useProtectedState<boolean>(true);
useState<boolean>(true);
const [isLoadingManifestDetail, setIsLoadingManifestDetail] =
useProtectedState<boolean>(true);
useState<boolean>(true);
const [isLoadingManifestTemplate, setIsLoadingManifestTemplate] =
useProtectedState<boolean>(true);
const [manifestOverviews, setManifestOverviews] = useProtectedState<
useState<boolean>(true);
const [manifestOverviews, setManifestOverviews] = useState<
APIManifestOverviewList | undefined
>(undefined);
const [manifestDetail, setManifestDetail] = useProtectedState<
>();
const [manifestDetail, setManifestDetail] = useState<
APIManifestDetail | undefined
>(undefined);
const [manifestTemplate, setManifestTemplate] = useProtectedState<
>();
const [manifestTemplate, setManifestTemplate] = useState<
APIManifestTemplate | undefined
>(undefined);
>();
const { isLoading: isLoadingManifestOverviews } =
periodicFetch<APIManifestOverviewList>(`${API_BASE_URL}/manifest`, {

@ -27,7 +27,6 @@ import useChecklist from '../../hooks/useChecklist';
import useConfirmDialogProps from '../../hooks/useConfirmDialogProps';
import useFormUtils from '../../hooks/useFormUtils';
import useIsFirstRender from '../../hooks/useIsFirstRender';
import useProtectedState from '../../hooks/useProtectedState';
type UpsFormData = {
agent: string;
@ -115,12 +114,11 @@ const ManageUpsPanel: FC = () => {
const [confirmDialogProps, setConfirmDialogProps] = useConfirmDialogProps();
const [formDialogProps, setFormDialogProps] = useConfirmDialogProps();
const [isEditUpses, setIsEditUpses] = useState<boolean>(false);
const [isLoadingUpsTemplate, setIsLoadingUpsTemplate] =
useProtectedState<boolean>(true);
const [upsTemplate, setUpsTemplate] = useProtectedState<
APIUpsTemplate | undefined
>(undefined);
useState<boolean>(true);
const [upsTemplate, setUpsTemplate] = useState<APIUpsTemplate | undefined>();
const { data: upsOverviews, isLoading: isUpsOverviewLoading } =
periodicFetch<APIUpsOverview>(`${API_BASE_URL}/ups`, {

@ -1,5 +1,5 @@
import { Box, Divider, styled } from '@mui/material';
import { useContext } from 'react';
import { useContext, useState } from 'react';
import API_BASE_URL from '../../lib/consts/API_BASE_URL';
import {
@ -14,7 +14,6 @@ import periodicFetch from '../../lib/fetchers/periodicFetch';
import processNetworkData from './processNetwork';
import Spinner from '../Spinner';
import { HeaderText, BodyText } from '../Text';
import useProtectedState from '../../hooks/useProtectedState';
const PREFIX = 'Network';
@ -72,9 +71,7 @@ const selectDecorator = (state: string): Colours => {
const Network = (): JSX.Element => {
const { uuid } = useContext(AnvilContext);
const [processed, setProcessed] = useProtectedState<
ProcessedNetwork | undefined
>(undefined);
const [processed, setProcessed] = useState<ProcessedNetwork | undefined>();
const { isLoading } = periodicFetch<AnvilNetwork>(
`${API_BASE_URL}/anvil/${uuid}/network`,

@ -31,8 +31,6 @@ import { Panel, PanelHeader } from './Panels';
import RadioGroupWithLabel from './RadioGroupWithLabel';
import Spinner from './Spinner';
import { BodyText, HeaderText, MonoText } from './Text';
import useProtect from '../hooks/useProtect';
import useProtectedState from '../hooks/useProtectedState';
const ENTERPRISE_KEY_LABEL = 'Alteeve enterprise key';
const HOST_IP_LABEL = 'Host IP address';
@ -55,8 +53,6 @@ const GRID_COLUMNS: Exclude<GridProps['columns'], undefined> = {
const GRID_SPACING: Exclude<GridProps['spacing'], undefined> = '1em';
const PrepareHostForm: FC = () => {
const { protect } = useProtect();
const confirmDialogRef = useRef<ConfirmDialogForwardedRefContent>({});
const inputEnterpriseKeyRef = useRef<InputForwardedRefContent<'string'>>({});
const inputHostNameRef = useRef<InputForwardedRefContent<'string'>>({});
@ -77,13 +73,10 @@ const PrepareHostForm: FC = () => {
const [connectedHostIPAddress, setConnectedHostIPAddress] = useState<
string | undefined
>();
const [connectedHostPassword, setConnectedHostPassword] = useProtectedState<
const [connectedHostPassword, setConnectedHostPassword] = useState<
string | undefined
>(undefined, protect);
const [connectedHostUUID, setConnectedHostUUID] = useProtectedState<string>(
'',
protect,
);
>();
const [connectedHostUUID, setConnectedHostUUID] = useState<string>('');
const [inputHostType, setInputHostType] = useState<string>('');
const [isInputEnterpriseKeyValid, setIsInputEnterpriseKeyValid] =
useState<boolean>(true);

@ -1,5 +1,5 @@
import { useRouter } from 'next/router';
import { FC, useCallback, useEffect, useMemo, useRef } from 'react';
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import api from '../lib/api';
import ConfirmDialog from './ConfirmDialog';
@ -22,7 +22,6 @@ import { buildPeacefulStringTestBatch } from '../lib/test_input';
import { HeaderText } from './Text';
import useConfirmDialogProps from '../hooks/useConfirmDialogProps';
import useFormUtils from '../hooks/useFormUtils';
import useProtectedState from '../hooks/useProtectedState';
const INPUT_ID_PREP_NET_HOST_NAME = 'prepare-network-host-name-input';
@ -107,16 +106,14 @@ const PrepareNetworkForm: FC<PrepareNetworkFormProps> = ({
const [confirmDialogProps, setConfirmDialogProps] = useConfirmDialogProps();
const [hostDetail, setHostDetail] = useProtectedState<
APIHostDetail | undefined
>(undefined);
const [fatalErrorMessage, setFatalErrorMessage] = useProtectedState<
const [hostDetail, setHostDetail] = useState<APIHostDetail | undefined>();
const [fatalErrorMessage, setFatalErrorMessage] = useState<
Message | undefined
>(undefined);
const [isLoadingHostDetail, setIsLoadingHostDetail] =
useProtectedState<boolean>(true);
const [previousHostUUID, setPreviousHostUUID] =
useProtectedState<PrepareNetworkFormProps['hostUUID']>(undefined);
>();
const [isLoadingHostDetail, setIsLoadingHostDetail] = useState<boolean>(true);
const [previousHostUUID, setPreviousHostUUID] = useState<
string | undefined
>();
const {
buildFinishInputTestBatchFunction,

@ -27,7 +27,6 @@ import {
buildPeacefulStringTestBatch,
} from '../../lib/test_input';
import { HeaderText } from '../Text';
import useProtectedState from '../../hooks/useProtectedState';
const IT_IDS = {
dbPort: 'dbPort',
@ -61,7 +60,7 @@ const AddPeerDialog = forwardRef<
}>({});
const [isEnablePingTest, setIsEnablePingTest] = useState<boolean>(false);
const [isSubmittingAddPeer, setIsSubmittingAddPeer] =
useProtectedState<boolean>(false);
useState<boolean>(false);
const buildInputFirstRenderFunction = useCallback(
(key: string) =>

@ -15,21 +15,14 @@ import { ExpandablePanel } from '../Panels';
import periodicFetch from '../../lib/fetchers/periodicFetch';
import State from '../State';
import { BodyText, MonoText, SmallText } from '../Text';
import useProtect from '../../hooks/useProtect';
import useProtectedState from '../../hooks/useProtectedState';
const ConfigPeersForm: FC<ConfigPeerFormProps> = ({
refreshInterval = 60000,
}) => {
const { protect } = useProtect();
const addPeerDialogRef = useRef<ConfirmDialogForwardedRefContent>({});
const confirmDialogRef = useRef<ConfirmDialogForwardedRefContent>({});
const [apiMessage, setAPIMessage] = useProtectedState<Message | undefined>(
undefined,
protect,
);
const [apiMessage, setApiMessage] = useState<Message | undefined>(undefined);
const [confirmDialogProps, setConfirmDialogProps] =
useState<ConfirmDialogProps>({
actionProceedText: '',
@ -37,11 +30,12 @@ const ConfigPeersForm: FC<ConfigPeerFormProps> = ({
titleText: '',
});
const [inboundConnections, setInboundConnections] =
useProtectedState<InboundConnectionList>({}, protect);
useState<InboundConnectionList>({});
const [isEditPeerConnections, setIsEditPeerConnections] =
useState<boolean>(false);
const [peerConnections, setPeerConnections] =
useProtectedState<PeerConnectionList>({}, protect);
const [peerConnections, setPeerConnections] = useState<PeerConnectionList>(
{},
);
const apiMessageElement = useMemo(
() =>
@ -58,7 +52,7 @@ const ConfigPeersForm: FC<ConfigPeerFormProps> = ({
{
refreshInterval,
onError: (error) => {
setAPIMessage({
setApiMessage({
children: `Failed to get connection data. Error: ${error}`,
type: 'error',
});
@ -190,7 +184,7 @@ const ConfigPeersForm: FC<ConfigPeerFormProps> = ({
emsg.children = `Failed to delete peer connection(s). ${emsg.children}`;
setAPIMessage(emsg);
setApiMessage(emsg);
});
},
proceedColour: 'red',

@ -1,4 +1,4 @@
import { FC, useMemo, useRef } from 'react';
import { FC, useMemo, useRef, useState } from 'react';
import API_BASE_URL from '../../lib/consts/API_BASE_URL';
@ -14,7 +14,6 @@ import { ExpandablePanel } from '../Panels';
import periodicFetch from '../../lib/fetchers/periodicFetch';
import { BodyText } from '../Text';
import useChecklist from '../../hooks/useChecklist';
import useProtectedState from '../../hooks/useProtectedState';
const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({
mitmExternalHref = 'https://en.wikipedia.org/wiki/Man-in-the-middle_attack',
@ -22,14 +21,10 @@ const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({
}) => {
const confirmDialogRef = useRef<ConfirmDialogForwardedRefContent>({});
const [apiMessage, setAPIMessage] = useProtectedState<Message | undefined>(
undefined,
);
const [changedSSHKeys, setChangedSSHKeys] = useProtectedState<ChangedSSHKeys>(
{},
);
const [apiMessage, setApiMessage] = useState<Message | undefined>();
const [changedSSHKeys, setChangedSSHKeys] = useState<ChangedSSHKeys>({});
const [confirmDialogProps, setConfirmDialogProps] =
useProtectedState<ConfirmDialogProps>({
useState<ConfirmDialogProps>({
actionProceedText: '',
content: '',
titleText: '',
@ -51,7 +46,7 @@ const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({
`${API_BASE_URL}/ssh-key/conflict`,
{
onError: (error) => {
setAPIMessage({
setApiMessage({
children: `Failed to fetch SSH key conflicts. Error: ${error}`,
type: 'error',
});
@ -177,7 +172,7 @@ const ManageChangedSSHKeysForm: FC<ManageChangedSSHKeysFormProps> = ({
emsg.children = `Failed to delete selected SSH key conflicts. ${emsg.children}`;
setAPIMessage(emsg);
setApiMessage(emsg);
});
},
proceedColour: 'red',

@ -20,7 +20,6 @@ import { BodyText } from '../Text';
import useChecklist from '../../hooks/useChecklist';
import useConfirmDialogProps from '../../hooks/useConfirmDialogProps';
import useFormUtils from '../../hooks/useFormUtils';
import useProtectedState from '../../hooks/useProtectedState';
const getFormEntries = (
...[{ target }]: DivFormEventHandlerParameters
@ -51,12 +50,12 @@ const ManageUsersForm: FC = () => {
const [confirmDialogProps, setConfirmDialogProps] = useConfirmDialogProps();
const [editUsers, setEditUsers] = useState<boolean>(false);
const [listMessage, setListMessage] = useProtectedState<Message>({
const [listMessage, setListMessage] = useState<Message>({
children: `No users found.`,
});
const [userDetail, setUserDetail] = useProtectedState<
const [userDetail, setUserDetail] = useState<
UserOverviewMetadata | undefined
>(undefined);
>();
const { data: users, isLoading: loadingUsers } =
periodicFetch<UserOverviewMetadataList>(`${API_BASE_URL}/user`, {

@ -1,5 +1,5 @@
import { Grid, Switch } from '@mui/material';
import { FC, useMemo } from 'react';
import { FC, useMemo, useState } from 'react';
import api from '../../lib/api';
import ContainedButton from '../ContainedButton';
@ -9,8 +9,6 @@ import MessageBox, { Message } from '../MessageBox';
import { Panel, PanelHeader } from '../Panels';
import Spinner from '../Spinner';
import { BodyText, HeaderText } from '../Text';
import useProtect from '../../hooks/useProtect';
import useProtectedState from '../../hooks/useProtectedState';
const StretchedButton: FC<ContainedButtonProps> = (props) => (
<ContainedButton {...props} sx={{ width: '100%' }} />
@ -21,12 +19,7 @@ const SimpleOperationsPanel: FC<SimpleOperationsPanelProps> = ({
onSubmit,
title,
}) => {
const { protect } = useProtect();
const [message, setMessage] = useProtectedState<Message | undefined>(
undefined,
protect,
);
const [message, setMessage] = useState<Message | undefined>();
const headerElement = useMemo(
() =>

@ -30,7 +30,6 @@ import { Panel, PanelHeader } from './Panels';
import setMapNetwork from '../lib/setMapNetwork';
import Spinner from './Spinner';
import { BodyText, HeaderText, InlineMonoText, MonoText } from './Text';
import useProtectedState from '../hooks/useProtectedState';
const StrikerInitForm: FC = () => {
const {
@ -51,9 +50,7 @@ const StrikerInitForm: FC = () => {
const [isSubmittingForm, setIsSubmittingForm] = useState<boolean>(false);
const [hostNumber, setHostNumber] = useState<string | undefined>();
const [hostDetail, setHostDetail] = useProtectedState<
APIHostDetail | undefined
>(undefined);
const [hostDetail, setHostDetail] = useState<APIHostDetail | undefined>();
const allowGetHostDetail = useRef<boolean>(true);

@ -1,8 +1,7 @@
import { useCallback } from 'react';
import { useCallback, useState } from 'react';
import api from '../lib/api';
import handleAPIError from '../lib/handleAPIError';
import useProtectedState from './useProtectedState';
type ActiveFetchSetter<T> = (data: T) => void;
@ -22,7 +21,7 @@ const useActiveFetch = <Data>(
): ActiveFetchHookResponse => {
const { onError, onData, url: urlPrefix = '' } = options;
const [loading, setLoading] = useProtectedState<boolean>(false);
const [loading, setLoading] = useState<boolean>(false);
const fetch = useCallback<ActiveFetcher>(
(urlPostfix = '') => {

@ -7,7 +7,6 @@ import buildObjectStateSetterCallback, {
import handleAPIError from '../lib/handleAPIError';
import { Message } from '../components/MessageBox';
import { MessageGroupForwardedRefContent } from '../components/MessageGroup';
import useProtectedState from './useProtectedState';
const useFormUtils = <
U extends string,
@ -17,7 +16,7 @@ const useFormUtils = <
ids: I,
messageGroupRef: MutableRefObject<MessageGroupForwardedRefContent>,
): FormUtils<M> => {
const [formSubmitting, setFormSubmitting] = useProtectedState<boolean>(false);
const [formSubmitting, setFormSubmitting] = useState<boolean>(false);
const [formValidity, setFormValidity] = useState<FormValidity<M>>({});
const setApiMessage = useCallback(

@ -1,30 +0,0 @@
import { useEffect, useRef } from 'react';
// Allow any function as callback in the protect function.
// Could be used to wrap async callbacks to prevent them from running after
// component unmount.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type AnyFunction = (...args: any[]) => any;
type ProtectFunction = <F extends AnyFunction>(
fn: F,
...args: Parameters<F>
) => ReturnType<F>;
const useProtect = (): { protect: ProtectFunction } => {
const isComponentMountedRef = useRef<boolean>(true);
useEffect(
() => () => {
isComponentMountedRef.current = false;
},
[],
);
return {
protect: (fn, ...args) =>
isComponentMountedRef.current ? fn(...args) : undefined,
};
};
export default useProtect;

@ -1,34 +0,0 @@
import { Dispatch, SetStateAction, useMemo, useState } from 'react';
import useProtect from './useProtect';
type SetStateFunction<S> = Dispatch<SetStateAction<S>>;
type SetStateParameters<S> = Parameters<SetStateFunction<S>>;
type SetStateReturnType<S> = ReturnType<SetStateFunction<S>>;
const useProtectedState = <S>(
initialState: S | (() => S),
protect?: (
fn: SetStateFunction<S>,
...args: SetStateParameters<S>
) => SetStateReturnType<S>,
): [S, SetStateFunction<S>] => {
const { protect: defaultProtect } = useProtect();
const [state, setState] = useState<S>(initialState);
const pfn = useMemo(
() => protect ?? defaultProtect,
[defaultProtect, protect],
);
return [
state,
(...args: SetStateParameters<S>): SetStateReturnType<S> =>
pfn(setState, ...args),
];
};
export default useProtectedState;

@ -11,16 +11,12 @@ import {
ComplexOperationsPanel,
SimpleOperationsPanel,
} from '../../components/StrikerConfig';
import useProtect from '../../hooks/useProtect';
import useProtectedState from '../../hooks/useProtectedState';
// This page can't be reused, and default is set within the render function.
// eslint-disable-next-line react/require-default-props
const Config: FC<{ refreshInterval?: number }> = ({
refreshInterval = 60000,
}) => {
const { protect } = useProtect();
const [isOpenConfirmDialog, setIsOpenConfirmDialog] =
useState<boolean>(false);
const [confirmDialogProps, setConfirmDialogProps] =
@ -37,11 +33,10 @@ const Config: FC<{ refreshInterval?: number }> = ({
},
titleText: '',
});
const [simpleOpsInstallTarget, setSimpleOpsInstallTarget] = useProtectedState<
const [simpleOpsInstallTarget, setSimpleOpsInstallTarget] = useState<
APIHostInstallTarget | undefined
>(undefined, protect);
const [simpleOpsPanelHeader, setSimpleOpsPanelHeader] =
useProtectedState<string>('', protect);
>();
const [simpleOpsPanelHeader, setSimpleOpsPanelHeader] = useState<string>('');
const { data: hostDetail, isLoading: loadingHostDetail } =
periodicFetch<APIHostDetail>(`${API_BASE_URL}/host/local`, {

@ -18,8 +18,6 @@ import Tab from '../../components/Tab';
import TabContent from '../../components/TabContent';
import Tabs from '../../components/Tabs';
import useIsFirstRender from '../../hooks/useIsFirstRender';
import useProtect from '../../hooks/useProtect';
import useProtectedState from '../../hooks/useProtectedState';
const TAB_ID_PREPARE_HOST = 'prepare-host';
const TAB_ID_PREPARE_NETWORK = 'prepare-network';
@ -54,11 +52,9 @@ const PrepareHostTabContent: FC = () => (
const PrepareNetworkTabContent: FC = () => {
const isFirstRender = useIsFirstRender();
const { protect } = useProtect();
const [hostOverviewList, setHostOverviewList] = useProtectedState<
const [hostOverviewList, setHostOverviewList] = useState<
APIHostOverviewList | undefined
>(undefined, protect);
>();
const [hostSubTabId, setHostSubTabId] = useState<string | false>(false);
const hostSubTabs = useMemo(() => {

Loading…
Cancel
Save