[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:
parent
3f0693bad6
commit
94b1f4a772
39 changed files with 166 additions and 124 deletions
|
@ -22,7 +22,8 @@
|
|||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"prettier"
|
||||
"prettier",
|
||||
"eslint-plugin-react-hooks"
|
||||
],
|
||||
"extends": [
|
||||
"airbnb",
|
||||
|
@ -33,6 +34,8 @@
|
|||
"prettier"
|
||||
],
|
||||
"rules": {
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-hooks/exhaustive-deps": "warn",
|
||||
"react/no-unused-prop-types": "off",
|
||||
"react/require-default-props": "off",
|
||||
"prettier/prettier": "warn",
|
||||
|
|
|
@ -8,14 +8,20 @@ import Alert from 'components/Alerts/Alert';
|
|||
const Alerts: React.FC = () => {
|
||||
const alerts = useAppSelector(selectAll);
|
||||
const dispatch = useAppDispatch();
|
||||
const dismiss = React.useCallback((id: string) => {
|
||||
const dismiss = React.useCallback(
|
||||
(id: string) => {
|
||||
dispatch(alertDissmissed(id));
|
||||
}, []);
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const legacyAlerts = useAppSelector(getAlerts);
|
||||
const dismissLegacy = React.useCallback((id: string) => {
|
||||
const dismissLegacy = React.useCallback(
|
||||
(id: string) => {
|
||||
dispatch(dismissAlert(id));
|
||||
}, []);
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -36,11 +36,11 @@ const App: React.FC = () => {
|
|||
|
||||
React.useEffect(() => {
|
||||
closeSidebar();
|
||||
}, [location]);
|
||||
}, [closeSidebar, location]);
|
||||
|
||||
React.useEffect(() => {
|
||||
dispatch(fetchClusters());
|
||||
}, [fetchClusters]);
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
|
|
|
@ -35,7 +35,7 @@ const Brokers: React.FC = () => {
|
|||
|
||||
React.useEffect(() => {
|
||||
dispatch(fetchClusterStats(clusterName));
|
||||
}, [fetchClusterStats, clusterName]);
|
||||
}, [clusterName, dispatch]);
|
||||
|
||||
useInterval(() => {
|
||||
fetchClusterStats(clusterName);
|
||||
|
|
|
@ -49,7 +49,12 @@ const Cluster: React.FC = () => {
|
|||
hasSchemaRegistryConfigured,
|
||||
isTopicDeletionAllowed,
|
||||
}),
|
||||
[features]
|
||||
[
|
||||
hasKafkaConnectConfigured,
|
||||
hasSchemaRegistryConfigured,
|
||||
isReadOnly,
|
||||
isTopicDeletionAllowed,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -70,7 +70,7 @@ const Actions: React.FC<ActionsProps> = ({
|
|||
} catch {
|
||||
// do not redirect
|
||||
}
|
||||
}, [deleteConnector, clusterName, connectName, connectorName]);
|
||||
}, [deleteConnector, clusterName, connectName, connectorName, history]);
|
||||
|
||||
const restartConnectorHandler = React.useCallback(() => {
|
||||
restartConnector(clusterName, connectName, connectorName);
|
||||
|
|
|
@ -100,7 +100,7 @@ const Edit: React.FC<EditProps> = ({
|
|||
);
|
||||
}
|
||||
},
|
||||
[updateConfig, clusterName, connectName, connectorName]
|
||||
[updateConfig, clusterName, connectName, connectorName, history]
|
||||
);
|
||||
|
||||
if (isConfigFetching) return <PageLoader />;
|
||||
|
|
|
@ -45,7 +45,7 @@ const ListItem: React.FC<ListItemProps> = ({
|
|||
dispatch(deleteConnector(clusterName, connect, name));
|
||||
}
|
||||
setDeleteConnectorConfirmationVisible(false);
|
||||
}, [clusterName, connect, name]);
|
||||
}, [clusterName, connect, dispatch, name]);
|
||||
|
||||
const runningTasks = React.useMemo(() => {
|
||||
if (!tasksCount) return null;
|
||||
|
|
|
@ -101,7 +101,7 @@ const New: React.FC<NewProps> = ({
|
|||
);
|
||||
}
|
||||
},
|
||||
[createConnector, clusterName]
|
||||
[createConnector, clusterName, history]
|
||||
);
|
||||
|
||||
if (areConnectsFetching) {
|
||||
|
|
|
@ -18,7 +18,7 @@ const ConsumerGroups: React.FC = () => {
|
|||
const isFetched = useAppSelector(getAreConsumerGroupsFulfilled);
|
||||
React.useEffect(() => {
|
||||
dispatch(fetchConsumerGroups(clusterName));
|
||||
}, [fetchConsumerGroups, clusterName]);
|
||||
}, [clusterName, dispatch]);
|
||||
|
||||
if (isFetched) {
|
||||
return (
|
||||
|
|
|
@ -47,7 +47,7 @@ const Details: React.FC = () => {
|
|||
|
||||
React.useEffect(() => {
|
||||
dispatch(fetchConsumerGroupDetails({ clusterName, consumerGroupID }));
|
||||
}, [fetchConsumerGroupDetails, clusterName, consumerGroupID]);
|
||||
}, [clusterName, consumerGroupID, dispatch]);
|
||||
|
||||
const onDelete = () => {
|
||||
setIsConfirmationModalVisible(false);
|
||||
|
@ -57,7 +57,7 @@ const Details: React.FC = () => {
|
|||
if (isDeleted) {
|
||||
history.push(clusterConsumerGroupsPath(clusterName));
|
||||
}
|
||||
}, [isDeleted]);
|
||||
}, [clusterName, history, isDeleted]);
|
||||
|
||||
const onResetOffsets = () => {
|
||||
history.push(
|
||||
|
|
|
@ -59,7 +59,7 @@ const ResetOffsets: React.FC = () => {
|
|||
|
||||
React.useEffect(() => {
|
||||
dispatch(fetchConsumerGroupDetails({ clusterName, consumerGroupID }));
|
||||
}, [clusterName, consumerGroupID]);
|
||||
}, [clusterName, consumerGroupID, dispatch]);
|
||||
|
||||
const [uniqueTopics, setUniqueTopics] = React.useState<string[]>([]);
|
||||
const [selectedPartitions, setSelectedPartitions] = React.useState<Option[]>(
|
||||
|
@ -96,7 +96,7 @@ const ResetOffsets: React.FC = () => {
|
|||
setValue('topic', consumerGroup.partitions[0].topic);
|
||||
setUniqueTopics(Object.keys(groupBy(consumerGroup.partitions, 'topic')));
|
||||
}
|
||||
}, [isFetched]);
|
||||
}, [consumerGroup?.partitions, isFetched, setValue]);
|
||||
|
||||
const onSelectedPartitionsChange = (value: Option[]) => {
|
||||
clearErrors();
|
||||
|
@ -117,6 +117,7 @@ const ResetOffsets: React.FC = () => {
|
|||
|
||||
React.useEffect(() => {
|
||||
onSelectedPartitionsChange([]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [topicValue]);
|
||||
|
||||
const onSubmit = (data: FormType) => {
|
||||
|
@ -169,7 +170,7 @@ const ResetOffsets: React.FC = () => {
|
|||
clusterConsumerGroupDetailsPath(clusterName, consumerGroupID)
|
||||
);
|
||||
}
|
||||
}, [isOffsetReseted]);
|
||||
}, [clusterName, consumerGroupID, dispatch, history, isOffsetReseted]);
|
||||
|
||||
if (!isFetched || !consumerGroup) {
|
||||
return <PageLoader />;
|
||||
|
|
|
@ -32,7 +32,7 @@ const List: FC = () => {
|
|||
|
||||
useEffect(() => {
|
||||
dispatch(fetchKsqlDbTables(clusterName));
|
||||
}, []);
|
||||
}, [clusterName, dispatch]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -42,7 +42,7 @@ const Query: FC = () => {
|
|||
|
||||
useEffect(() => {
|
||||
return reset;
|
||||
}, []);
|
||||
}, [reset]);
|
||||
|
||||
const { handleSubmit, setValue, control } = useForm<FormValues>({
|
||||
mode: 'onTouched',
|
||||
|
@ -53,7 +53,8 @@ const Query: FC = () => {
|
|||
},
|
||||
});
|
||||
|
||||
const submitHandler = useCallback(async (values: FormValues) => {
|
||||
const submitHandler = useCallback(
|
||||
async (values: FormValues) => {
|
||||
dispatch(
|
||||
executeKsql({
|
||||
clusterName,
|
||||
|
@ -65,7 +66,9 @@ const Query: FC = () => {
|
|||
},
|
||||
})
|
||||
);
|
||||
}, []);
|
||||
},
|
||||
[clusterName, dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -20,6 +20,7 @@ const ResultRenderer: React.FC<{ result: KsqlCommandResponse | null }> = ({
|
|||
|
||||
const { headers, rows } = rawTable;
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const transformedRows = React.useMemo(
|
||||
() =>
|
||||
rows.map((row) =>
|
||||
|
@ -31,7 +32,7 @@ const ResultRenderer: React.FC<{ result: KsqlCommandResponse | null }> = ({
|
|||
{} as Dictionary<string>
|
||||
)
|
||||
),
|
||||
[]
|
||||
[rawTable.headers, rows]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -47,14 +47,14 @@ const Details: React.FC = () => {
|
|||
return () => {
|
||||
dispatch(resetLoaderById(SCHEMA_LATEST_FETCH_ACTION));
|
||||
};
|
||||
}, []);
|
||||
}, [clusterName, dispatch, subject]);
|
||||
|
||||
React.useEffect(() => {
|
||||
dispatch(fetchSchemaVersions({ clusterName, subject }));
|
||||
return () => {
|
||||
dispatch(resetLoaderById(SCHEMAS_VERSIONS_FETCH_ACTION));
|
||||
};
|
||||
}, [clusterName, subject]);
|
||||
}, [clusterName, dispatch, subject]);
|
||||
|
||||
const versions = useAppSelector((state) => selectAllSchemaVersions(state));
|
||||
const schema = useAppSelector(getSchemaLatest);
|
||||
|
@ -72,7 +72,7 @@ const Details: React.FC = () => {
|
|||
const err = await getResponse(e as Response);
|
||||
dispatch(serverErrorAlertAdded(err));
|
||||
}
|
||||
}, [clusterName, subject]);
|
||||
}, [clusterName, dispatch, history, subject]);
|
||||
|
||||
if (!isFetched || !schema) {
|
||||
return <PageLoader />;
|
||||
|
|
|
@ -47,7 +47,7 @@ const Edit: React.FC = () => {
|
|||
return () => {
|
||||
dispatch(resetLoaderById(SCHEMA_LATEST_FETCH_ACTION));
|
||||
};
|
||||
}, [clusterName, subject]);
|
||||
}, [clusterName, dispatch, subject]);
|
||||
|
||||
const schema = useAppSelector((state) => getSchemaLatest(state));
|
||||
const isFetched = useAppSelector(getAreSchemaLatestFulfilled);
|
||||
|
@ -58,7 +58,8 @@ const Edit: React.FC = () => {
|
|||
: JSON.stringify(JSON.parse(schema?.schema || '{}'), null, '\t');
|
||||
}, [schema]);
|
||||
|
||||
const onSubmit = React.useCallback(async (props: NewSchemaSubjectRaw) => {
|
||||
const onSubmit = React.useCallback(
|
||||
async (props: NewSchemaSubjectRaw) => {
|
||||
if (!schema) return;
|
||||
|
||||
try {
|
||||
|
@ -95,7 +96,18 @@ const Edit: React.FC = () => {
|
|||
const err = await getResponse(e as Response);
|
||||
dispatch(serverErrorAlertAdded(err));
|
||||
}
|
||||
}, []);
|
||||
},
|
||||
[
|
||||
clusterName,
|
||||
dirtyFields.compatibilityLevel,
|
||||
dirtyFields.newSchema,
|
||||
dirtyFields.schemaType,
|
||||
dispatch,
|
||||
history,
|
||||
schema,
|
||||
subject,
|
||||
]
|
||||
);
|
||||
|
||||
if (!isFetched || !schema) {
|
||||
return <PageLoader />;
|
||||
|
|
|
@ -48,7 +48,7 @@ const GlobalSchemaSelector: React.FC = () => {
|
|||
};
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
}, [clusterName]);
|
||||
|
||||
const handleChangeCompatibilityLevel = (level: string | number) => {
|
||||
setNextCompatibilityLevel(level as CompatibilityLevelCompatibilityEnum);
|
||||
|
|
|
@ -41,7 +41,7 @@ const List: React.FC = () => {
|
|||
return () => {
|
||||
dispatch(resetLoaderById(SCHEMAS_FETCH_ACTION));
|
||||
};
|
||||
}, [clusterName, page, perPage, searchText]);
|
||||
}, [clusterName, dispatch, page, perPage, searchText]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -55,7 +55,7 @@ const New: React.FC = () => {
|
|||
dispatch(serverErrorAlertAdded(err));
|
||||
}
|
||||
},
|
||||
[clusterName]
|
||||
[clusterName, dispatch, history]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -93,7 +93,7 @@ const List: React.FC<TopicsListProps> = ({
|
|||
const handleSwitch = React.useCallback(() => {
|
||||
setShowInternal(!showInternal);
|
||||
history.push(`${pathname}?page=1&perPage=${perPage || PER_PAGE}`);
|
||||
}, [showInternal]);
|
||||
}, [history, pathname, perPage, showInternal]);
|
||||
|
||||
const [confirmationModal, setConfirmationModal] = React.useState<
|
||||
'' | 'deleteTopics' | 'purgeMessages'
|
||||
|
@ -127,18 +127,18 @@ const List: React.FC<TopicsListProps> = ({
|
|||
deleteTopics(clusterName, Array.from(selectedTopics));
|
||||
closeConfirmationModal();
|
||||
clearSelectedTopics();
|
||||
}, [clusterName, selectedTopics]);
|
||||
}, [clusterName, deleteTopics, selectedTopics]);
|
||||
const purgeMessagesHandler = React.useCallback(() => {
|
||||
clearTopicsMessages(clusterName, Array.from(selectedTopics));
|
||||
closeConfirmationModal();
|
||||
clearSelectedTopics();
|
||||
}, [clusterName, selectedTopics]);
|
||||
}, [clearTopicsMessages, clusterName, selectedTopics]);
|
||||
const searchHandler = React.useCallback(
|
||||
(searchString: string) => {
|
||||
setTopicsSearch(searchString);
|
||||
history.push(`${pathname}?page=1&perPage=${perPage || PER_PAGE}`);
|
||||
},
|
||||
[search, pathname, perPage]
|
||||
[setTopicsSearch, history, pathname, perPage]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -70,11 +70,11 @@ const ListItem: React.FC<ListItemProps> = ({
|
|||
|
||||
const deleteTopicHandler = React.useCallback(() => {
|
||||
deleteTopic(clusterName, name);
|
||||
}, [clusterName, name]);
|
||||
}, [clusterName, deleteTopic, name]);
|
||||
|
||||
const clearTopicMessagesHandler = React.useCallback(() => {
|
||||
clearTopicMessages(clusterName, name);
|
||||
}, [clusterName, name]);
|
||||
}, [clearTopicMessages, clusterName, name]);
|
||||
const [vElipsisVisble, setVElipsisVisble] = React.useState(false);
|
||||
|
||||
return (
|
||||
|
|
|
@ -27,7 +27,7 @@ const TopicConsumerGroups: React.FC<Props> = ({
|
|||
}) => {
|
||||
React.useEffect(() => {
|
||||
fetchTopicConsumerGroups(clusterName, topicName);
|
||||
}, []);
|
||||
}, [clusterName, fetchTopicConsumerGroups, topicName]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
|
@ -64,19 +64,19 @@ const Details: React.FC<Props> = ({
|
|||
React.useState(false);
|
||||
const deleteTopicHandler = React.useCallback(() => {
|
||||
deleteTopic(clusterName, topicName);
|
||||
}, [clusterName, topicName]);
|
||||
}, [clusterName, deleteTopic, topicName]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isDeleted) {
|
||||
dispatch(deleteTopicAction.cancel());
|
||||
history.push(clusterTopicsPath(clusterName));
|
||||
}
|
||||
}, [isDeleted]);
|
||||
}, [clusterName, dispatch, history, isDeleted]);
|
||||
|
||||
const clearTopicMessagesHandler = React.useCallback(() => {
|
||||
clearTopicMessages(clusterName, topicName);
|
||||
setClearTopicConfirmationVisible(false);
|
||||
}, [clusterName, topicName]);
|
||||
}, [clearTopicMessages, clusterName, topicName]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
|
@ -126,7 +126,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
[partitions]
|
||||
);
|
||||
|
||||
const handleFiltersSubmit = () => {
|
||||
const handleFiltersSubmit = React.useCallback(() => {
|
||||
setAttempt(attempt + 1);
|
||||
|
||||
const props: Query = {
|
||||
|
@ -166,7 +166,8 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
history.push({
|
||||
search: `?${qs}`,
|
||||
});
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const toggleSeekDirection = (val: string) => {
|
||||
const nextSeekDirectionValue =
|
||||
|
@ -224,17 +225,26 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
sse.close();
|
||||
};
|
||||
}
|
||||
}, [clusterName, topicName, location]);
|
||||
}, [
|
||||
clusterName,
|
||||
topicName,
|
||||
location,
|
||||
setIsFetching,
|
||||
resetMessages,
|
||||
addMessage,
|
||||
updatePhase,
|
||||
updateMeta,
|
||||
]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (location.search.length === 0) {
|
||||
handleFiltersSubmit();
|
||||
}
|
||||
}, [location]);
|
||||
}, [handleFiltersSubmit, location]);
|
||||
|
||||
React.useEffect(() => {
|
||||
handleFiltersSubmit();
|
||||
}, [seekDirection]);
|
||||
}, [handleFiltersSubmit, seekDirection]);
|
||||
|
||||
return (
|
||||
<S.FiltersWrapper>
|
||||
|
|
|
@ -27,7 +27,7 @@ const MessagesTable: React.FC = () => {
|
|||
|
||||
const searchParams = React.useMemo(
|
||||
() => new URLSearchParams(location.search),
|
||||
[location, history]
|
||||
[location]
|
||||
);
|
||||
|
||||
const messages = useSelector(getTopicMessges);
|
||||
|
|
|
@ -88,10 +88,7 @@ const Edit: React.FC<Props> = ({
|
|||
fetchTopicConfig,
|
||||
updateTopic,
|
||||
}) => {
|
||||
const defaultValues = React.useMemo(
|
||||
() => topicParams(topic),
|
||||
[topicParams, topic]
|
||||
);
|
||||
const defaultValues = React.useMemo(() => topicParams(topic), [topic]);
|
||||
const methods = useForm<TopicFormData>({
|
||||
defaultValues,
|
||||
resolver: yupResolver(topicFormValidationSchema),
|
||||
|
@ -109,7 +106,7 @@ const Edit: React.FC<Props> = ({
|
|||
const { name } = methods.getValues();
|
||||
history.push(clusterTopicPath(clusterName, name));
|
||||
}
|
||||
}, [isSubmitting, isTopicUpdated, clusterTopicPath, clusterName, methods]);
|
||||
}, [isSubmitting, isTopicUpdated, clusterName, methods, history]);
|
||||
|
||||
if (!isFetched || !topic || !topic.config) {
|
||||
return null;
|
||||
|
|
|
@ -41,7 +41,7 @@ const SendMessage: React.FC = () => {
|
|||
|
||||
React.useEffect(() => {
|
||||
dispatch(fetchTopicMessageSchema(clusterName, topicName));
|
||||
}, []);
|
||||
}, [clusterName, dispatch, topicName]);
|
||||
|
||||
const messageSchema = useAppSelector((state) =>
|
||||
getMessageSchemaByTopicName(state, topicName)
|
||||
|
|
|
@ -54,7 +54,7 @@ const CustomParamField: React.FC<Props> = ({
|
|||
shouldValidate: true,
|
||||
});
|
||||
}
|
||||
}, [nameValue]);
|
||||
}, [existingFields, index, nameValue, setExistingFields, setValue]);
|
||||
|
||||
return (
|
||||
<C.Column>
|
||||
|
|
|
@ -15,6 +15,7 @@ const BreadcrumbRouteInternal: React.FC = () => {
|
|||
|
||||
useEffect(() => {
|
||||
context.handleRouteChange({ ...match, url: location.pathname });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [location.pathname]);
|
||||
|
||||
return null;
|
||||
|
|
|
@ -13,7 +13,7 @@ const Breadcrumb: React.FC = () => {
|
|||
|
||||
const links = React.useMemo(
|
||||
() => breadcrumbContext.path.slice(basePathEntriesLength),
|
||||
[breadcrumbContext.link]
|
||||
[breadcrumbContext.path]
|
||||
);
|
||||
|
||||
const getPathPredicate = React.useCallback(
|
||||
|
|
|
@ -21,7 +21,7 @@ const BytesFormatted: React.FC<Props> = ({ value, precision = 0 }) => {
|
|||
} catch (e) {
|
||||
return `-Bytes`;
|
||||
}
|
||||
}, [value]);
|
||||
}, [precision, value]);
|
||||
|
||||
return <span>{formatedValue}</span>;
|
||||
};
|
||||
|
|
|
@ -19,14 +19,14 @@ const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
|
|||
onConfirm,
|
||||
isConfirming = false,
|
||||
}) => {
|
||||
if (!isOpen) return null;
|
||||
|
||||
const cancelHandler = React.useCallback(() => {
|
||||
if (!isConfirming) {
|
||||
onCancel();
|
||||
}
|
||||
}, [isConfirming, onCancel]);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<ConfirmationModalWrapper>
|
||||
<div onClick={cancelHandler} aria-hidden="true" />
|
||||
|
|
|
@ -22,7 +22,7 @@ const Dropdown: React.FC<DropdownProps> = ({ label, right, up, children }) => {
|
|||
'is-right': right,
|
||||
'is-up': up,
|
||||
}),
|
||||
[active]
|
||||
[active, right, up]
|
||||
);
|
||||
return (
|
||||
<div className={classNames} ref={wrapperRef}>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import cx from 'classnames';
|
||||
|
||||
interface DynamicTextButtonProps {
|
||||
|
@ -18,15 +18,15 @@ const DynamicTextButton: React.FC<DynamicTextButtonProps> = ({
|
|||
}) => {
|
||||
const [clicked, setClicked] = React.useState(false);
|
||||
|
||||
let timeout: number;
|
||||
const timeout = useRef(0);
|
||||
|
||||
const clickHandler = useCallback(() => {
|
||||
onClick();
|
||||
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 (
|
||||
<button
|
||||
|
|
|
@ -68,7 +68,7 @@ const Pagination: React.FC<PaginationProps> = ({ totalPages }) => {
|
|||
}
|
||||
|
||||
return p;
|
||||
}, []);
|
||||
}, [currentPage, totalPages]);
|
||||
|
||||
return (
|
||||
<S.Wrapper role="navigation" aria-label="pagination">
|
||||
|
|
|
@ -21,10 +21,13 @@ const Tabs: React.FC<TabsProps> = ({
|
|||
setSelectedIndex(defaultSelectedIndex);
|
||||
}, [defaultSelectedIndex]);
|
||||
|
||||
const handleChange = React.useCallback((index: number) => {
|
||||
const handleChange = React.useCallback(
|
||||
(index: number) => {
|
||||
setSelectedIndex(index);
|
||||
onChange?.(index);
|
||||
}, []);
|
||||
},
|
||||
[onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -25,7 +25,7 @@ describe('useDataSaver hook', () => {
|
|||
|
||||
const HookWrapper: React.FC = () => {
|
||||
const { saveFile } = useDataSaver('message', content);
|
||||
useEffect(() => saveFile(), []);
|
||||
useEffect(() => saveFile(), [saveFile]);
|
||||
return null;
|
||||
};
|
||||
|
||||
|
@ -52,7 +52,7 @@ describe('useDataSaver hook', () => {
|
|||
|
||||
const HookWrapper: React.FC = () => {
|
||||
const { saveFile } = useDataSaver('message', 'content');
|
||||
useEffect(() => saveFile(), []);
|
||||
useEffect(() => saveFile(), [saveFile]);
|
||||
return null;
|
||||
};
|
||||
|
||||
|
@ -81,7 +81,7 @@ describe('useDataSaver hook', () => {
|
|||
it('data with type Object', () => {
|
||||
const HookWrapper: React.FC = () => {
|
||||
const { copyToClipboard } = useDataSaver('topic', content);
|
||||
useEffect(() => copyToClipboard(), []);
|
||||
useEffect(() => copyToClipboard(), [copyToClipboard]);
|
||||
return null;
|
||||
};
|
||||
render(<HookWrapper />);
|
||||
|
@ -96,7 +96,7 @@ describe('useDataSaver hook', () => {
|
|||
'topic',
|
||||
'{ title: "title", }'
|
||||
);
|
||||
useEffect(() => copyToClipboard(), []);
|
||||
useEffect(() => copyToClipboard(), [copyToClipboard]);
|
||||
return null;
|
||||
};
|
||||
render(<HookWrapper />);
|
||||
|
|
|
@ -21,7 +21,7 @@ const useSearch = (initValue = ''): [string, (value: string) => void] => {
|
|||
queryParams.set(SEARCH_QUERY_ARG, initValue.trim());
|
||||
history.push({ pathname, search: queryParams.toString() });
|
||||
}
|
||||
}, []);
|
||||
}, [history, initValue, pathname, q, queryParams]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(value: string) => {
|
||||
|
@ -39,7 +39,7 @@ const useSearch = (initValue = ''): [string, (value: string) => void] => {
|
|||
history.replace({ pathname, search: queryParams.toString() });
|
||||
}
|
||||
},
|
||||
[history, pathname, queryParams, q]
|
||||
[q, page, history, pathname, queryParams]
|
||||
);
|
||||
|
||||
return [q || initValue.trim() || '', handleChange];
|
||||
|
|
Loading…
Add table
Reference in a new issue