chore(ws): enforce named imports for react hooks (#414)
Signed-off-by: paulovmr <832830+paulovmr@users.noreply.github.com>
This commit is contained in:
parent
cd02eb46c6
commit
99538a7f81
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
const noReactHookNamespace = require('./eslint-local-rules/no-react-hook-namespace');
|
||||
|
||||
module.exports = {
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
"browser": true,
|
||||
|
@ -216,7 +218,8 @@
|
|||
"no-useless-return": "error",
|
||||
"symbol-description": "error",
|
||||
"yoda": "error",
|
||||
"func-names": "warn"
|
||||
"func-names": "warn",
|
||||
"no-react-hook-namespace": "error"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
|
@ -262,6 +265,12 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.{js,jsx,ts,tsx}'],
|
||||
rules: {
|
||||
'no-react-hook-namespace': 'error',
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Disallow using React hooks through React namespace',
|
||||
},
|
||||
messages: {
|
||||
avoidNamespaceHook: 'Import React hook "{{hook}}" directly instead of using React.{{hook}}.',
|
||||
},
|
||||
schema: [],
|
||||
},
|
||||
create(context) {
|
||||
const hooks = new Set([
|
||||
'useState', 'useEffect', 'useContext', 'useReducer',
|
||||
'useCallback', 'useMemo', 'useRef', 'useLayoutEffect',
|
||||
'useImperativeHandle', 'useDebugValue', 'useDeferredValue',
|
||||
'useTransition', 'useId', 'useSyncExternalStore',
|
||||
]);
|
||||
return {
|
||||
MemberExpression(node) {
|
||||
if (
|
||||
node.object?.name === 'React' &&
|
||||
hooks.has(node.property?.name)
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'avoidNamespaceHook',
|
||||
data: { hook: node.property.name },
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
|
@ -24,8 +24,8 @@
|
|||
"test:unit": "npm run test:jest -- --silent",
|
||||
"test:watch": "jest --watch",
|
||||
"test:coverage": "jest --coverage",
|
||||
"test:fix": "eslint --ext .js,.ts,.jsx,.tsx ./src --fix",
|
||||
"test:lint": "eslint --max-warnings 0 --ext .js,.ts,.jsx,.tsx ./src",
|
||||
"test:fix": "eslint --rulesdir eslint-local-rules --ext .js,.ts,.jsx,.tsx ./src --fix",
|
||||
"test:lint": "eslint --rulesdir eslint-local-rules --max-warnings 0 --ext .js,.ts,.jsx,.tsx ./src",
|
||||
"cypress:open": "cypress open --project src/__tests__/cypress",
|
||||
"cypress:open:mock": "CY_MOCK=1 CY_WS_PORT=9002 npm run cypress:open -- ",
|
||||
"cypress:run": "cypress run -b chrome --project src/__tests__/cypress",
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import * as React from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { createComparativeValue, renderHook, standardUseFetchState, testHook } from './hooks';
|
||||
|
||||
const useSayHello = (who: string, showCount = false) => {
|
||||
const countRef = React.useRef(0);
|
||||
const countRef = useRef(0);
|
||||
countRef.current++;
|
||||
return `Hello ${who}!${showCount && countRef.current > 1 ? ` x${countRef.current}` : ''}`;
|
||||
};
|
||||
|
||||
const useSayHelloDelayed = (who: string, delay = 0) => {
|
||||
const [speech, setSpeech] = React.useState('');
|
||||
React.useEffect(() => {
|
||||
const [speech, setSpeech] = useState('');
|
||||
useEffect(() => {
|
||||
const handle = setTimeout(() => setSpeech(`Hello ${who}!`), delay);
|
||||
return () => clearTimeout(handle);
|
||||
}, [who, delay]);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import '@patternfly/react-core/dist/styles/base.css';
|
||||
import './app.css';
|
||||
import {
|
||||
|
@ -26,7 +26,7 @@ import { isMUITheme, Theme } from './const';
|
|||
import { BrowserStorageContextProvider } from './context/BrowserStorageContext';
|
||||
|
||||
const App: React.FC = () => {
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
// Apply the theme based on the value of STYLE_THEME
|
||||
if (isMUITheme()) {
|
||||
document.documentElement.classList.add(Theme.MUI);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { Route, Routes, Navigate } from 'react-router-dom';
|
||||
import { AppRoutePaths } from '~/app/routes';
|
||||
import { WorkspaceForm } from '~/app/pages/Workspaces/Form/WorkspaceForm';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { Bullseye, Spinner } from '@patternfly/react-core';
|
||||
import { useNotebookAPI } from './hooks/useNotebookAPI';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { NavLink, useLocation } from 'react-router-dom';
|
||||
import {
|
||||
Brand,
|
||||
|
@ -29,7 +29,7 @@ const NavHref: React.FC<{ item: NavDataHref }> = ({ item }) => {
|
|||
|
||||
const NavGroup: React.FC<{ item: NavDataGroup }> = ({ item }) => {
|
||||
const { children } = item;
|
||||
const [expanded, setExpanded] = React.useState(false);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
return (
|
||||
<NavExpandable
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { SearchInput, SearchInputProps, TextInput } from '@patternfly/react-core';
|
||||
import FormFieldset from 'app/components/FormFieldset';
|
||||
import { isMUITheme } from 'app/const';
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
import React, { createContext, useCallback, useContext, useEffect } from 'react';
|
||||
import React, {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
export interface BrowserStorageContextType {
|
||||
getValue: (key: string) => unknown;
|
||||
|
@ -17,8 +25,8 @@ const BrowserStorageContext = createContext<BrowserStorageContextType>({
|
|||
export const BrowserStorageContextProvider: React.FC<BrowserStorageContextProviderProps> = ({
|
||||
children,
|
||||
}) => {
|
||||
const [values, setValues] = React.useState<{ [key: string]: unknown }>({});
|
||||
const valuesRef = React.useRef(values);
|
||||
const [values, setValues] = useState<{ [key: string]: unknown }>({});
|
||||
const valuesRef = useRef(values);
|
||||
useEffect(() => {
|
||||
valuesRef.current = values;
|
||||
}, [values]);
|
||||
|
@ -49,7 +57,7 @@ export const BrowserStorageContextProvider: React.FC<BrowserStorageContextProvid
|
|||
);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const contextValue = React.useMemo(() => ({ getValue, setValue }), [getValue, setValue, values]);
|
||||
const contextValue = useMemo(() => ({ getValue, setValue }), [getValue, setValue, values]);
|
||||
|
||||
return (
|
||||
<BrowserStorageContext.Provider value={contextValue}>{children}</BrowserStorageContext.Provider>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useContext, ReactNode, useMemo, useCallback } from 'react';
|
||||
import React, { ReactNode, useCallback, useContext, useMemo, useState } from 'react';
|
||||
import useMount from '~/app/hooks/useMount';
|
||||
import useNamespaces from '~/app/hooks/useNamespaces';
|
||||
import { useStorage } from './BrowserStorageContext';
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import React, { ReactNode, useMemo } from 'react';
|
||||
import { BFF_API_VERSION } from '~/app/const';
|
||||
import EnsureAPIAvailability from '~/app/EnsureAPIAvailability';
|
||||
import useNotebookAPIState, { NotebookAPIState } from './useNotebookAPIState';
|
||||
|
@ -26,7 +25,7 @@ export const NotebookContextProvider: React.FC<NotebookContextProviderProps> = (
|
|||
|
||||
return (
|
||||
<NotebookContext.Provider
|
||||
value={React.useMemo(
|
||||
value={useMemo(
|
||||
() => ({
|
||||
apiState,
|
||||
refreshAPIState,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { NotebookAPIs } from '~/shared/api/notebookApi';
|
||||
import {
|
||||
createWorkspace,
|
||||
|
@ -48,7 +48,7 @@ const MOCK_API_ENABLED = process.env.WEBPACK_REPLACE__mockApiEnabled === 'true';
|
|||
const useNotebookAPIState = (
|
||||
hostPath: string | null,
|
||||
): [apiState: NotebookAPIState, refreshAPIState: () => void] => {
|
||||
const createApi = React.useCallback(
|
||||
const createApi = useCallback(
|
||||
(path: string): NotebookAPIs => ({
|
||||
// Health
|
||||
getHealthCheck: getHealthCheck(path),
|
||||
|
@ -75,7 +75,7 @@ const useNotebookAPIState = (
|
|||
[],
|
||||
);
|
||||
|
||||
const createMockApi = React.useCallback(
|
||||
const createMockApi = useCallback(
|
||||
(path: string): NotebookAPIs => ({
|
||||
// Health
|
||||
getHealthCheck: mockGetHealthCheck(path),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button, Split, SplitItem, Title } from '@patternfly/react-core';
|
||||
import { TimesIcon } from '@patternfly/react-icons';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
ClipboardCopy,
|
||||
ClipboardCopyVariant,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
EmptyState,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
|
||||
export type UpdateObjectAtPropAndValue<T> = <K extends keyof T>(
|
||||
propKey: K,
|
||||
|
@ -13,9 +13,9 @@ export type GenericObjectState<T> = [
|
|||
];
|
||||
|
||||
const useGenericObjectState = <T>(defaultData: T | (() => T)): GenericObjectState<T> => {
|
||||
const [value, setValue] = React.useState<T>(defaultData);
|
||||
const [value, setValue] = useState<T>(defaultData);
|
||||
|
||||
const setPropValue = React.useCallback<UpdateObjectAtPropAndValue<T>>((propKey, propValue) => {
|
||||
const setPropValue = useCallback<UpdateObjectAtPropAndValue<T>>((propKey, propValue) => {
|
||||
setValue((oldValue) => {
|
||||
if (oldValue[propKey] !== propValue) {
|
||||
return { ...oldValue, [propKey]: propValue };
|
||||
|
@ -24,12 +24,12 @@ const useGenericObjectState = <T>(defaultData: T | (() => T)): GenericObjectStat
|
|||
});
|
||||
}, []);
|
||||
|
||||
const defaultDataRef = React.useRef(value);
|
||||
const resetToDefault = React.useCallback(() => {
|
||||
const defaultDataRef = useRef(value);
|
||||
const resetToDefault = useCallback(() => {
|
||||
setValue(defaultDataRef.current);
|
||||
}, []);
|
||||
|
||||
const replace = React.useCallback((newValue: T) => {
|
||||
const replace = useCallback((newValue: T) => {
|
||||
setValue(newValue);
|
||||
}, []);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import useFetchState, {
|
||||
FetchState,
|
||||
FetchStateCallbackPromise,
|
||||
|
@ -9,7 +9,7 @@ import { Namespace } from '~/shared/api/backendApiTypes';
|
|||
const useNamespaces = (): FetchState<Namespace[] | null> => {
|
||||
const { api, apiAvailable } = useNotebookAPI();
|
||||
|
||||
const call = React.useCallback<FetchStateCallbackPromise<Namespace[] | null>>(
|
||||
const call = useCallback<FetchStateCallbackPromise<Namespace[] | null>>(
|
||||
(opts) => {
|
||||
if (!apiAvailable) {
|
||||
return Promise.reject(new Error('API not yet available'));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { NotebookAPIState } from '~/app/context/useNotebookAPIState';
|
||||
import { NotebookContext } from '~/app/context/NotebookContext';
|
||||
|
||||
|
@ -7,7 +7,7 @@ type UseNotebookAPI = NotebookAPIState & {
|
|||
};
|
||||
|
||||
export const useNotebookAPI = (): UseNotebookAPI => {
|
||||
const { apiState, refreshAPIState: refreshAllAPI } = React.useContext(NotebookContext);
|
||||
const { apiState, refreshAPIState: refreshAllAPI } = useContext(NotebookContext);
|
||||
|
||||
return {
|
||||
refreshAllAPI,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNotebookAPI } from '~/app/hooks/useNotebookAPI';
|
||||
import { Workspace, WorkspaceKind } from '~/shared/api/backendApiTypes';
|
||||
import { WorkspaceCountPerOption } from '~/app/types';
|
||||
|
@ -8,11 +8,9 @@ export type WorkspaceCountPerKind = Record<WorkspaceKind['name'], WorkspaceCount
|
|||
export const useWorkspaceCountPerKind = (): WorkspaceCountPerKind => {
|
||||
const { api } = useNotebookAPI();
|
||||
|
||||
const [workspaceCountPerKind, setWorkspaceCountPerKind] = React.useState<WorkspaceCountPerKind>(
|
||||
{},
|
||||
);
|
||||
const [workspaceCountPerKind, setWorkspaceCountPerKind] = useState<WorkspaceCountPerKind>({});
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
api.listAllWorkspaces({}).then((workspaces) => {
|
||||
const countPerKind = workspaces.reduce((acc: WorkspaceCountPerKind, workspace: Workspace) => {
|
||||
acc[workspace.workspaceKind.name] = acc[workspace.workspaceKind.name] ?? {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useNotebookAPI } from '~/app/hooks/useNotebookAPI';
|
||||
import { WorkspaceFormData } from '~/app/types';
|
||||
import useFetchState, {
|
||||
|
@ -25,7 +25,7 @@ const useWorkspaceFormData = (args: {
|
|||
}): FetchState<WorkspaceFormData> => {
|
||||
const { api, apiAvailable } = useNotebookAPI();
|
||||
|
||||
const call = React.useCallback<FetchStateCallbackPromise<WorkspaceFormData>>(
|
||||
const call = useCallback<FetchStateCallbackPromise<WorkspaceFormData>>(
|
||||
async (opts) => {
|
||||
if (!apiAvailable) {
|
||||
throw new Error('API not yet available');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import useFetchState, {
|
||||
FetchState,
|
||||
FetchStateCallbackPromise,
|
||||
|
@ -9,7 +9,7 @@ import { WorkspaceKind } from '~/shared/api/backendApiTypes';
|
|||
const useWorkspaceKindByName = (kind: string): FetchState<WorkspaceKind | null> => {
|
||||
const { api, apiAvailable } = useNotebookAPI();
|
||||
|
||||
const call = React.useCallback<FetchStateCallbackPromise<WorkspaceKind | null>>(
|
||||
const call = useCallback<FetchStateCallbackPromise<WorkspaceKind | null>>(
|
||||
(opts) => {
|
||||
if (!apiAvailable) {
|
||||
return Promise.reject(new Error('API not yet available'));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import useFetchState, {
|
||||
FetchState,
|
||||
FetchStateCallbackPromise,
|
||||
|
@ -8,7 +8,7 @@ import { useNotebookAPI } from '~/app/hooks/useNotebookAPI';
|
|||
|
||||
const useWorkspaceKinds = (): FetchState<WorkspaceKind[]> => {
|
||||
const { api, apiAvailable } = useNotebookAPI();
|
||||
const call = React.useCallback<FetchStateCallbackPromise<WorkspaceKind[]>>(
|
||||
const call = useCallback<FetchStateCallbackPromise<WorkspaceKind[]>>(
|
||||
(opts) => {
|
||||
if (!apiAvailable) {
|
||||
return Promise.reject(new Error('API not yet available'));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import useFetchState, {
|
||||
FetchState,
|
||||
FetchStateCallbackPromise,
|
||||
|
@ -9,7 +9,7 @@ import { Workspace } from '~/shared/api/backendApiTypes';
|
|||
const useWorkspaces = (namespace: string): FetchState<Workspace[] | null> => {
|
||||
const { api, apiAvailable } = useNotebookAPI();
|
||||
|
||||
const call = React.useCallback<FetchStateCallbackPromise<Workspace[] | null>>(
|
||||
const call = useCallback<FetchStateCallbackPromise<Workspace[] | null>>(
|
||||
(opts) => {
|
||||
if (!apiAvailable) {
|
||||
return Promise.reject(new Error('API not yet available'));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { CubesIcon } from '@patternfly/react-icons';
|
||||
import {
|
||||
Button,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
Drawer,
|
||||
DrawerContent,
|
||||
|
@ -48,7 +48,7 @@ export enum ActionType {
|
|||
|
||||
export const WorkspaceKinds: React.FunctionComponent = () => {
|
||||
// Table columns
|
||||
const columns: WorkspaceKindsColumns = React.useMemo(
|
||||
const columns: WorkspaceKindsColumns = useMemo(
|
||||
() => ({
|
||||
icon: { name: '', label: 'Icon', id: 'icon' },
|
||||
name: { name: 'Name', label: 'Name', id: 'name' },
|
||||
|
@ -65,16 +65,14 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
|
||||
const [workspaceKinds, workspaceKindsLoaded, workspaceKindsError] = useWorkspaceKinds();
|
||||
const workspaceCountPerKind = useWorkspaceCountPerKind();
|
||||
const [selectedWorkspaceKind, setSelectedWorkspaceKind] = React.useState<WorkspaceKind | null>(
|
||||
null,
|
||||
);
|
||||
const [activeActionType, setActiveActionType] = React.useState<ActionType | null>(null);
|
||||
const [selectedWorkspaceKind, setSelectedWorkspaceKind] = useState<WorkspaceKind | null>(null);
|
||||
const [activeActionType, setActiveActionType] = useState<ActionType | null>(null);
|
||||
|
||||
// Column sorting
|
||||
const [activeSortIndex, setActiveSortIndex] = React.useState<number | null>(null);
|
||||
const [activeSortDirection, setActiveSortDirection] = React.useState<'asc' | 'desc' | null>(null);
|
||||
const [activeSortIndex, setActiveSortIndex] = useState<number | null>(null);
|
||||
const [activeSortDirection, setActiveSortDirection] = useState<'asc' | 'desc' | null>(null);
|
||||
|
||||
const getSortableRowValues = React.useCallback(
|
||||
const getSortableRowValues = useCallback(
|
||||
(workspaceKind: WorkspaceKind): (string | boolean | number)[] => {
|
||||
const {
|
||||
icon,
|
||||
|
@ -95,7 +93,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
[workspaceCountPerKind],
|
||||
);
|
||||
|
||||
const sortedWorkspaceKinds = React.useMemo(() => {
|
||||
const sortedWorkspaceKinds = useMemo(() => {
|
||||
if (activeSortIndex === null) {
|
||||
return workspaceKinds;
|
||||
}
|
||||
|
@ -114,7 +112,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
});
|
||||
}, [workspaceKinds, activeSortIndex, activeSortDirection, getSortableRowValues]);
|
||||
|
||||
const getSortParams = React.useCallback(
|
||||
const getSortParams = useCallback(
|
||||
(columnIndex: number): ThProps['sort'] => ({
|
||||
sortBy: {
|
||||
index: activeSortIndex || 0,
|
||||
|
@ -131,19 +129,19 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
);
|
||||
|
||||
// Set up filter - Attribute search.
|
||||
const [searchNameValue, setSearchNameValue] = React.useState('');
|
||||
const [searchDescriptionValue, setSearchDescriptionValue] = React.useState('');
|
||||
const [statusSelection, setStatusSelection] = React.useState('');
|
||||
const [searchNameValue, setSearchNameValue] = useState('');
|
||||
const [searchDescriptionValue, setSearchDescriptionValue] = useState('');
|
||||
const [statusSelection, setStatusSelection] = useState('');
|
||||
|
||||
const onSearchNameChange = React.useCallback((value: string) => {
|
||||
const onSearchNameChange = useCallback((value: string) => {
|
||||
setSearchNameValue(value);
|
||||
}, []);
|
||||
|
||||
const onSearchDescriptionChange = React.useCallback((value: string) => {
|
||||
const onSearchDescriptionChange = useCallback((value: string) => {
|
||||
setSearchDescriptionValue(value);
|
||||
}, []);
|
||||
|
||||
const onFilter = React.useCallback(
|
||||
const onFilter = useCallback(
|
||||
(workspaceKind: WorkspaceKind) => {
|
||||
let nameRegex: RegExp;
|
||||
let descriptionRegex: RegExp;
|
||||
|
@ -178,24 +176,24 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
[searchNameValue, searchDescriptionValue, statusSelection],
|
||||
);
|
||||
|
||||
const filteredWorkspaceKinds = React.useMemo(
|
||||
const filteredWorkspaceKinds = useMemo(
|
||||
() => sortedWorkspaceKinds.filter(onFilter),
|
||||
[sortedWorkspaceKinds, onFilter],
|
||||
);
|
||||
|
||||
const clearAllFilters = React.useCallback(() => {
|
||||
const clearAllFilters = useCallback(() => {
|
||||
setSearchNameValue('');
|
||||
setStatusSelection('');
|
||||
setSearchDescriptionValue('');
|
||||
}, []);
|
||||
|
||||
// Set up status single select
|
||||
const [isStatusMenuOpen, setIsStatusMenuOpen] = React.useState<boolean>(false);
|
||||
const statusToggleRef = React.useRef<HTMLButtonElement>(null);
|
||||
const statusMenuRef = React.useRef<HTMLDivElement>(null);
|
||||
const statusContainerRef = React.useRef<HTMLDivElement>(null);
|
||||
const [isStatusMenuOpen, setIsStatusMenuOpen] = useState<boolean>(false);
|
||||
const statusToggleRef = useRef<HTMLButtonElement>(null);
|
||||
const statusMenuRef = useRef<HTMLDivElement>(null);
|
||||
const statusContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleStatusMenuKeys = React.useCallback(
|
||||
const handleStatusMenuKeys = useCallback(
|
||||
(event: KeyboardEvent) => {
|
||||
if (isStatusMenuOpen && statusMenuRef.current?.contains(event.target as Node)) {
|
||||
if (event.key === 'Escape' || event.key === 'Tab') {
|
||||
|
@ -207,7 +205,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
[isStatusMenuOpen],
|
||||
);
|
||||
|
||||
const handleStatusClickOutside = React.useCallback(
|
||||
const handleStatusClickOutside = useCallback(
|
||||
(event: MouseEvent) => {
|
||||
if (isStatusMenuOpen && !statusMenuRef.current?.contains(event.target as Node)) {
|
||||
setIsStatusMenuOpen(false);
|
||||
|
@ -216,7 +214,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
[isStatusMenuOpen],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', handleStatusMenuKeys);
|
||||
window.addEventListener('click', handleStatusClickOutside);
|
||||
return () => {
|
||||
|
@ -225,7 +223,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
};
|
||||
}, [isStatusMenuOpen, statusMenuRef, handleStatusClickOutside, handleStatusMenuKeys]);
|
||||
|
||||
const onStatusToggleClick = React.useCallback((ev: React.MouseEvent) => {
|
||||
const onStatusToggleClick = useCallback((ev: React.MouseEvent) => {
|
||||
ev.stopPropagation();
|
||||
setTimeout(() => {
|
||||
const firstElement = statusMenuRef.current?.querySelector('li > button:not(:disabled)');
|
||||
|
@ -236,7 +234,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
setIsStatusMenuOpen((prev) => !prev);
|
||||
}, []);
|
||||
|
||||
const onStatusSelect = React.useCallback(
|
||||
const onStatusSelect = useCallback(
|
||||
(event: React.MouseEvent | undefined, itemId: string | number | undefined) => {
|
||||
if (typeof itemId === 'undefined') {
|
||||
return;
|
||||
|
@ -248,7 +246,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
[],
|
||||
);
|
||||
|
||||
const statusToggle = React.useMemo(
|
||||
const statusToggle = useMemo(
|
||||
() => (
|
||||
<MenuToggle
|
||||
ref={statusToggleRef}
|
||||
|
@ -262,7 +260,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
[isStatusMenuOpen, onStatusToggleClick],
|
||||
);
|
||||
|
||||
const statusMenu = React.useMemo(
|
||||
const statusMenu = useMemo(
|
||||
() => (
|
||||
<Menu
|
||||
ref={statusMenuRef}
|
||||
|
@ -281,7 +279,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
[statusSelection, onStatusSelect],
|
||||
);
|
||||
|
||||
const statusSelect = React.useMemo(
|
||||
const statusSelect = useMemo(
|
||||
() => (
|
||||
<div ref={statusContainerRef}>
|
||||
<Popper
|
||||
|
@ -298,15 +296,15 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
);
|
||||
|
||||
// Set up attribute selector
|
||||
const [activeAttributeMenu, setActiveAttributeMenu] = React.useState<
|
||||
'Name' | 'Description' | 'Status'
|
||||
>('Name');
|
||||
const [isAttributeMenuOpen, setIsAttributeMenuOpen] = React.useState(false);
|
||||
const attributeToggleRef = React.useRef<HTMLButtonElement>(null);
|
||||
const attributeMenuRef = React.useRef<HTMLDivElement>(null);
|
||||
const attributeContainerRef = React.useRef<HTMLDivElement>(null);
|
||||
const [activeAttributeMenu, setActiveAttributeMenu] = useState<'Name' | 'Description' | 'Status'>(
|
||||
'Name',
|
||||
);
|
||||
const [isAttributeMenuOpen, setIsAttributeMenuOpen] = useState(false);
|
||||
const attributeToggleRef = useRef<HTMLButtonElement>(null);
|
||||
const attributeMenuRef = useRef<HTMLDivElement>(null);
|
||||
const attributeContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleAttributeMenuKeys = React.useCallback(
|
||||
const handleAttributeMenuKeys = useCallback(
|
||||
(event: KeyboardEvent) => {
|
||||
if (!isAttributeMenuOpen) {
|
||||
return;
|
||||
|
@ -324,7 +322,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
[isAttributeMenuOpen],
|
||||
);
|
||||
|
||||
const handleAttributeClickOutside = React.useCallback(
|
||||
const handleAttributeClickOutside = useCallback(
|
||||
(event: MouseEvent) => {
|
||||
if (isAttributeMenuOpen && !attributeMenuRef.current?.contains(event.target as Node)) {
|
||||
setIsAttributeMenuOpen(false);
|
||||
|
@ -333,7 +331,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
[isAttributeMenuOpen],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', handleAttributeMenuKeys);
|
||||
window.addEventListener('click', handleAttributeClickOutside);
|
||||
return () => {
|
||||
|
@ -342,7 +340,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
};
|
||||
}, [isAttributeMenuOpen, attributeMenuRef, handleAttributeMenuKeys, handleAttributeClickOutside]);
|
||||
|
||||
const onAttributeToggleClick = React.useCallback((ev: React.MouseEvent) => {
|
||||
const onAttributeToggleClick = useCallback((ev: React.MouseEvent) => {
|
||||
ev.stopPropagation();
|
||||
|
||||
setTimeout(() => {
|
||||
|
@ -355,7 +353,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
setIsAttributeMenuOpen((prev) => !prev);
|
||||
}, []);
|
||||
|
||||
const attributeToggle = React.useMemo(
|
||||
const attributeToggle = useMemo(
|
||||
() => (
|
||||
<MenuToggle
|
||||
ref={attributeToggleRef}
|
||||
|
@ -369,7 +367,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
[isAttributeMenuOpen, onAttributeToggleClick, activeAttributeMenu],
|
||||
);
|
||||
|
||||
const attributeMenu = React.useMemo(
|
||||
const attributeMenu = useMemo(
|
||||
() => (
|
||||
<Menu
|
||||
ref={attributeMenuRef}
|
||||
|
@ -390,7 +388,7 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
[],
|
||||
);
|
||||
|
||||
const attributeDropdown = React.useMemo(
|
||||
const attributeDropdown = useMemo(
|
||||
() => (
|
||||
<div ref={attributeContainerRef}>
|
||||
<Popper
|
||||
|
@ -406,19 +404,19 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
|
|||
[attributeToggle, attributeMenu, isAttributeMenuOpen],
|
||||
);
|
||||
|
||||
const emptyState = React.useMemo(
|
||||
const emptyState = useMemo(
|
||||
() => <CustomEmptyState onClearFilters={clearAllFilters} />,
|
||||
[clearAllFilters],
|
||||
);
|
||||
|
||||
// Actions
|
||||
|
||||
const viewDetailsClick = React.useCallback((workspaceKind: WorkspaceKind) => {
|
||||
const viewDetailsClick = useCallback((workspaceKind: WorkspaceKind) => {
|
||||
setSelectedWorkspaceKind(workspaceKind);
|
||||
setActiveActionType(ActionType.ViewDetails);
|
||||
}, []);
|
||||
|
||||
const workspaceKindsDefaultActions = React.useCallback(
|
||||
const workspaceKindsDefaultActions = useCallback(
|
||||
(workspaceKind: WorkspaceKind): IActions => [
|
||||
{
|
||||
id: 'view-details',
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
DrawerActions,
|
||||
DrawerCloseButton,
|
||||
|
@ -30,7 +30,7 @@ export const WorkspaceKindDetails: React.FunctionComponent<WorkspaceKindDetailsP
|
|||
workspaceCountPerKind,
|
||||
onCloseClick,
|
||||
}) => {
|
||||
const [activeTabKey, setActiveTabKey] = React.useState<string | number>(0);
|
||||
const [activeTabKey, setActiveTabKey] = useState<string | number>(0);
|
||||
|
||||
const handleTabClick = (
|
||||
event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
ClipboardCopy,
|
||||
ClipboardCopyVariant,
|
||||
|
@ -11,7 +12,6 @@ import {
|
|||
Tooltip,
|
||||
} from '@patternfly/react-core';
|
||||
import { DatabaseIcon, LockedIcon } from '@patternfly/react-icons';
|
||||
import * as React from 'react';
|
||||
import { Workspace } from '~/shared/api/backendApiTypes';
|
||||
|
||||
interface DataVolumesListProps {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
DrawerActions,
|
||||
DrawerCloseButton,
|
||||
|
@ -32,7 +32,7 @@ export const WorkspaceDetails: React.FunctionComponent<WorkspaceDetailsProps> =
|
|||
// onEditClick,
|
||||
onDeleteClick,
|
||||
}) => {
|
||||
const [activeTabKey, setActiveTabKey] = React.useState<string | number>(0);
|
||||
const [activeTabKey, setActiveTabKey] = useState<string | number>(0);
|
||||
|
||||
const handleTabClick = (
|
||||
event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownList,
|
||||
|
@ -18,7 +18,7 @@ export const WorkspaceDetailsActions: React.FC<WorkspaceDetailsActionsProps> = (
|
|||
// onEditClick,
|
||||
onDeleteClick,
|
||||
}) => {
|
||||
const [isOpen, setOpen] = React.useState(false);
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<Flex>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { ExpandableRowContent, Td, Tr } from '@patternfly/react-table';
|
||||
import { Workspace } from '~/shared/api/backendApiTypes';
|
||||
import { DataVolumesList } from '~/app/pages/Workspaces/DataVolumesList';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Content,
|
||||
|
@ -10,7 +10,6 @@ import {
|
|||
ProgressStepper,
|
||||
Stack,
|
||||
} from '@patternfly/react-core';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import useGenericObjectState from '~/app/hooks/useGenericObjectState';
|
||||
import { useNotebookAPI } from '~/app/hooks/useNotebookAPI';
|
||||
import { WorkspaceFormImageSelection } from '~/app/pages/Workspaces/Form/image/WorkspaceFormImageSelection';
|
||||
|
@ -48,13 +47,13 @@ const WorkspaceForm: React.FC = () => {
|
|||
workspaceName,
|
||||
});
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [currentStep, setCurrentStep] = useState(WorkspaceFormSteps.KindSelection);
|
||||
|
||||
const [data, setData, resetData, replaceData] =
|
||||
useGenericObjectState<WorkspaceFormData>(initialFormData);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!initialFormDataLoaded || mode === 'create') {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
CardTitle,
|
||||
Gallery,
|
||||
|
@ -28,7 +28,7 @@ export const WorkspaceFormImageList: React.FunctionComponent<WorkspaceFormImageL
|
|||
}) => {
|
||||
const [workspaceImages, setWorkspaceImages] = useState<WorkspaceImageConfigValue[]>(images);
|
||||
const [filters, setFilters] = useState<FilteredColumn[]>([]);
|
||||
const filterRef = React.useRef<FilterRef>(null);
|
||||
const filterRef = useRef<FilterRef>(null);
|
||||
|
||||
const filterableColumns = useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useMemo, useState, useCallback } from 'react';
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { Content, Split, SplitItem } from '@patternfly/react-core';
|
||||
import { WorkspaceFormImageDetails } from '~/app/pages/Workspaces/Form/image/WorkspaceFormImageDetails';
|
||||
import { WorkspaceFormImageList } from '~/app/pages/Workspaces/Form/image/WorkspaceFormImageList';
|
||||
|
@ -19,7 +19,7 @@ const WorkspaceFormImageSelection: React.FunctionComponent<WorkspaceFormImageSel
|
|||
}) => {
|
||||
const [selectedLabels, setSelectedLabels] = useState<Map<string, Set<string>>>(new Map());
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const drawerRef = React.useRef<HTMLSpanElement>(undefined);
|
||||
const drawerRef = useRef<HTMLSpanElement>(undefined);
|
||||
|
||||
const onExpand = useCallback(() => {
|
||||
if (drawerRef.current) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
CardBody,
|
||||
CardTitle,
|
||||
|
@ -25,7 +25,7 @@ export const WorkspaceFormKindList: React.FunctionComponent<WorkspaceFormKindLis
|
|||
onSelect,
|
||||
}) => {
|
||||
const [workspaceKinds, setWorkspaceKinds] = useState<WorkspaceKind[]>(allWorkspaceKinds);
|
||||
const filterRef = React.useRef<FilterRef>(null);
|
||||
const filterRef = useRef<FilterRef>(null);
|
||||
|
||||
const filterableColumns = useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useRef, useMemo, useCallback } from 'react';
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { Content } from '@patternfly/react-core';
|
||||
import { WorkspaceKind } from '~/shared/api/backendApiTypes';
|
||||
import useWorkspaceKinds from '~/app/hooks/useWorkspaceKinds';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
CardTitle,
|
||||
Gallery,
|
||||
|
@ -26,7 +26,7 @@ export const WorkspaceFormPodConfigList: React.FunctionComponent<
|
|||
const [workspacePodConfigs, setWorkspacePodConfigs] =
|
||||
useState<WorkspacePodConfigValue[]>(podConfigs);
|
||||
const [filters, setFilters] = useState<FilteredColumn[]>([]);
|
||||
const filterRef = React.useRef<FilterRef>(null);
|
||||
const filterRef = useRef<FilterRef>(null);
|
||||
|
||||
const filterableColumns = useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { Content, Split, SplitItem } from '@patternfly/react-core';
|
||||
import { WorkspaceFormPodConfigDetails } from '~/app/pages/Workspaces/Form/podConfig/WorkspaceFormPodConfigDetails';
|
||||
import { WorkspaceFormPodConfigList } from '~/app/pages/Workspaces/Form/podConfig/WorkspaceFormPodConfigList';
|
||||
|
@ -17,7 +17,7 @@ const WorkspaceFormPodConfigSelection: React.FunctionComponent<
|
|||
> = ({ podConfigs, selectedPodConfig, onSelect }) => {
|
||||
const [selectedLabels, setSelectedLabels] = useState<Map<string, Set<string>>>(new Map());
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const drawerRef = React.useRef<HTMLSpanElement>(undefined);
|
||||
const drawerRef = useRef<HTMLSpanElement>(undefined);
|
||||
|
||||
const onExpand = useCallback(() => {
|
||||
if (drawerRef.current) {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import {
|
||||
Checkbox,
|
||||
Content,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import React, { useCallback, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
|
@ -15,7 +16,6 @@ import {
|
|||
} from '@patternfly/react-core';
|
||||
import { EllipsisVIcon } from '@patternfly/react-icons';
|
||||
import { Table, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { WorkspacePodVolumeMount } from '~/shared/api/backendApiTypes';
|
||||
|
||||
interface WorkspaceFormPropertiesVolumesProps {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
|
@ -16,7 +16,7 @@ type WorkspaceConnectActionProps = {
|
|||
export const WorkspaceConnectAction: React.FunctionComponent<WorkspaceConnectActionProps> = ({
|
||||
workspace,
|
||||
}) => {
|
||||
const [open, setIsOpen] = React.useState(false);
|
||||
const [open, setIsOpen] = useState(false);
|
||||
|
||||
const onToggleClick = () => {
|
||||
setIsOpen(!open);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
Drawer,
|
||||
DrawerContent,
|
||||
|
@ -33,7 +33,6 @@ import {
|
|||
QuestionCircleIcon,
|
||||
CodeIcon,
|
||||
} from '@patternfly/react-icons';
|
||||
import { useState } from 'react';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { Workspace, WorkspaceState } from '~/shared/api/backendApiTypes';
|
||||
import { WorkspaceDetails } from '~/app/pages/Workspaces/Details/WorkspaceDetails';
|
||||
|
@ -68,7 +67,7 @@ export enum ActionType {
|
|||
|
||||
export const Workspaces: React.FunctionComponent = () => {
|
||||
const navigate = useTypedNavigate();
|
||||
const createWorkspace = React.useCallback(() => {
|
||||
const createWorkspace = useCallback(() => {
|
||||
navigate('workspaceCreate');
|
||||
}, [navigate]);
|
||||
|
||||
|
@ -83,7 +82,7 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
const workspaceRedirectStatus = buildWorkspaceRedirectStatus(workspaceKinds);
|
||||
|
||||
// Table columns
|
||||
const columnNames: WorkspacesColumnNames = React.useMemo(
|
||||
const columnNames: WorkspacesColumnNames = useMemo(
|
||||
() => ({
|
||||
redirectStatus: 'Redirect Status',
|
||||
name: 'Name',
|
||||
|
@ -114,20 +113,20 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
const [initialWorkspaces, initialWorkspacesLoaded, , initialWorkspacesRefresh] =
|
||||
useWorkspaces(selectedNamespace);
|
||||
const [workspaces, setWorkspaces] = useState<Workspace[]>([]);
|
||||
const [expandedWorkspacesNames, setExpandedWorkspacesNames] = React.useState<string[]>([]);
|
||||
const [selectedWorkspace, setSelectedWorkspace] = React.useState<Workspace | null>(null);
|
||||
const [isActionAlertModalOpen, setIsActionAlertModalOpen] = React.useState(false);
|
||||
const [activeActionType, setActiveActionType] = React.useState<ActionType | null>(null);
|
||||
const filterRef = React.useRef<FilterRef>(null);
|
||||
const [expandedWorkspacesNames, setExpandedWorkspacesNames] = useState<string[]>([]);
|
||||
const [selectedWorkspace, setSelectedWorkspace] = useState<Workspace | null>(null);
|
||||
const [isActionAlertModalOpen, setIsActionAlertModalOpen] = useState(false);
|
||||
const [activeActionType, setActiveActionType] = useState<ActionType | null>(null);
|
||||
const filterRef = useRef<FilterRef>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!initialWorkspacesLoaded) {
|
||||
return;
|
||||
}
|
||||
setWorkspaces(initialWorkspaces ?? []);
|
||||
}, [initialWorkspaces, initialWorkspacesLoaded]);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (activeActionType !== ActionType.Edit || !selectedWorkspace) {
|
||||
return;
|
||||
}
|
||||
|
@ -139,7 +138,7 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
});
|
||||
}, [activeActionType, navigate, selectedWorkspace]);
|
||||
|
||||
const selectWorkspace = React.useCallback(
|
||||
const selectWorkspace = useCallback(
|
||||
(newSelectedWorkspace: Workspace | null) => {
|
||||
if (selectedWorkspace?.name === newSelectedWorkspace?.name) {
|
||||
setSelectedWorkspace(null);
|
||||
|
@ -162,7 +161,7 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
expandedWorkspacesNames.includes(workspace.name);
|
||||
|
||||
// filter function to pass to the filter component
|
||||
const onFilter = React.useCallback(
|
||||
const onFilter = useCallback(
|
||||
(filters: FilteredColumn[]) => {
|
||||
// Search name with search value
|
||||
let filteredWorkspaces = initialWorkspaces ?? [];
|
||||
|
@ -212,15 +211,15 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
[initialWorkspaces, columnNames],
|
||||
);
|
||||
|
||||
const emptyState = React.useMemo(
|
||||
const emptyState = useMemo(
|
||||
() => <CustomEmptyState onClearFilters={() => filterRef.current?.clearAll()} />,
|
||||
[],
|
||||
);
|
||||
|
||||
// Column sorting
|
||||
|
||||
const [activeSortIndex, setActiveSortIndex] = React.useState<number | null>(null);
|
||||
const [activeSortDirection, setActiveSortDirection] = React.useState<'asc' | 'desc' | null>(null);
|
||||
const [activeSortIndex, setActiveSortIndex] = useState<number | null>(null);
|
||||
const [activeSortDirection, setActiveSortDirection] = useState<'asc' | 'desc' | null>(null);
|
||||
|
||||
const getSortableRowValues = (workspace: Workspace): (string | number)[] => {
|
||||
const { redirectStatus, name, kind, image, podConfig, state, homeVol, cpu, ram, lastActivity } =
|
||||
|
@ -274,7 +273,7 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
|
||||
// Actions
|
||||
|
||||
const viewDetailsClick = React.useCallback((workspace: Workspace) => {
|
||||
const viewDetailsClick = useCallback((workspace: Workspace) => {
|
||||
setSelectedWorkspace(workspace);
|
||||
setActiveActionType(ActionType.ViewDetails);
|
||||
}, []);
|
||||
|
@ -285,7 +284,7 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
// setActiveActionType(ActionType.Edit);
|
||||
// }, []);
|
||||
|
||||
const deleteAction = React.useCallback(async () => {
|
||||
const deleteAction = useCallback(async () => {
|
||||
if (!selectedWorkspace) {
|
||||
return;
|
||||
}
|
||||
|
@ -301,19 +300,19 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
}
|
||||
}, [api, initialWorkspacesRefresh, selectedNamespace, selectedWorkspace]);
|
||||
|
||||
const startRestartAction = React.useCallback((workspace: Workspace, action: ActionType) => {
|
||||
const startRestartAction = useCallback((workspace: Workspace, action: ActionType) => {
|
||||
setSelectedWorkspace(workspace);
|
||||
setActiveActionType(action);
|
||||
setIsActionAlertModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const stopAction = React.useCallback((workspace: Workspace) => {
|
||||
const stopAction = useCallback((workspace: Workspace) => {
|
||||
setSelectedWorkspace(workspace);
|
||||
setActiveActionType(ActionType.Stop);
|
||||
setIsActionAlertModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const handleDeleteClick = React.useCallback((workspace: Workspace) => {
|
||||
const handleDeleteClick = useCallback((workspace: Workspace) => {
|
||||
const buttonElement = document.activeElement as HTMLElement;
|
||||
buttonElement.blur(); // Remove focus from the currently focused button
|
||||
setSelectedWorkspace(workspace);
|
||||
|
@ -482,8 +481,8 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
|
||||
// Pagination
|
||||
|
||||
const [page, setPage] = React.useState(1);
|
||||
const [perPage, setPerPage] = React.useState(10);
|
||||
const [page, setPage] = useState(1);
|
||||
const [perPage, setPerPage] = useState(10);
|
||||
|
||||
const onSetPage = (
|
||||
_event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { ExpandableSection, Icon, Tab, Tabs, TabTitleText, Content } from '@patternfly/react-core';
|
||||
import {
|
||||
ExclamationCircleIcon,
|
||||
ExclamationTriangleIcon,
|
||||
InfoCircleIcon,
|
||||
} from '@patternfly/react-icons';
|
||||
import * as React from 'react';
|
||||
import useWorkspaceKindByName from '~/app/hooks/useWorkspaceKindByName';
|
||||
import { WorkspaceKind } from '~/shared/api/backendApiTypes';
|
||||
|
||||
|
@ -44,14 +44,14 @@ interface WorkspaceRedirectInformationViewProps {
|
|||
export const WorkspaceRedirectInformationView: React.FC<WorkspaceRedirectInformationViewProps> = ({
|
||||
kind,
|
||||
}) => {
|
||||
const [activeKey, setActiveKey] = React.useState<string | number>(0);
|
||||
const [activeKey, setActiveKey] = useState<string | number>(0);
|
||||
const [workspaceKind, workspaceKindLoaded] = useWorkspaceKindByName(kind);
|
||||
const [imageConfig, setImageConfig] =
|
||||
React.useState<WorkspaceKind['podTemplate']['options']['imageConfig']>();
|
||||
useState<WorkspaceKind['podTemplate']['options']['imageConfig']>();
|
||||
const [podConfig, setPodConfig] =
|
||||
React.useState<WorkspaceKind['podTemplate']['options']['podConfig']>();
|
||||
useState<WorkspaceKind['podTemplate']['options']['podConfig']>();
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!workspaceKindLoaded) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
Content,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Modal,
|
||||
|
@ -30,9 +30,9 @@ export const WorkspaceStartActionModal: React.FC<StartActionAlertProps> = ({
|
|||
onUpdateAndStart,
|
||||
onActionDone,
|
||||
}) => {
|
||||
const [actionOnGoing, setActionOnGoing] = React.useState<StartAction | null>(null);
|
||||
const [actionOnGoing, setActionOnGoing] = useState<StartAction | null>(null);
|
||||
|
||||
const executeAction = React.useCallback(
|
||||
const executeAction = useCallback(
|
||||
async (args: { action: StartAction; callback: () => Promise<void> }) => {
|
||||
setActionOnGoing(args.action);
|
||||
try {
|
||||
|
@ -44,7 +44,7 @@ export const WorkspaceStartActionModal: React.FC<StartActionAlertProps> = ({
|
|||
[],
|
||||
);
|
||||
|
||||
const handleStart = React.useCallback(async () => {
|
||||
const handleStart = useCallback(async () => {
|
||||
try {
|
||||
await executeAction({ action: 'start', callback: onStart });
|
||||
// TODO: alert user about success
|
||||
|
@ -58,7 +58,7 @@ export const WorkspaceStartActionModal: React.FC<StartActionAlertProps> = ({
|
|||
}, [executeAction, onActionDone, onClose, onStart]);
|
||||
|
||||
// TODO: combine handleStart and handleUpdateAndStart if they end up being similar
|
||||
const handleUpdateAndStart = React.useCallback(async () => {
|
||||
const handleUpdateAndStart = useCallback(async () => {
|
||||
try {
|
||||
await executeAction({ action: 'updateAndStart', callback: onUpdateAndStart });
|
||||
// TODO: alert user about success
|
||||
|
@ -71,7 +71,7 @@ export const WorkspaceStartActionModal: React.FC<StartActionAlertProps> = ({
|
|||
}
|
||||
}, [executeAction, onActionDone, onClose, onUpdateAndStart]);
|
||||
|
||||
const shouldShowActionButton = React.useCallback(
|
||||
const shouldShowActionButton = useCallback(
|
||||
(action: StartAction) => !actionOnGoing || actionOnGoing === action,
|
||||
[actionOnGoing],
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Content,
|
||||
|
@ -32,9 +32,9 @@ export const WorkspaceStopActionModal: React.FC<StopActionAlertProps> = ({
|
|||
onActionDone,
|
||||
}) => {
|
||||
const workspacePendingUpdate = workspace?.pendingRestart;
|
||||
const [actionOnGoing, setActionOnGoing] = React.useState<StopAction | null>(null);
|
||||
const [actionOnGoing, setActionOnGoing] = useState<StopAction | null>(null);
|
||||
|
||||
const executeAction = React.useCallback(
|
||||
const executeAction = useCallback(
|
||||
async (args: { action: StopAction; callback: () => Promise<void> }) => {
|
||||
setActionOnGoing(args.action);
|
||||
try {
|
||||
|
@ -46,7 +46,7 @@ export const WorkspaceStopActionModal: React.FC<StopActionAlertProps> = ({
|
|||
[],
|
||||
);
|
||||
|
||||
const handleStop = React.useCallback(async () => {
|
||||
const handleStop = useCallback(async () => {
|
||||
try {
|
||||
await executeAction({ action: 'stop', callback: onStop });
|
||||
// TODO: alert user about success
|
||||
|
@ -60,7 +60,7 @@ export const WorkspaceStopActionModal: React.FC<StopActionAlertProps> = ({
|
|||
}, [executeAction, onActionDone, onClose, onStop]);
|
||||
|
||||
// TODO: combine handleStop and handleUpdateAndStop if they end up being similar
|
||||
const handleUpdateAndStop = React.useCallback(async () => {
|
||||
const handleUpdateAndStop = useCallback(async () => {
|
||||
try {
|
||||
await executeAction({ action: 'updateAndStop', callback: onUpdateAndStop });
|
||||
// TODO: alert user about success
|
||||
|
@ -73,7 +73,7 @@ export const WorkspaceStopActionModal: React.FC<StopActionAlertProps> = ({
|
|||
}
|
||||
}, [executeAction, onActionDone, onClose, onUpdateAndStop]);
|
||||
|
||||
const shouldShowActionButton = React.useCallback(
|
||||
const shouldShowActionButton = useCallback(
|
||||
(action: StopAction) => !actionOnGoing || actionOnGoing === action,
|
||||
[actionOnGoing],
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { ExclamationTriangleIcon } from '@patternfly/react-icons';
|
||||
import {
|
||||
Button,
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import * as React from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { APIState } from '~/shared/api/types';
|
||||
|
||||
const useAPIState = <T>(
|
||||
hostPath: string | null,
|
||||
createAPI: (path: string) => T,
|
||||
): [apiState: APIState<T>, refreshAPIState: () => void] => {
|
||||
const [internalAPIToggleState, setInternalAPIToggleState] = React.useState(false);
|
||||
const [internalAPIToggleState, setInternalAPIToggleState] = useState(false);
|
||||
|
||||
const refreshAPIState = React.useCallback(() => {
|
||||
const refreshAPIState = useCallback(() => {
|
||||
setInternalAPIToggleState((v) => !v);
|
||||
}, []);
|
||||
|
||||
const apiState = React.useMemo<APIState<T>>(() => {
|
||||
const apiState = useMemo<APIState<T>>(() => {
|
||||
let path = hostPath;
|
||||
if (!path) {
|
||||
// TODO: we need to figure out maybe a stopgap or something
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { Button } from '@patternfly/react-core';
|
||||
|
||||
type ActionButtonProps = {
|
||||
|
@ -13,9 +13,9 @@ export const ActionButton: React.FC<ActionButtonProps> = ({
|
|||
onClick,
|
||||
...props
|
||||
}) => {
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleClick = React.useCallback(async () => {
|
||||
const handleClick = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await onClick();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
EmptyState,
|
||||
EmptyStateBody,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
Modal,
|
||||
ModalBody,
|
||||
|
@ -34,7 +34,7 @@ const DeleteModal: React.FC<DeleteModalProps> = ({
|
|||
onDelete,
|
||||
}) => {
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [isDeleting, setIsDeleting] = React.useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
import * as React from 'react';
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {
|
||||
Menu,
|
||||
MenuContent,
|
||||
|
@ -38,19 +45,19 @@ export interface FilterRef {
|
|||
const Filter = React.forwardRef<FilterRef, FilterProps>(
|
||||
({ id, onFilter, columnNames, toolbarActions }, ref) => {
|
||||
Filter.displayName = 'Filter';
|
||||
const [activeFilter, setActiveFilter] = React.useState<FilteredColumn>({
|
||||
const [activeFilter, setActiveFilter] = useState<FilteredColumn>({
|
||||
columnName: Object.values(columnNames)[0],
|
||||
value: '',
|
||||
});
|
||||
const [searchValue, setSearchValue] = React.useState<string>('');
|
||||
const [isFilterMenuOpen, setIsFilterMenuOpen] = React.useState<boolean>(false);
|
||||
const [filters, setFilters] = React.useState<FilteredColumn[]>([]);
|
||||
const [searchValue, setSearchValue] = useState<string>('');
|
||||
const [isFilterMenuOpen, setIsFilterMenuOpen] = useState<boolean>(false);
|
||||
const [filters, setFilters] = useState<FilteredColumn[]>([]);
|
||||
|
||||
const filterToggleRef = React.useRef<MenuToggleElement | null>(null);
|
||||
const filterMenuRef = React.useRef<HTMLDivElement | null>(null);
|
||||
const filterContainerRef = React.useRef<HTMLDivElement | null>(null);
|
||||
const filterToggleRef = useRef<MenuToggleElement | null>(null);
|
||||
const filterMenuRef = useRef<HTMLDivElement | null>(null);
|
||||
const filterContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const handleFilterMenuKeys = React.useCallback(
|
||||
const handleFilterMenuKeys = useCallback(
|
||||
(event: KeyboardEvent) => {
|
||||
if (!isFilterMenuOpen) {
|
||||
return;
|
||||
|
@ -68,7 +75,7 @@ const Filter = React.forwardRef<FilterRef, FilterProps>(
|
|||
[isFilterMenuOpen, filterMenuRef, filterToggleRef],
|
||||
);
|
||||
|
||||
const handleClickOutside = React.useCallback(
|
||||
const handleClickOutside = useCallback(
|
||||
(event: MouseEvent) => {
|
||||
if (isFilterMenuOpen && !filterMenuRef.current?.contains(event.target as Node)) {
|
||||
setIsFilterMenuOpen(false);
|
||||
|
@ -77,7 +84,7 @@ const Filter = React.forwardRef<FilterRef, FilterProps>(
|
|||
[isFilterMenuOpen, filterMenuRef],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', handleFilterMenuKeys);
|
||||
window.addEventListener('click', handleClickOutside);
|
||||
return () => {
|
||||
|
@ -86,7 +93,7 @@ const Filter = React.forwardRef<FilterRef, FilterProps>(
|
|||
};
|
||||
}, [isFilterMenuOpen, filterMenuRef, handleFilterMenuKeys, handleClickOutside]);
|
||||
|
||||
const onFilterToggleClick = React.useCallback(
|
||||
const onFilterToggleClick = useCallback(
|
||||
(ev: React.MouseEvent) => {
|
||||
ev.stopPropagation(); // Stop handleClickOutside from handling
|
||||
setTimeout(() => {
|
||||
|
@ -100,7 +107,7 @@ const Filter = React.forwardRef<FilterRef, FilterProps>(
|
|||
[isFilterMenuOpen],
|
||||
);
|
||||
|
||||
const updateFilters = React.useCallback(
|
||||
const updateFilters = useCallback(
|
||||
(filterObj: FilteredColumn) => {
|
||||
setFilters((prevFilters) => {
|
||||
const index = prevFilters.findIndex(
|
||||
|
@ -128,7 +135,7 @@ const Filter = React.forwardRef<FilterRef, FilterProps>(
|
|||
[onFilter],
|
||||
);
|
||||
|
||||
const onSearchChange = React.useCallback(
|
||||
const onSearchChange = useCallback(
|
||||
(value: string) => {
|
||||
setSearchValue(value);
|
||||
setActiveFilter((prevActiveFilter) => {
|
||||
|
@ -140,7 +147,7 @@ const Filter = React.forwardRef<FilterRef, FilterProps>(
|
|||
[updateFilters],
|
||||
);
|
||||
|
||||
const onDeleteLabelGroup = React.useCallback(
|
||||
const onDeleteLabelGroup = useCallback(
|
||||
(filter: FilteredColumn) => {
|
||||
setFilters((prevFilters) => {
|
||||
const newFilters = prevFilters.filter(
|
||||
|
@ -161,7 +168,7 @@ const Filter = React.forwardRef<FilterRef, FilterProps>(
|
|||
);
|
||||
|
||||
// Expose the clearAllFilters logic via the ref
|
||||
const clearAllInternal = React.useCallback(() => {
|
||||
const clearAllInternal = useCallback(() => {
|
||||
setFilters([]);
|
||||
setSearchValue('');
|
||||
setActiveFilter({
|
||||
|
@ -171,11 +178,11 @@ const Filter = React.forwardRef<FilterRef, FilterProps>(
|
|||
onFilter([]);
|
||||
}, [columnNames, onFilter]);
|
||||
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
useImperativeHandle(ref, () => ({
|
||||
clearAll: clearAllInternal,
|
||||
}));
|
||||
|
||||
const onFilterSelect = React.useCallback(
|
||||
const onFilterSelect = useCallback(
|
||||
(itemId: string | number | undefined) => {
|
||||
// Use the functional update form to toggle the state
|
||||
setIsFilterMenuOpen((prevIsMenuOpen) => !prevIsMenuOpen); // Fix is here
|
||||
|
@ -195,7 +202,7 @@ const Filter = React.forwardRef<FilterRef, FilterProps>(
|
|||
[columnNames, filters],
|
||||
);
|
||||
|
||||
const filterMenuToggle = React.useMemo(
|
||||
const filterMenuToggle = useMemo(
|
||||
() => (
|
||||
<MenuToggle
|
||||
ref={filterToggleRef}
|
||||
|
@ -209,7 +216,7 @@ const Filter = React.forwardRef<FilterRef, FilterProps>(
|
|||
[activeFilter.columnName, isFilterMenuOpen, onFilterToggleClick],
|
||||
);
|
||||
|
||||
const filterMenu = React.useMemo(
|
||||
const filterMenu = useMemo(
|
||||
() => (
|
||||
<Menu ref={filterMenuRef} onSelect={(_ev, itemId) => onFilterSelect(itemId)}>
|
||||
<MenuContent>
|
||||
|
@ -226,7 +233,7 @@ const Filter = React.forwardRef<FilterRef, FilterProps>(
|
|||
[columnNames, id, onFilterSelect],
|
||||
);
|
||||
|
||||
const filterDropdown = React.useMemo(
|
||||
const filterDropdown = useMemo(
|
||||
() => (
|
||||
<div ref={filterContainerRef}>
|
||||
<Popper
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { FC, useMemo, useState, useEffect } from 'react';
|
||||
import React, { FC, useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { APIOptions } from '~/shared/api/types';
|
||||
|
||||
/**
|
||||
|
@ -120,15 +120,15 @@ const useFetchState = <Type>(
|
|||
/** Configurable features */
|
||||
{ refreshRate = 0, initialPromisePurity = false }: Partial<FetchOptions> = {},
|
||||
): FetchState<Type> => {
|
||||
const initialDefaultStateRef = React.useRef(initialDefaultState);
|
||||
const [result, setResult] = React.useState<Type>(initialDefaultState);
|
||||
const [loaded, setLoaded] = React.useState(false);
|
||||
const [loadError, setLoadError] = React.useState<Error | undefined>(undefined);
|
||||
const abortCallbackRef = React.useRef<() => void>(() => undefined);
|
||||
const changePendingRef = React.useRef(true);
|
||||
const initialDefaultStateRef = useRef(initialDefaultState);
|
||||
const [result, setResult] = useState<Type>(initialDefaultState);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [loadError, setLoadError] = useState<Error | undefined>(undefined);
|
||||
const abortCallbackRef = useRef<() => void>(() => undefined);
|
||||
const changePendingRef = useRef(true);
|
||||
|
||||
/** Setup on initial hook a singular reset function. DefaultState & resetDataOnNewPromise are initial render states. */
|
||||
const cleanupRef = React.useRef(() => {
|
||||
const cleanupRef = useRef(() => {
|
||||
if (initialPromisePurity) {
|
||||
setResult(initialDefaultState);
|
||||
setLoaded(false);
|
||||
|
@ -136,11 +136,11 @@ const useFetchState = <Type>(
|
|||
}
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
cleanupRef.current();
|
||||
}, [fetchCallbackPromise]);
|
||||
|
||||
const call = React.useCallback<() => [Promise<Type | undefined>, () => void]>(() => {
|
||||
const call = useCallback<() => [Promise<Type | undefined>, () => void]>(() => {
|
||||
let alreadyAborted = false;
|
||||
const abortController = new AbortController();
|
||||
|
||||
|
@ -208,13 +208,13 @@ const useFetchState = <Type>(
|
|||
}, [fetchCallbackPromise]);
|
||||
|
||||
// Use a memmo to update the `changePendingRef` immediately on change.
|
||||
React.useMemo(() => {
|
||||
useMemo(() => {
|
||||
changePendingRef.current = true;
|
||||
// React to changes to the `call` reference.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [call]);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
let interval: ReturnType<typeof setInterval>;
|
||||
|
||||
const callAndSave = () => {
|
||||
|
@ -237,10 +237,10 @@ const useFetchState = <Type>(
|
|||
}, [call, refreshRate]);
|
||||
|
||||
// Use a reference for `call` to ensure a stable reference to `refresh` is always returned
|
||||
const callRef = React.useRef(call);
|
||||
const callRef = useRef(call);
|
||||
callRef.current = call;
|
||||
|
||||
const refresh = React.useCallback<FetchStateRefreshPromise<Type>>(() => {
|
||||
const refresh = useCallback<FetchStateRefreshPromise<Type>>(() => {
|
||||
abortCallbackRef.current();
|
||||
const [callPromise, unload] = callRef.current();
|
||||
abortCallbackRef.current = unload;
|
||||
|
|
Loading…
Reference in New Issue