[Fixed issue/1587] Got rid of react-hooks/exhaustive-deps errors (#1616)

* got rid of react-hooks/exhaustive-deps errors - part 1

* got rid of react-hooks/exhaustive-deps errors in useSearch

* got rid of react-hooks/exhaustive-deps errors in Filters

* got rid of react-hooks/exhaustive-deps errors in ResetOffsets

* got rid of react-hooks/exhaustive-deps errors in Filters

* got rid of react-hooks/exhaustive-deps errors in Breadcrumbs

* got rid of react-hooks/exhaustive-deps errors in DynamicTextButton

* got rid of react-hooks/exhaustive-deps errors in useDataSaver

* got rid of react-hooks/exhaustive-deps errors in ResultRenderer

Co-authored-by: Roman Zabaluev <rzabaluev@provectus.com>
This commit is contained in:
Denys Malofeiev 2022-02-21 14:03:56 +02:00 committed by GitHub
parent 3f0693bad6
commit 94b1f4a772
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 166 additions and 124 deletions

View file

@ -22,7 +22,8 @@
}, },
"plugins": [ "plugins": [
"@typescript-eslint", "@typescript-eslint",
"prettier" "prettier",
"eslint-plugin-react-hooks"
], ],
"extends": [ "extends": [
"airbnb", "airbnb",
@ -33,6 +34,8 @@
"prettier" "prettier"
], ],
"rules": { "rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/no-unused-prop-types": "off", "react/no-unused-prop-types": "off",
"react/require-default-props": "off", "react/require-default-props": "off",
"prettier/prettier": "warn", "prettier/prettier": "warn",

View file

@ -8,14 +8,20 @@ import Alert from 'components/Alerts/Alert';
const Alerts: React.FC = () => { const Alerts: React.FC = () => {
const alerts = useAppSelector(selectAll); const alerts = useAppSelector(selectAll);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const dismiss = React.useCallback((id: string) => { const dismiss = React.useCallback(
dispatch(alertDissmissed(id)); (id: string) => {
}, []); dispatch(alertDissmissed(id));
},
[dispatch]
);
const legacyAlerts = useAppSelector(getAlerts); const legacyAlerts = useAppSelector(getAlerts);
const dismissLegacy = React.useCallback((id: string) => { const dismissLegacy = React.useCallback(
dispatch(dismissAlert(id)); (id: string) => {
}, []); dispatch(dismissAlert(id));
},
[dispatch]
);
return ( return (
<> <>

View file

@ -36,11 +36,11 @@ const App: React.FC = () => {
React.useEffect(() => { React.useEffect(() => {
closeSidebar(); closeSidebar();
}, [location]); }, [closeSidebar, location]);
React.useEffect(() => { React.useEffect(() => {
dispatch(fetchClusters()); dispatch(fetchClusters());
}, [fetchClusters]); }, [dispatch]);
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>

View file

@ -35,7 +35,7 @@ const Brokers: React.FC = () => {
React.useEffect(() => { React.useEffect(() => {
dispatch(fetchClusterStats(clusterName)); dispatch(fetchClusterStats(clusterName));
}, [fetchClusterStats, clusterName]); }, [clusterName, dispatch]);
useInterval(() => { useInterval(() => {
fetchClusterStats(clusterName); fetchClusterStats(clusterName);

View file

@ -49,7 +49,12 @@ const Cluster: React.FC = () => {
hasSchemaRegistryConfigured, hasSchemaRegistryConfigured,
isTopicDeletionAllowed, isTopicDeletionAllowed,
}), }),
[features] [
hasKafkaConnectConfigured,
hasSchemaRegistryConfigured,
isReadOnly,
isTopicDeletionAllowed,
]
); );
return ( return (

View file

@ -70,7 +70,7 @@ const Actions: React.FC<ActionsProps> = ({
} catch { } catch {
// do not redirect // do not redirect
} }
}, [deleteConnector, clusterName, connectName, connectorName]); }, [deleteConnector, clusterName, connectName, connectorName, history]);
const restartConnectorHandler = React.useCallback(() => { const restartConnectorHandler = React.useCallback(() => {
restartConnector(clusterName, connectName, connectorName); restartConnector(clusterName, connectName, connectorName);

View file

@ -100,7 +100,7 @@ const Edit: React.FC<EditProps> = ({
); );
} }
}, },
[updateConfig, clusterName, connectName, connectorName] [updateConfig, clusterName, connectName, connectorName, history]
); );
if (isConfigFetching) return <PageLoader />; if (isConfigFetching) return <PageLoader />;

View file

@ -45,7 +45,7 @@ const ListItem: React.FC<ListItemProps> = ({
dispatch(deleteConnector(clusterName, connect, name)); dispatch(deleteConnector(clusterName, connect, name));
} }
setDeleteConnectorConfirmationVisible(false); setDeleteConnectorConfirmationVisible(false);
}, [clusterName, connect, name]); }, [clusterName, connect, dispatch, name]);
const runningTasks = React.useMemo(() => { const runningTasks = React.useMemo(() => {
if (!tasksCount) return null; if (!tasksCount) return null;

View file

@ -101,7 +101,7 @@ const New: React.FC<NewProps> = ({
); );
} }
}, },
[createConnector, clusterName] [createConnector, clusterName, history]
); );
if (areConnectsFetching) { if (areConnectsFetching) {

View file

@ -18,7 +18,7 @@ const ConsumerGroups: React.FC = () => {
const isFetched = useAppSelector(getAreConsumerGroupsFulfilled); const isFetched = useAppSelector(getAreConsumerGroupsFulfilled);
React.useEffect(() => { React.useEffect(() => {
dispatch(fetchConsumerGroups(clusterName)); dispatch(fetchConsumerGroups(clusterName));
}, [fetchConsumerGroups, clusterName]); }, [clusterName, dispatch]);
if (isFetched) { if (isFetched) {
return ( return (

View file

@ -47,7 +47,7 @@ const Details: React.FC = () => {
React.useEffect(() => { React.useEffect(() => {
dispatch(fetchConsumerGroupDetails({ clusterName, consumerGroupID })); dispatch(fetchConsumerGroupDetails({ clusterName, consumerGroupID }));
}, [fetchConsumerGroupDetails, clusterName, consumerGroupID]); }, [clusterName, consumerGroupID, dispatch]);
const onDelete = () => { const onDelete = () => {
setIsConfirmationModalVisible(false); setIsConfirmationModalVisible(false);
@ -57,7 +57,7 @@ const Details: React.FC = () => {
if (isDeleted) { if (isDeleted) {
history.push(clusterConsumerGroupsPath(clusterName)); history.push(clusterConsumerGroupsPath(clusterName));
} }
}, [isDeleted]); }, [clusterName, history, isDeleted]);
const onResetOffsets = () => { const onResetOffsets = () => {
history.push( history.push(

View file

@ -59,7 +59,7 @@ const ResetOffsets: React.FC = () => {
React.useEffect(() => { React.useEffect(() => {
dispatch(fetchConsumerGroupDetails({ clusterName, consumerGroupID })); dispatch(fetchConsumerGroupDetails({ clusterName, consumerGroupID }));
}, [clusterName, consumerGroupID]); }, [clusterName, consumerGroupID, dispatch]);
const [uniqueTopics, setUniqueTopics] = React.useState<string[]>([]); const [uniqueTopics, setUniqueTopics] = React.useState<string[]>([]);
const [selectedPartitions, setSelectedPartitions] = React.useState<Option[]>( const [selectedPartitions, setSelectedPartitions] = React.useState<Option[]>(
@ -96,7 +96,7 @@ const ResetOffsets: React.FC = () => {
setValue('topic', consumerGroup.partitions[0].topic); setValue('topic', consumerGroup.partitions[0].topic);
setUniqueTopics(Object.keys(groupBy(consumerGroup.partitions, 'topic'))); setUniqueTopics(Object.keys(groupBy(consumerGroup.partitions, 'topic')));
} }
}, [isFetched]); }, [consumerGroup?.partitions, isFetched, setValue]);
const onSelectedPartitionsChange = (value: Option[]) => { const onSelectedPartitionsChange = (value: Option[]) => {
clearErrors(); clearErrors();
@ -117,6 +117,7 @@ const ResetOffsets: React.FC = () => {
React.useEffect(() => { React.useEffect(() => {
onSelectedPartitionsChange([]); onSelectedPartitionsChange([]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [topicValue]); }, [topicValue]);
const onSubmit = (data: FormType) => { const onSubmit = (data: FormType) => {
@ -169,7 +170,7 @@ const ResetOffsets: React.FC = () => {
clusterConsumerGroupDetailsPath(clusterName, consumerGroupID) clusterConsumerGroupDetailsPath(clusterName, consumerGroupID)
); );
} }
}, [isOffsetReseted]); }, [clusterName, consumerGroupID, dispatch, history, isOffsetReseted]);
if (!isFetched || !consumerGroup) { if (!isFetched || !consumerGroup) {
return <PageLoader />; return <PageLoader />;

View file

@ -32,7 +32,7 @@ const List: FC = () => {
useEffect(() => { useEffect(() => {
dispatch(fetchKsqlDbTables(clusterName)); dispatch(fetchKsqlDbTables(clusterName));
}, []); }, [clusterName, dispatch]);
return ( return (
<> <>

View file

@ -42,7 +42,7 @@ const Query: FC = () => {
useEffect(() => { useEffect(() => {
return reset; return reset;
}, []); }, [reset]);
const { handleSubmit, setValue, control } = useForm<FormValues>({ const { handleSubmit, setValue, control } = useForm<FormValues>({
mode: 'onTouched', mode: 'onTouched',
@ -53,19 +53,22 @@ const Query: FC = () => {
}, },
}); });
const submitHandler = useCallback(async (values: FormValues) => { const submitHandler = useCallback(
dispatch( async (values: FormValues) => {
executeKsql({ dispatch(
clusterName, executeKsql({
ksqlCommand: { clusterName,
...values, ksqlCommand: {
streamsProperties: values.streamsProperties ...values,
? JSON.parse(values.streamsProperties) streamsProperties: values.streamsProperties
: undefined, ? JSON.parse(values.streamsProperties)
}, : undefined,
}) },
); })
}, []); );
},
[clusterName, dispatch]
);
return ( return (
<> <>

View file

@ -20,6 +20,7 @@ const ResultRenderer: React.FC<{ result: KsqlCommandResponse | null }> = ({
const { headers, rows } = rawTable; const { headers, rows } = rawTable;
// eslint-disable-next-line react-hooks/rules-of-hooks
const transformedRows = React.useMemo( const transformedRows = React.useMemo(
() => () =>
rows.map((row) => rows.map((row) =>
@ -31,7 +32,7 @@ const ResultRenderer: React.FC<{ result: KsqlCommandResponse | null }> = ({
{} as Dictionary<string> {} as Dictionary<string>
) )
), ),
[] [rawTable.headers, rows]
); );
return ( return (

View file

@ -47,14 +47,14 @@ const Details: React.FC = () => {
return () => { return () => {
dispatch(resetLoaderById(SCHEMA_LATEST_FETCH_ACTION)); dispatch(resetLoaderById(SCHEMA_LATEST_FETCH_ACTION));
}; };
}, []); }, [clusterName, dispatch, subject]);
React.useEffect(() => { React.useEffect(() => {
dispatch(fetchSchemaVersions({ clusterName, subject })); dispatch(fetchSchemaVersions({ clusterName, subject }));
return () => { return () => {
dispatch(resetLoaderById(SCHEMAS_VERSIONS_FETCH_ACTION)); dispatch(resetLoaderById(SCHEMAS_VERSIONS_FETCH_ACTION));
}; };
}, [clusterName, subject]); }, [clusterName, dispatch, subject]);
const versions = useAppSelector((state) => selectAllSchemaVersions(state)); const versions = useAppSelector((state) => selectAllSchemaVersions(state));
const schema = useAppSelector(getSchemaLatest); const schema = useAppSelector(getSchemaLatest);
@ -72,7 +72,7 @@ const Details: React.FC = () => {
const err = await getResponse(e as Response); const err = await getResponse(e as Response);
dispatch(serverErrorAlertAdded(err)); dispatch(serverErrorAlertAdded(err));
} }
}, [clusterName, subject]); }, [clusterName, dispatch, history, subject]);
if (!isFetched || !schema) { if (!isFetched || !schema) {
return <PageLoader />; return <PageLoader />;

View file

@ -47,7 +47,7 @@ const Edit: React.FC = () => {
return () => { return () => {
dispatch(resetLoaderById(SCHEMA_LATEST_FETCH_ACTION)); dispatch(resetLoaderById(SCHEMA_LATEST_FETCH_ACTION));
}; };
}, [clusterName, subject]); }, [clusterName, dispatch, subject]);
const schema = useAppSelector((state) => getSchemaLatest(state)); const schema = useAppSelector((state) => getSchemaLatest(state));
const isFetched = useAppSelector(getAreSchemaLatestFulfilled); const isFetched = useAppSelector(getAreSchemaLatestFulfilled);
@ -58,44 +58,56 @@ const Edit: React.FC = () => {
: JSON.stringify(JSON.parse(schema?.schema || '{}'), null, '\t'); : JSON.stringify(JSON.parse(schema?.schema || '{}'), null, '\t');
}, [schema]); }, [schema]);
const onSubmit = React.useCallback(async (props: NewSchemaSubjectRaw) => { const onSubmit = React.useCallback(
if (!schema) return; async (props: NewSchemaSubjectRaw) => {
if (!schema) return;
try { try {
if (dirtyFields.newSchema || dirtyFields.schemaType) { if (dirtyFields.newSchema || dirtyFields.schemaType) {
const resp = await schemasApiClient.createNewSchema({ const resp = await schemasApiClient.createNewSchema({
clusterName, clusterName,
newSchemaSubject: { newSchemaSubject: {
...schema, ...schema,
schema: props.newSchema || schema.schema, schema: props.newSchema || schema.schema,
schemaType: props.schemaType || schema.schemaType, schemaType: props.schemaType || schema.schemaType,
}, },
}); });
dispatch(schemaAdded(resp)); dispatch(schemaAdded(resp));
}
if (dirtyFields.compatibilityLevel) {
await schemasApiClient.updateSchemaCompatibilityLevel({
clusterName,
subject,
compatibilityLevel: {
compatibility: props.compatibilityLevel,
},
});
dispatch(
schemaUpdated({
...schema,
compatibilityLevel: props.compatibilityLevel,
})
);
}
history.push(clusterSchemaPath(clusterName, subject));
} catch (e) {
const err = await getResponse(e as Response);
dispatch(serverErrorAlertAdded(err));
} }
},
if (dirtyFields.compatibilityLevel) { [
await schemasApiClient.updateSchemaCompatibilityLevel({ clusterName,
clusterName, dirtyFields.compatibilityLevel,
subject, dirtyFields.newSchema,
compatibilityLevel: { dirtyFields.schemaType,
compatibility: props.compatibilityLevel, dispatch,
}, history,
}); schema,
dispatch( subject,
schemaUpdated({ ]
...schema, );
compatibilityLevel: props.compatibilityLevel,
})
);
}
history.push(clusterSchemaPath(clusterName, subject));
} catch (e) {
const err = await getResponse(e as Response);
dispatch(serverErrorAlertAdded(err));
}
}, []);
if (!isFetched || !schema) { if (!isFetched || !schema) {
return <PageLoader />; return <PageLoader />;

View file

@ -48,7 +48,7 @@ const GlobalSchemaSelector: React.FC = () => {
}; };
fetchData(); fetchData();
}, []); }, [clusterName]);
const handleChangeCompatibilityLevel = (level: string | number) => { const handleChangeCompatibilityLevel = (level: string | number) => {
setNextCompatibilityLevel(level as CompatibilityLevelCompatibilityEnum); setNextCompatibilityLevel(level as CompatibilityLevelCompatibilityEnum);

View file

@ -41,7 +41,7 @@ const List: React.FC = () => {
return () => { return () => {
dispatch(resetLoaderById(SCHEMAS_FETCH_ACTION)); dispatch(resetLoaderById(SCHEMAS_FETCH_ACTION));
}; };
}, [clusterName, page, perPage, searchText]); }, [clusterName, dispatch, page, perPage, searchText]);
return ( return (
<> <>

View file

@ -55,7 +55,7 @@ const New: React.FC = () => {
dispatch(serverErrorAlertAdded(err)); dispatch(serverErrorAlertAdded(err));
} }
}, },
[clusterName] [clusterName, dispatch, history]
); );
return ( return (

View file

@ -93,7 +93,7 @@ const List: React.FC<TopicsListProps> = ({
const handleSwitch = React.useCallback(() => { const handleSwitch = React.useCallback(() => {
setShowInternal(!showInternal); setShowInternal(!showInternal);
history.push(`${pathname}?page=1&perPage=${perPage || PER_PAGE}`); history.push(`${pathname}?page=1&perPage=${perPage || PER_PAGE}`);
}, [showInternal]); }, [history, pathname, perPage, showInternal]);
const [confirmationModal, setConfirmationModal] = React.useState< const [confirmationModal, setConfirmationModal] = React.useState<
'' | 'deleteTopics' | 'purgeMessages' '' | 'deleteTopics' | 'purgeMessages'
@ -127,18 +127,18 @@ const List: React.FC<TopicsListProps> = ({
deleteTopics(clusterName, Array.from(selectedTopics)); deleteTopics(clusterName, Array.from(selectedTopics));
closeConfirmationModal(); closeConfirmationModal();
clearSelectedTopics(); clearSelectedTopics();
}, [clusterName, selectedTopics]); }, [clusterName, deleteTopics, selectedTopics]);
const purgeMessagesHandler = React.useCallback(() => { const purgeMessagesHandler = React.useCallback(() => {
clearTopicsMessages(clusterName, Array.from(selectedTopics)); clearTopicsMessages(clusterName, Array.from(selectedTopics));
closeConfirmationModal(); closeConfirmationModal();
clearSelectedTopics(); clearSelectedTopics();
}, [clusterName, selectedTopics]); }, [clearTopicsMessages, clusterName, selectedTopics]);
const searchHandler = React.useCallback( const searchHandler = React.useCallback(
(searchString: string) => { (searchString: string) => {
setTopicsSearch(searchString); setTopicsSearch(searchString);
history.push(`${pathname}?page=1&perPage=${perPage || PER_PAGE}`); history.push(`${pathname}?page=1&perPage=${perPage || PER_PAGE}`);
}, },
[search, pathname, perPage] [setTopicsSearch, history, pathname, perPage]
); );
return ( return (

View file

@ -70,11 +70,11 @@ const ListItem: React.FC<ListItemProps> = ({
const deleteTopicHandler = React.useCallback(() => { const deleteTopicHandler = React.useCallback(() => {
deleteTopic(clusterName, name); deleteTopic(clusterName, name);
}, [clusterName, name]); }, [clusterName, deleteTopic, name]);
const clearTopicMessagesHandler = React.useCallback(() => { const clearTopicMessagesHandler = React.useCallback(() => {
clearTopicMessages(clusterName, name); clearTopicMessages(clusterName, name);
}, [clusterName, name]); }, [clearTopicMessages, clusterName, name]);
const [vElipsisVisble, setVElipsisVisble] = React.useState(false); const [vElipsisVisble, setVElipsisVisble] = React.useState(false);
return ( return (

View file

@ -27,7 +27,7 @@ const TopicConsumerGroups: React.FC<Props> = ({
}) => { }) => {
React.useEffect(() => { React.useEffect(() => {
fetchTopicConsumerGroups(clusterName, topicName); fetchTopicConsumerGroups(clusterName, topicName);
}, []); }, [clusterName, fetchTopicConsumerGroups, topicName]);
return ( return (
<div> <div>

View file

@ -64,19 +64,19 @@ const Details: React.FC<Props> = ({
React.useState(false); React.useState(false);
const deleteTopicHandler = React.useCallback(() => { const deleteTopicHandler = React.useCallback(() => {
deleteTopic(clusterName, topicName); deleteTopic(clusterName, topicName);
}, [clusterName, topicName]); }, [clusterName, deleteTopic, topicName]);
React.useEffect(() => { React.useEffect(() => {
if (isDeleted) { if (isDeleted) {
dispatch(deleteTopicAction.cancel()); dispatch(deleteTopicAction.cancel());
history.push(clusterTopicsPath(clusterName)); history.push(clusterTopicsPath(clusterName));
} }
}, [isDeleted]); }, [clusterName, dispatch, history, isDeleted]);
const clearTopicMessagesHandler = React.useCallback(() => { const clearTopicMessagesHandler = React.useCallback(() => {
clearTopicMessages(clusterName, topicName); clearTopicMessages(clusterName, topicName);
setClearTopicConfirmationVisible(false); setClearTopicConfirmationVisible(false);
}, [clusterName, topicName]); }, [clearTopicMessages, clusterName, topicName]);
return ( return (
<div> <div>

View file

@ -126,7 +126,7 @@ const Filters: React.FC<FiltersProps> = ({
[partitions] [partitions]
); );
const handleFiltersSubmit = () => { const handleFiltersSubmit = React.useCallback(() => {
setAttempt(attempt + 1); setAttempt(attempt + 1);
const props: Query = { const props: Query = {
@ -166,7 +166,8 @@ const Filters: React.FC<FiltersProps> = ({
history.push({ history.push({
search: `?${qs}`, search: `?${qs}`,
}); });
}; // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const toggleSeekDirection = (val: string) => { const toggleSeekDirection = (val: string) => {
const nextSeekDirectionValue = const nextSeekDirectionValue =
@ -224,17 +225,26 @@ const Filters: React.FC<FiltersProps> = ({
sse.close(); sse.close();
}; };
} }
}, [clusterName, topicName, location]); }, [
clusterName,
topicName,
location,
setIsFetching,
resetMessages,
addMessage,
updatePhase,
updateMeta,
]);
React.useEffect(() => { React.useEffect(() => {
if (location.search.length === 0) { if (location.search.length === 0) {
handleFiltersSubmit(); handleFiltersSubmit();
} }
}, [location]); }, [handleFiltersSubmit, location]);
React.useEffect(() => { React.useEffect(() => {
handleFiltersSubmit(); handleFiltersSubmit();
}, [seekDirection]); }, [handleFiltersSubmit, seekDirection]);
return ( return (
<S.FiltersWrapper> <S.FiltersWrapper>

View file

@ -27,7 +27,7 @@ const MessagesTable: React.FC = () => {
const searchParams = React.useMemo( const searchParams = React.useMemo(
() => new URLSearchParams(location.search), () => new URLSearchParams(location.search),
[location, history] [location]
); );
const messages = useSelector(getTopicMessges); const messages = useSelector(getTopicMessges);

View file

@ -88,10 +88,7 @@ const Edit: React.FC<Props> = ({
fetchTopicConfig, fetchTopicConfig,
updateTopic, updateTopic,
}) => { }) => {
const defaultValues = React.useMemo( const defaultValues = React.useMemo(() => topicParams(topic), [topic]);
() => topicParams(topic),
[topicParams, topic]
);
const methods = useForm<TopicFormData>({ const methods = useForm<TopicFormData>({
defaultValues, defaultValues,
resolver: yupResolver(topicFormValidationSchema), resolver: yupResolver(topicFormValidationSchema),
@ -109,7 +106,7 @@ const Edit: React.FC<Props> = ({
const { name } = methods.getValues(); const { name } = methods.getValues();
history.push(clusterTopicPath(clusterName, name)); history.push(clusterTopicPath(clusterName, name));
} }
}, [isSubmitting, isTopicUpdated, clusterTopicPath, clusterName, methods]); }, [isSubmitting, isTopicUpdated, clusterName, methods, history]);
if (!isFetched || !topic || !topic.config) { if (!isFetched || !topic || !topic.config) {
return null; return null;

View file

@ -41,7 +41,7 @@ const SendMessage: React.FC = () => {
React.useEffect(() => { React.useEffect(() => {
dispatch(fetchTopicMessageSchema(clusterName, topicName)); dispatch(fetchTopicMessageSchema(clusterName, topicName));
}, []); }, [clusterName, dispatch, topicName]);
const messageSchema = useAppSelector((state) => const messageSchema = useAppSelector((state) =>
getMessageSchemaByTopicName(state, topicName) getMessageSchemaByTopicName(state, topicName)

View file

@ -54,7 +54,7 @@ const CustomParamField: React.FC<Props> = ({
shouldValidate: true, shouldValidate: true,
}); });
} }
}, [nameValue]); }, [existingFields, index, nameValue, setExistingFields, setValue]);
return ( return (
<C.Column> <C.Column>

View file

@ -15,6 +15,7 @@ const BreadcrumbRouteInternal: React.FC = () => {
useEffect(() => { useEffect(() => {
context.handleRouteChange({ ...match, url: location.pathname }); context.handleRouteChange({ ...match, url: location.pathname });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location.pathname]); }, [location.pathname]);
return null; return null;

View file

@ -13,7 +13,7 @@ const Breadcrumb: React.FC = () => {
const links = React.useMemo( const links = React.useMemo(
() => breadcrumbContext.path.slice(basePathEntriesLength), () => breadcrumbContext.path.slice(basePathEntriesLength),
[breadcrumbContext.link] [breadcrumbContext.path]
); );
const getPathPredicate = React.useCallback( const getPathPredicate = React.useCallback(

View file

@ -21,7 +21,7 @@ const BytesFormatted: React.FC<Props> = ({ value, precision = 0 }) => {
} catch (e) { } catch (e) {
return `-Bytes`; return `-Bytes`;
} }
}, [value]); }, [precision, value]);
return <span>{formatedValue}</span>; return <span>{formatedValue}</span>;
}; };

View file

@ -19,14 +19,14 @@ const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
onConfirm, onConfirm,
isConfirming = false, isConfirming = false,
}) => { }) => {
if (!isOpen) return null;
const cancelHandler = React.useCallback(() => { const cancelHandler = React.useCallback(() => {
if (!isConfirming) { if (!isConfirming) {
onCancel(); onCancel();
} }
}, [isConfirming, onCancel]); }, [isConfirming, onCancel]);
if (!isOpen) return null;
return ( return (
<ConfirmationModalWrapper> <ConfirmationModalWrapper>
<div onClick={cancelHandler} aria-hidden="true" /> <div onClick={cancelHandler} aria-hidden="true" />

View file

@ -22,7 +22,7 @@ const Dropdown: React.FC<DropdownProps> = ({ label, right, up, children }) => {
'is-right': right, 'is-right': right,
'is-up': up, 'is-up': up,
}), }),
[active] [active, right, up]
); );
return ( return (
<div className={classNames} ref={wrapperRef}> <div className={classNames} ref={wrapperRef}>

View file

@ -1,4 +1,4 @@
import React, { useCallback } from 'react'; import React, { useCallback, useRef } from 'react';
import cx from 'classnames'; import cx from 'classnames';
interface DynamicTextButtonProps { interface DynamicTextButtonProps {
@ -18,15 +18,15 @@ const DynamicTextButton: React.FC<DynamicTextButtonProps> = ({
}) => { }) => {
const [clicked, setClicked] = React.useState(false); const [clicked, setClicked] = React.useState(false);
let timeout: number; const timeout = useRef(0);
const clickHandler = useCallback(() => { const clickHandler = useCallback(() => {
onClick(); onClick();
setClicked(true); setClicked(true);
timeout = window.setTimeout(() => setClicked(false), delay); timeout.current = window.setTimeout(() => setClicked(false), delay);
}, []); }, [delay, onClick]);
React.useEffect(() => () => window.clearTimeout(timeout), []); React.useEffect(() => () => window.clearTimeout(timeout.current));
return ( return (
<button <button

View file

@ -68,7 +68,7 @@ const Pagination: React.FC<PaginationProps> = ({ totalPages }) => {
} }
return p; return p;
}, []); }, [currentPage, totalPages]);
return ( return (
<S.Wrapper role="navigation" aria-label="pagination"> <S.Wrapper role="navigation" aria-label="pagination">

View file

@ -21,10 +21,13 @@ const Tabs: React.FC<TabsProps> = ({
setSelectedIndex(defaultSelectedIndex); setSelectedIndex(defaultSelectedIndex);
}, [defaultSelectedIndex]); }, [defaultSelectedIndex]);
const handleChange = React.useCallback((index: number) => { const handleChange = React.useCallback(
setSelectedIndex(index); (index: number) => {
onChange?.(index); setSelectedIndex(index);
}, []); onChange?.(index);
},
[onChange]
);
return ( return (
<> <>

View file

@ -25,7 +25,7 @@ describe('useDataSaver hook', () => {
const HookWrapper: React.FC = () => { const HookWrapper: React.FC = () => {
const { saveFile } = useDataSaver('message', content); const { saveFile } = useDataSaver('message', content);
useEffect(() => saveFile(), []); useEffect(() => saveFile(), [saveFile]);
return null; return null;
}; };
@ -52,7 +52,7 @@ describe('useDataSaver hook', () => {
const HookWrapper: React.FC = () => { const HookWrapper: React.FC = () => {
const { saveFile } = useDataSaver('message', 'content'); const { saveFile } = useDataSaver('message', 'content');
useEffect(() => saveFile(), []); useEffect(() => saveFile(), [saveFile]);
return null; return null;
}; };
@ -81,7 +81,7 @@ describe('useDataSaver hook', () => {
it('data with type Object', () => { it('data with type Object', () => {
const HookWrapper: React.FC = () => { const HookWrapper: React.FC = () => {
const { copyToClipboard } = useDataSaver('topic', content); const { copyToClipboard } = useDataSaver('topic', content);
useEffect(() => copyToClipboard(), []); useEffect(() => copyToClipboard(), [copyToClipboard]);
return null; return null;
}; };
render(<HookWrapper />); render(<HookWrapper />);
@ -96,7 +96,7 @@ describe('useDataSaver hook', () => {
'topic', 'topic',
'{ title: "title", }' '{ title: "title", }'
); );
useEffect(() => copyToClipboard(), []); useEffect(() => copyToClipboard(), [copyToClipboard]);
return null; return null;
}; };
render(<HookWrapper />); render(<HookWrapper />);

View file

@ -21,7 +21,7 @@ const useSearch = (initValue = ''): [string, (value: string) => void] => {
queryParams.set(SEARCH_QUERY_ARG, initValue.trim()); queryParams.set(SEARCH_QUERY_ARG, initValue.trim());
history.push({ pathname, search: queryParams.toString() }); history.push({ pathname, search: queryParams.toString() });
} }
}, []); }, [history, initValue, pathname, q, queryParams]);
const handleChange = useCallback( const handleChange = useCallback(
(value: string) => { (value: string) => {
@ -39,7 +39,7 @@ const useSearch = (initValue = ''): [string, (value: string) => void] => {
history.replace({ pathname, search: queryParams.toString() }); history.replace({ pathname, search: queryParams.toString() });
} }
}, },
[history, pathname, queryParams, q] [q, page, history, pathname, queryParams]
); );
return [q || initValue.trim() || '', handleChange]; return [q || initValue.trim() || '', handleChange];