mirror of
https://github.com/outline/outline.git
synced 2026-06-13 03:14:59 +03:00
chore: resolve mechanical react-hooks/exhaustive-deps warnings (#12207)
Adds missing stable dependencies (e.g. `t`, prop callbacks, store refs, `setFocusedCommentId`) and removes unnecessary ones across hooks where the fix is straightforward. For the two MobX-observed `.orderedData` deps in `History.tsx`, keeps the original deps and silences the false positive with `eslint-disable-next-line` so the memos still recompute when the underlying observable arrays change. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -146,7 +146,14 @@ function Collaborators(props: Props) {
|
||||
/>
|
||||
);
|
||||
},
|
||||
[presentIds, editingIds, observingUserId, currentUserId, handleAvatarClick]
|
||||
[
|
||||
presentIds,
|
||||
editingIds,
|
||||
observingUserId,
|
||||
currentUserId,
|
||||
handleAvatarClick,
|
||||
t,
|
||||
]
|
||||
);
|
||||
|
||||
if (!document.insightsEnabled) {
|
||||
|
||||
@@ -99,7 +99,7 @@ function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
|
||||
);
|
||||
}, 2000);
|
||||
onFileUploadStart?.();
|
||||
}, [onFileUploadStart, dictionary.uploadingWithProgress]);
|
||||
}, [onFileUploadStart, dictionary]);
|
||||
|
||||
const handleFileUploadProgress = React.useCallback(
|
||||
(fileId: string, fractionComplete: number) => {
|
||||
@@ -118,7 +118,7 @@ function Editor(props: Props, ref: React.RefObject<SharedEditor> | null) {
|
||||
});
|
||||
}
|
||||
},
|
||||
[dictionary.uploadingWithProgress]
|
||||
[dictionary]
|
||||
);
|
||||
|
||||
const handleFileUploadStop = React.useCallback(() => {
|
||||
|
||||
@@ -40,7 +40,7 @@ export function ShareSubscribeForm({
|
||||
setStatus("error");
|
||||
}
|
||||
},
|
||||
[shareId, documentId, email]
|
||||
[shareId, documentId, email, t]
|
||||
);
|
||||
|
||||
const handleChange = useCallback(
|
||||
|
||||
@@ -83,7 +83,7 @@ const Sidebar = React.forwardRef<HTMLDivElement, Props>(function Sidebar_(
|
||||
ui.set({ sidebarWidth: Math.max(newWidth, minWidth) });
|
||||
}
|
||||
},
|
||||
[ui, theme, offset, minWidth, maxWidth, direction]
|
||||
[ui, theme, offset, minWidth, maxWidth, direction, canCollapse]
|
||||
);
|
||||
|
||||
const handleStopDrag = React.useCallback(() => {
|
||||
@@ -107,7 +107,7 @@ const Sidebar = React.forwardRef<HTMLDivElement, Props>(function Sidebar_(
|
||||
} else {
|
||||
ui.set({ sidebarWidth: width });
|
||||
}
|
||||
}, [ui, isSmallerThanMinimum, minWidth, width]);
|
||||
}, [ui, isSmallerThanMinimum, minWidth, width, canCollapse]);
|
||||
|
||||
const handleBlur = React.useCallback(() => {
|
||||
setHovering(false);
|
||||
|
||||
@@ -49,13 +49,13 @@ function CollectionLinkChildren({
|
||||
if (!expanded) {
|
||||
setShowing(pageSize);
|
||||
}
|
||||
}, [expanded]);
|
||||
}, [expanded, pageSize]);
|
||||
|
||||
const showMore = useCallback(() => {
|
||||
if (childDocuments && childDocuments.length > showing) {
|
||||
setShowing((value) => value + pageSize);
|
||||
}
|
||||
}, [childDocuments, showing]);
|
||||
}, [childDocuments, showing, pageSize]);
|
||||
|
||||
return (
|
||||
<Folder expanded={expanded}>
|
||||
|
||||
@@ -70,7 +70,7 @@ function SharedWithMeLink({ membership, depth = 0 }: Props) {
|
||||
void documents.fetch(documentId);
|
||||
void membership.fetchDocuments();
|
||||
}
|
||||
}, [documentId, documents]);
|
||||
}, [documentId, documents, membership]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isActiveDocument && membership.documentId) {
|
||||
|
||||
@@ -132,7 +132,7 @@ function SidebarLink(
|
||||
onClick(ev);
|
||||
}
|
||||
},
|
||||
[onClick, disabled, expanded]
|
||||
[onClick, disabled]
|
||||
);
|
||||
|
||||
const handleDisclosureClick = React.useCallback(
|
||||
|
||||
@@ -102,7 +102,7 @@ const LinkEditor: React.FC<Props> = ({
|
||||
|
||||
const openLink = React.useCallback(() => {
|
||||
commands["openLink"]();
|
||||
}, []);
|
||||
}, [commands]);
|
||||
|
||||
const removeLink = React.useCallback(() => {
|
||||
commands["removeLink"]();
|
||||
|
||||
@@ -166,7 +166,16 @@ export function MediaDimension() {
|
||||
height: finalHeight,
|
||||
});
|
||||
}
|
||||
}, [commands, width, height, localDimension, nodeType, error, reset]);
|
||||
}, [
|
||||
commands,
|
||||
width,
|
||||
height,
|
||||
localDimension,
|
||||
nodeType,
|
||||
error,
|
||||
reset,
|
||||
isOutsideBounds,
|
||||
]);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
|
||||
@@ -61,7 +61,7 @@ export function MediaLinkEditor({
|
||||
const { state, dispatch } = view;
|
||||
dispatch(state.tr.deleteSelection());
|
||||
onLinkRemove();
|
||||
}, [view]);
|
||||
}, [view, onLinkRemove]);
|
||||
|
||||
const update = useCallback(() => {
|
||||
const { state } = view;
|
||||
@@ -74,7 +74,7 @@ export function MediaLinkEditor({
|
||||
view.dispatch(tr);
|
||||
moveSelectionToEnd();
|
||||
onLinkUpdate();
|
||||
}, [localUrl, node, view, moveSelectionToEnd]);
|
||||
}, [localUrl, node, view, moveSelectionToEnd, onLinkUpdate]);
|
||||
|
||||
useOnClickOutside(wrapperRef, onClickOutside);
|
||||
|
||||
@@ -99,7 +99,7 @@ export function MediaLinkEditor({
|
||||
}
|
||||
}
|
||||
},
|
||||
[update, moveSelectionToEnd]
|
||||
[update, moveSelectionToEnd, onEscape]
|
||||
);
|
||||
|
||||
if (!node) {
|
||||
|
||||
@@ -69,7 +69,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
|
||||
res.data.collections.map(collections.add);
|
||||
res.data.groups.map(groups.add);
|
||||
});
|
||||
}, [search, documents, users, collections])
|
||||
}, [search, documents, users, collections, groups, maxResultsInSection])
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -187,7 +187,7 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
|
||||
selection.to
|
||||
)
|
||||
);
|
||||
}, [props.search, props.trigger, view]);
|
||||
}, [props.search, props.trigger, view, isMobile]);
|
||||
|
||||
const restoreSelection = React.useCallback(() => {
|
||||
if (!isMobile) {
|
||||
|
||||
@@ -65,7 +65,7 @@ export function useCollectionMenuAction({ collectionId, onRename }: Props) {
|
||||
ActionSeparator,
|
||||
deleteCollection,
|
||||
],
|
||||
[t, can.createDocument, can.update, onRename]
|
||||
[t, can.update, onRename]
|
||||
);
|
||||
|
||||
return useMenuAction(actions);
|
||||
|
||||
@@ -158,7 +158,7 @@ function CommentThreadItem({
|
||||
setFocusedCommentId(null);
|
||||
}
|
||||
},
|
||||
[comment.id, onUpdate]
|
||||
[comment.id, onUpdate, setFocusedCommentId]
|
||||
);
|
||||
|
||||
const handleDelete = React.useCallback(() => {
|
||||
|
||||
@@ -222,6 +222,7 @@ function DataLoader({ match, children }: Props) {
|
||||
shares,
|
||||
ui,
|
||||
revisionId,
|
||||
missingPolicy,
|
||||
]);
|
||||
|
||||
// Auto-enter presentation mode when ?present=true query param is set
|
||||
|
||||
@@ -103,7 +103,7 @@ function DocumentEditor(props: Props, ref: React.RefObject<any>) {
|
||||
}
|
||||
ui.set({ rightSidebar: "comments" });
|
||||
}
|
||||
}, [focusedComment, ui, document.id, params]);
|
||||
}, [focusedComment, ui, document.id, params, setFocusedCommentId]);
|
||||
|
||||
// Save document when blurring title, but delay so that if clicking on a
|
||||
// button this is allowed to execute first.
|
||||
@@ -148,7 +148,7 @@ function DocumentEditor(props: Props, ref: React.RefObject<any>) {
|
||||
setFocusedCommentId(commentId);
|
||||
}
|
||||
},
|
||||
[comments, user?.id, props.id]
|
||||
[comments, user?.id, props.id, setFocusedCommentId]
|
||||
);
|
||||
|
||||
// Soft delete the Comment model when associated mark is totally removed.
|
||||
|
||||
@@ -96,7 +96,7 @@ function History() {
|
||||
updateLocation({ changes: null, compareTo: null });
|
||||
}
|
||||
},
|
||||
[updateLocation]
|
||||
[updateLocation, setDefaultShowChanges]
|
||||
);
|
||||
|
||||
const selectedRevisionId = historyMatch?.params.revisionId;
|
||||
@@ -127,7 +127,7 @@ function History() {
|
||||
if (defaultShowChanges) {
|
||||
updateLocation({ changes: "true" });
|
||||
}
|
||||
}, [defaultShowChanges]);
|
||||
}, [defaultShowChanges, updateLocation]);
|
||||
|
||||
const fetchHistory = React.useCallback(async () => {
|
||||
if (!document) {
|
||||
@@ -170,6 +170,7 @@ function History() {
|
||||
.getByDocumentId(document.id)
|
||||
.filter((revision: Revision) => revision.id !== latestRevisionId)
|
||||
.slice(0, revisionsOffset);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [document, revisions.orderedData, revisionsOffset]);
|
||||
|
||||
const nonRevisionEvents = React.useMemo(
|
||||
@@ -177,6 +178,7 @@ function History() {
|
||||
document
|
||||
? events.getByDocumentId(document.id).slice(0, eventsOffset)
|
||||
: [],
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[document, events.orderedData, eventsOffset]
|
||||
);
|
||||
|
||||
@@ -228,7 +230,7 @@ function History() {
|
||||
} else {
|
||||
history.goBack();
|
||||
}
|
||||
}, [history, document, sidebarContext]);
|
||||
}, [history, document, sidebarContext, isMobile]);
|
||||
|
||||
useKeyDown("Escape", onCloseHistory);
|
||||
|
||||
|
||||
@@ -69,7 +69,16 @@ function DocumentDelete({ document, onSubmit }: Props) {
|
||||
setDeleting(false);
|
||||
}
|
||||
},
|
||||
[onSubmit, ui, document, documents, history, collection]
|
||||
[
|
||||
onSubmit,
|
||||
ui,
|
||||
document,
|
||||
documents,
|
||||
history,
|
||||
collection,
|
||||
userMemberships,
|
||||
groupMemberships,
|
||||
]
|
||||
);
|
||||
|
||||
const handleArchive = React.useCallback(
|
||||
|
||||
@@ -177,6 +177,7 @@ export const GroupMembersTable = observer(function GroupMembersTable({
|
||||
t,
|
||||
can.update,
|
||||
group.id,
|
||||
group.isExternallyManaged,
|
||||
groupUsers.orderedData,
|
||||
permissions,
|
||||
handlePermissionChange,
|
||||
|
||||
@@ -146,7 +146,7 @@ export function SharesTable({ data, canManage, ...rest }: Props) {
|
||||
}
|
||||
: undefined,
|
||||
]),
|
||||
[t, hasDomain, canManage]
|
||||
[t, hasDomain, canManage, formatNumber]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -55,7 +55,15 @@ export const Notion = observer(() => {
|
||||
onClose: clearQueryParams,
|
||||
});
|
||||
}
|
||||
}, [t, dialogs, oauthSuccess, service, clearQueryParams]);
|
||||
}, [
|
||||
t,
|
||||
dialogs,
|
||||
oauthSuccess,
|
||||
service,
|
||||
clearQueryParams,
|
||||
handleSubmit,
|
||||
integrationId,
|
||||
]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!oauthError) {
|
||||
|
||||
@@ -53,7 +53,15 @@ export function ImportDialog({ integrationId, onSubmit }: Props) {
|
||||
toast.error(err.message);
|
||||
resetSubmitting();
|
||||
}
|
||||
}, [permission, onSubmit]);
|
||||
}, [
|
||||
permission,
|
||||
onSubmit,
|
||||
integrationId,
|
||||
t,
|
||||
imports,
|
||||
resetSubmitting,
|
||||
setSubmitting,
|
||||
]);
|
||||
|
||||
return (
|
||||
<Flex column gap={12}>
|
||||
|
||||
@@ -239,7 +239,7 @@ export const MentionURL = (props: IssueUrlProps) => {
|
||||
};
|
||||
|
||||
void fetchUnfurl();
|
||||
}, [unfurls, url, node, isMounted]);
|
||||
}, [unfurls, url, node, isMounted, onChangeUnfurl]);
|
||||
|
||||
if (!unfurl) {
|
||||
return !loaded ? (
|
||||
|
||||
Reference in New Issue
Block a user