mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-02 02:42:41 +02:00
chore: Enable ESLint rules of hooks + fix new lint errors (#5714)
This commit is contained in:
parent
3db4fcaec7
commit
098f210890
16 changed files with 110 additions and 48 deletions
|
@ -83,14 +83,14 @@ const useAnnouncementBarContextValue = (): AnnouncementBarAPI => {
|
|||
if (isNewAnnouncement || !isDismissedInStorage()) {
|
||||
setClosed(false);
|
||||
}
|
||||
}, []);
|
||||
}, [announcementBar]);
|
||||
|
||||
return useMemo(() => {
|
||||
return {
|
||||
isActive: !!announcementBar && !isClosed,
|
||||
close: handleClose,
|
||||
};
|
||||
}, [isClosed]);
|
||||
}, [announcementBar, isClosed, handleClose]);
|
||||
};
|
||||
|
||||
const AnnouncementBarContext = createContext<AnnouncementBarAPI | null>(null);
|
||||
|
|
|
@ -120,7 +120,7 @@ function useContextValue() {
|
|||
return {
|
||||
savePreferredVersion,
|
||||
};
|
||||
}, [setState]);
|
||||
}, [versionPersistence]);
|
||||
|
||||
return [state, api] as const;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ export function useDocsPreferredVersion(
|
|||
(versionName: string) => {
|
||||
api.savePreferredVersion(pluginId, versionName);
|
||||
},
|
||||
[api],
|
||||
[api, pluginId],
|
||||
);
|
||||
|
||||
return {preferredVersion, savePreferredVersionName} as const;
|
||||
|
|
|
@ -83,6 +83,7 @@ function useShallowMemoizedObject<O extends Record<string, unknown>>(obj: O) {
|
|||
return useMemo(
|
||||
() => obj,
|
||||
// Is this safe?
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[...Object.keys(obj), ...Object.values(obj)],
|
||||
);
|
||||
}
|
||||
|
|
34
packages/docusaurus-theme-common/src/utils/reactUtils.tsx
Normal file
34
packages/docusaurus-theme-common/src/utils/reactUtils.tsx
Normal file
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {useCallback, useEffect, useLayoutEffect, useRef} from 'react';
|
||||
|
||||
// This hook is like useLayoutEffect, but without the SSR warning
|
||||
// It seems hacky but it's used in many React libs (Redux, Formik...)
|
||||
// Also mentioned here: https://github.com/facebook/react/issues/16956
|
||||
// It is useful when you need to update a ref as soon as possible after a React render (before useEffect)
|
||||
export const useIsomorphicLayoutEffect =
|
||||
typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
||||
|
||||
// Permits to transform an unstable callback (like an arrow function provided as props)
|
||||
// to a "stable" callback that is safe to use in a useEffect dependency array
|
||||
// Useful to avoid React stale closure problems + avoid useless effect re-executions
|
||||
//
|
||||
// Workaround until the React team recommends a good solution, see https://github.com/facebook/react/issues/16956
|
||||
// This generally works has some potential drawbacks, such as https://github.com/facebook/react/issues/16956#issuecomment-536636418
|
||||
export function useDynamicCallback<T extends (...args: never[]) => unknown>(
|
||||
callback: T,
|
||||
): T {
|
||||
const ref = useRef<T>(callback);
|
||||
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
ref.current = callback;
|
||||
}, [callback]);
|
||||
|
||||
// @ts-expect-error: TODO, not sure how to fix this TS error
|
||||
return useCallback<T>((...args) => ref.current(...args), []);
|
||||
}
|
|
@ -15,6 +15,7 @@ import React, {
|
|||
useMemo,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import {useDynamicCallback} from './reactUtils';
|
||||
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
||||
|
||||
/**
|
||||
|
@ -103,20 +104,22 @@ export function useScrollPosition(
|
|||
const {scrollEventsEnabledRef} = useScrollController();
|
||||
const lastPositionRef = useRef<ScrollPosition | null>(getScrollPosition());
|
||||
|
||||
const handleScroll = () => {
|
||||
if (!scrollEventsEnabledRef.current) {
|
||||
return;
|
||||
}
|
||||
const currentPosition = getScrollPosition()!;
|
||||
|
||||
if (effect) {
|
||||
effect(currentPosition, lastPositionRef.current);
|
||||
}
|
||||
|
||||
lastPositionRef.current = currentPosition;
|
||||
};
|
||||
const dynamicEffect = useDynamicCallback(effect);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
if (!scrollEventsEnabledRef.current) {
|
||||
return;
|
||||
}
|
||||
const currentPosition = getScrollPosition()!;
|
||||
|
||||
if (dynamicEffect) {
|
||||
dynamicEffect(currentPosition, lastPositionRef.current);
|
||||
}
|
||||
|
||||
lastPositionRef.current = currentPosition;
|
||||
};
|
||||
|
||||
const opts: AddEventListenerOptions & EventListenerOptions = {
|
||||
passive: true,
|
||||
};
|
||||
|
@ -125,7 +128,12 @@ export function useScrollPosition(
|
|||
window.addEventListener('scroll', handleScroll, opts);
|
||||
|
||||
return () => window.removeEventListener('scroll', handleScroll, opts);
|
||||
}, deps);
|
||||
}, [
|
||||
dynamicEffect,
|
||||
scrollEventsEnabledRef,
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
...deps,
|
||||
]);
|
||||
}
|
||||
|
||||
type UseScrollPositionSaver = {
|
||||
|
@ -170,7 +178,7 @@ function useScrollPositionSaver(): UseScrollPositionSaver {
|
|||
return {restored: heightDiff !== 0};
|
||||
}, []);
|
||||
|
||||
return useMemo(() => ({save, restore}), []);
|
||||
return useMemo(() => ({save, restore}), [restore, save]);
|
||||
}
|
||||
|
||||
type UseScrollPositionBlockerReturn = {
|
||||
|
@ -217,7 +225,7 @@ export function useScrollPositionBlocker(): UseScrollPositionBlockerReturn {
|
|||
}
|
||||
};
|
||||
},
|
||||
[scrollController],
|
||||
[scrollController, scrollPositionSaver],
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
|
|
|
@ -9,6 +9,7 @@ import {useEffect} from 'react';
|
|||
import {useLocation} from '@docusaurus/router';
|
||||
import {Location} from '@docusaurus/history';
|
||||
import {usePrevious} from './usePrevious';
|
||||
import {useDynamicCallback} from './reactUtils';
|
||||
|
||||
type LocationChangeEvent = {
|
||||
location: Location;
|
||||
|
@ -21,10 +22,12 @@ export function useLocationChange(onLocationChange: OnLocationChange): void {
|
|||
const location = useLocation();
|
||||
const previousLocation = usePrevious(location);
|
||||
|
||||
const onLocationChangeDynamic = useDynamicCallback(onLocationChange);
|
||||
|
||||
useEffect(() => {
|
||||
onLocationChange({
|
||||
onLocationChangeDynamic({
|
||||
location,
|
||||
previousLocation,
|
||||
});
|
||||
}, [location]);
|
||||
}, [onLocationChangeDynamic, location, previousLocation]);
|
||||
}
|
||||
|
|
|
@ -5,12 +5,13 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {useRef, useEffect} from 'react';
|
||||
import {useRef} from 'react';
|
||||
import {useIsomorphicLayoutEffect} from './reactUtils';
|
||||
|
||||
export function usePrevious<T>(value: T): T | undefined {
|
||||
const ref = useRef<T>();
|
||||
|
||||
useEffect(() => {
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
ref.current = value;
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue