[Refactoring] Revise hook dependencies (#2424)
This commit is contained in:
parent
cf4c199eba
commit
9c25abbd9a
@ -40,7 +40,12 @@
|
||||
"no-console": "error",
|
||||
"semi": "error",
|
||||
"no-implicit-coercion": "error",
|
||||
"react-hooks/exhaustive-deps": "error",
|
||||
"react-hooks/exhaustive-deps": [
|
||||
"error",
|
||||
{
|
||||
"additionalHooks": "(useSyncEffect|useAsync|useDebouncedCallback|useThrottledCallback|useEffectWithPrevDeps|useLayoutEffectWithPrevDeps)$"
|
||||
}
|
||||
],
|
||||
"arrow-body-style": "off",
|
||||
"no-else-return": "off",
|
||||
"no-plusplus": "off",
|
||||
|
||||
@ -12,7 +12,7 @@ import buildClassName from '../../util/buildClassName';
|
||||
import renderText from '../common/helpers/renderText';
|
||||
import useLang from '../../hooks/useLang';
|
||||
import { isoToEmoji } from '../../util/emoji';
|
||||
import useOnChange from '../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||
|
||||
import DropdownMenu from '../ui/DropdownMenu';
|
||||
import MenuItem from '../ui/MenuItem';
|
||||
@ -53,11 +53,11 @@ const CountryCodeInput: FC<OwnProps & StateProps> = ({
|
||||
setFilteredList(getFilteredList(phoneCodeList, filterValue));
|
||||
}, [phoneCodeList]);
|
||||
|
||||
useOnChange(([prevPhoneCodeList]) => {
|
||||
if (prevPhoneCodeList?.length === 0 && phoneCodeList.length > 0) {
|
||||
updateFilter(filter);
|
||||
useSyncEffect(([prevPhoneCodeList]) => {
|
||||
if (!prevPhoneCodeList?.length && phoneCodeList.length) {
|
||||
setFilteredList(getFilteredList(phoneCodeList, filter));
|
||||
}
|
||||
}, [phoneCodeList, updateFilter]);
|
||||
}, [phoneCodeList, filter]);
|
||||
|
||||
const handleChange = useCallback((country: ApiCountryCode) => {
|
||||
onChange(country);
|
||||
|
||||
@ -12,7 +12,7 @@ import generateIdFor from '../../util/generateIdFor';
|
||||
|
||||
import useHeavyAnimationCheck from '../../hooks/useHeavyAnimationCheck';
|
||||
import useBackgroundMode from '../../hooks/useBackgroundMode';
|
||||
import useOnChange from '../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||
import useAppLayout from '../../hooks/useAppLayout';
|
||||
|
||||
export type OwnProps = {
|
||||
@ -229,13 +229,13 @@ const AnimatedSticker: FC<OwnProps> = ({
|
||||
fastRaf(unfreezeAnimation);
|
||||
}, [unfreezeAnimation]);
|
||||
|
||||
useOnChange(([prevNoLoop]) => {
|
||||
useSyncEffect(([prevNoLoop]) => {
|
||||
if (prevNoLoop !== undefined && noLoop !== prevNoLoop) {
|
||||
animation?.setNoLoop(noLoop);
|
||||
}
|
||||
}, [noLoop, animation]);
|
||||
|
||||
useOnChange(([prevSharedCanvasCoords, prevIsMobile]) => {
|
||||
useSyncEffect(([prevSharedCanvasCoords, prevIsMobile]) => {
|
||||
if (
|
||||
(prevSharedCanvasCoords !== undefined && sharedCanvasCoords !== prevSharedCanvasCoords)
|
||||
|| (prevIsMobile !== undefined && isMobile !== prevIsMobile)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from '../../lib/teact/teact';
|
||||
import React from '../../lib/teact/teact';
|
||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||
|
||||
import { ApiMediaFormat } from '../../api/types';
|
||||
@ -13,8 +13,6 @@ import {
|
||||
selectTabState,
|
||||
} from '../../global/selectors';
|
||||
import { DARK_THEME_BG_COLOR, LIGHT_THEME_BG_COLOR } from '../../config';
|
||||
import useFlag from '../../hooks/useFlag';
|
||||
import useShowTransition from '../../hooks/useShowTransition';
|
||||
import { pause } from '../../util/schedulers';
|
||||
import { preloadImage } from '../../util/files';
|
||||
import preloadFonts from '../../util/fonts';
|
||||
@ -22,6 +20,10 @@ import * as mediaLoader from '../../util/mediaLoader';
|
||||
import { Bundles, loadModule } from '../../util/moduleLoader';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
|
||||
import useFlag from '../../hooks/useFlag';
|
||||
import useShowTransition from '../../hooks/useShowTransition';
|
||||
import useEffectOnce from '../../hooks/useEffectOnce';
|
||||
|
||||
import styles from './UiLoader.module.scss';
|
||||
|
||||
import telegramLogoPath from '../../assets/telegram-logo.svg';
|
||||
@ -114,7 +116,7 @@ const UiLoader: FC<OwnProps & StateProps> = ({
|
||||
shouldRender: shouldRenderMask, transitionClassNames,
|
||||
} = useShowTransition(!isReady, undefined, true);
|
||||
|
||||
useEffect(() => {
|
||||
useEffectOnce(() => {
|
||||
let timeout: number | undefined;
|
||||
|
||||
const safePreload = async () => {
|
||||
@ -145,8 +147,7 @@ const UiLoader: FC<OwnProps & StateProps> = ({
|
||||
|
||||
setIsUiReady({ uiReadyState: 0 });
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@ -12,7 +12,7 @@ import { selectTabState, selectCurrentChat, selectIsForumPanelOpen } from '../..
|
||||
import useFoldersReducer from '../../hooks/reducers/useFoldersReducer';
|
||||
import { useResize } from '../../hooks/useResize';
|
||||
import { useHotkeys } from '../../hooks/useHotkeys';
|
||||
import useOnChange from '../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||
|
||||
import Transition from '../ui/Transition';
|
||||
import LeftMain from './main/LeftMain';
|
||||
@ -372,7 +372,7 @@ const LeftColumn: FC<StateProps> = ({
|
||||
}
|
||||
}, [clearTwoFaError, loadPasswordInfo, settingsScreen]);
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
if (nextSettingsScreen !== undefined) {
|
||||
setContent(LeftColumnContent.Settings);
|
||||
setSettingsScreen(nextSettingsScreen);
|
||||
|
||||
@ -117,7 +117,7 @@ const ChatList: FC<OwnProps> = ({
|
||||
return;
|
||||
}
|
||||
openChat({ id: chatId, shouldReplaceHistory: true });
|
||||
}, [], DRAG_ENTER_DEBOUNCE, true);
|
||||
}, [openChat], DRAG_ENTER_DEBOUNCE, true);
|
||||
|
||||
const handleDragLeave = useCallback((e: React.DragEvent<HTMLDivElement>) => {
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { memo, useRef } from '../../lib/teact/teact';
|
||||
import React, { memo, useCallback, useRef } from '../../lib/teact/teact';
|
||||
import { withGlobal } from '../../global';
|
||||
|
||||
import type { TabState } from '../../global/types';
|
||||
@ -9,7 +9,7 @@ import buildStyle from '../../util/buildStyle';
|
||||
import { selectTabState } from '../../global/selectors';
|
||||
|
||||
import useWindowSize from '../../hooks/useWindowSize';
|
||||
import useOnChange from '../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||
import useForceUpdate from '../../hooks/useForceUpdate';
|
||||
import useAppLayout from '../../hooks/useAppLayout';
|
||||
|
||||
@ -55,7 +55,7 @@ const ConfettiContainer: FC<StateProps> = ({ confetti }) => {
|
||||
lastConfettiTime, top, width, left, height,
|
||||
} = confetti || {};
|
||||
|
||||
function generateConfetti(w: number, h: number, amount = defaultConfettiAmount) {
|
||||
const generateConfetti = useCallback((w: number, h: number, amount = defaultConfettiAmount) => {
|
||||
for (let i = 0; i < amount; i++) {
|
||||
const leftSide = i % 2;
|
||||
const pos = {
|
||||
@ -83,9 +83,9 @@ const ConfettiContainer: FC<StateProps> = ({ confetti }) => {
|
||||
frameCount: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [defaultConfettiAmount]);
|
||||
|
||||
const updateCanvas = () => {
|
||||
const updateCanvas = useCallback(() => {
|
||||
if (!canvasRef.current || !isRafStartedRef.current) {
|
||||
return;
|
||||
}
|
||||
@ -166,9 +166,9 @@ const ConfettiContainer: FC<StateProps> = ({ confetti }) => {
|
||||
} else {
|
||||
isRafStartedRef.current = false;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useOnChange(([prevConfettiTime]) => {
|
||||
useSyncEffect(([prevConfettiTime]) => {
|
||||
let hideTimeout: ReturnType<typeof setTimeout>;
|
||||
if (prevConfettiTime !== lastConfettiTime) {
|
||||
generateConfetti(width || windowSize.width, height || windowSize.height);
|
||||
@ -179,11 +179,10 @@ const ConfettiContainer: FC<StateProps> = ({ confetti }) => {
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
if (hideTimeout) {
|
||||
clearTimeout(hideTimeout);
|
||||
}
|
||||
clearTimeout(hideTimeout);
|
||||
};
|
||||
}, [lastConfettiTime, updateCanvas]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- Old timeout should be cleared only if new confetti is generated
|
||||
}, [lastConfettiTime, forceUpdate, updateCanvas]);
|
||||
|
||||
if (!lastConfettiTime || Date.now() - lastConfettiTime > CONFETTI_FADEOUT_TIMEOUT) {
|
||||
return undefined;
|
||||
|
||||
@ -35,7 +35,7 @@ import { fastRaf } from '../../util/schedulers';
|
||||
import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps';
|
||||
import useBackgroundMode from '../../hooks/useBackgroundMode';
|
||||
import useBeforeUnload from '../../hooks/useBeforeUnload';
|
||||
import useOnChange from '../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||
import usePreventPinchZoomGesture from '../../hooks/usePreventPinchZoomGesture';
|
||||
import useForceUpdate from '../../hooks/useForceUpdate';
|
||||
import useShowTransition from '../../hooks/useShowTransition';
|
||||
@ -272,7 +272,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
ignoreCache: true,
|
||||
});
|
||||
}
|
||||
}, [lastSyncTime, isMasterTab] as const);
|
||||
}, [lastSyncTime, isMasterTab, loadCustomEmojis]);
|
||||
|
||||
// Sticker sets
|
||||
useEffect(() => {
|
||||
@ -324,7 +324,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
type: parsedLocationHash.type,
|
||||
});
|
||||
}
|
||||
}, [lastSyncTime] as const);
|
||||
}, [lastSyncTime, openChat]);
|
||||
|
||||
const leftColumnTransition = useShowTransition(
|
||||
isLeftColumnOpen, undefined, true, undefined, shouldSkipHistoryAnimations,
|
||||
@ -333,8 +333,8 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
// Handle opening middle column
|
||||
useOnChange(([prevIsLeftColumnOpen]) => {
|
||||
if (prevIsLeftColumnOpen === undefined || animationLevel === 0) {
|
||||
useSyncEffect(([prevIsLeftColumnOpen]) => {
|
||||
if (prevIsLeftColumnOpen === undefined || isLeftColumnOpen === prevIsLeftColumnOpen || animationLevel === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -353,7 +353,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
willAnimateLeftColumnRef.current = false;
|
||||
forceUpdate();
|
||||
});
|
||||
}, [isLeftColumnOpen]);
|
||||
}, [animationLevel, forceUpdate, isLeftColumnOpen]);
|
||||
|
||||
const rightColumnTransition = useShowTransition(
|
||||
isRightColumnOpen, undefined, true, undefined, shouldSkipHistoryAnimations,
|
||||
@ -362,8 +362,8 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
const [isNarrowMessageList, setIsNarrowMessageList] = useState(isRightColumnOpen);
|
||||
|
||||
// Handle opening right column
|
||||
useOnChange(([prevIsRightColumnOpen]) => {
|
||||
if (prevIsRightColumnOpen === undefined) {
|
||||
useSyncEffect(([prevIsRightColumnOpen]) => {
|
||||
if (prevIsRightColumnOpen === undefined || isRightColumnOpen === prevIsRightColumnOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -382,7 +382,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
forceUpdate();
|
||||
setIsNarrowMessageList(isRightColumnOpen);
|
||||
});
|
||||
}, [isRightColumnOpen]);
|
||||
}, [animationLevel, forceUpdate, isRightColumnOpen]);
|
||||
|
||||
const className = buildClassName(
|
||||
leftColumnTransition.hasShownClass && 'left-column-shown',
|
||||
|
||||
@ -18,7 +18,7 @@ import { extractCurrentThemeParams, validateHexColor } from '../../util/themeSty
|
||||
|
||||
import useInterval from '../../hooks/useInterval';
|
||||
import useLang from '../../hooks/useLang';
|
||||
import useOnChange from '../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||
import useWebAppFrame from './hooks/useWebAppFrame';
|
||||
import usePrevious from '../../hooks/usePrevious';
|
||||
import useFlag from '../../hooks/useFlag';
|
||||
@ -258,20 +258,20 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
}, [handlePopupClose]);
|
||||
|
||||
// Notify view that height changed
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
setTimeout(() => {
|
||||
sendViewport();
|
||||
}, ANIMATION_WAIT);
|
||||
}, [mainButton?.isVisible, sendViewport]);
|
||||
|
||||
// Notify view that theme changed
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
setTimeout(() => {
|
||||
sendTheme();
|
||||
}, ANIMATION_WAIT);
|
||||
}, [theme, sendTheme]);
|
||||
|
||||
useOnChange(([prevIsPaymentModalOpen]) => {
|
||||
useSyncEffect(([prevIsPaymentModalOpen]) => {
|
||||
if (isPaymentModalOpen === prevIsPaymentModalOpen) return;
|
||||
if (webApp?.slug && !isPaymentModalOpen && paymentStatus) {
|
||||
sendEvent({
|
||||
@ -285,7 +285,7 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
slug: undefined,
|
||||
});
|
||||
}
|
||||
}, [isPaymentModalOpen, paymentStatus, sendEvent, setWebAppPaymentSlug, webApp] as const);
|
||||
}, [isPaymentModalOpen, paymentStatus, sendEvent, setWebAppPaymentSlug, webApp]);
|
||||
|
||||
const handleToggleClick = useCallback(() => {
|
||||
toggleAttachBot({
|
||||
|
||||
@ -22,7 +22,7 @@ import renderText from '../../common/helpers/renderText';
|
||||
import { getUserFullName } from '../../../global/helpers';
|
||||
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useOnChange from '../../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||
|
||||
import Modal from '../../ui/Modal';
|
||||
import Button from '../../ui/Button';
|
||||
@ -170,11 +170,11 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
}, [isSuccess, showConfetti]);
|
||||
|
||||
useOnChange(([prevIsPremium]) => {
|
||||
useSyncEffect(([prevIsPremium]) => {
|
||||
if (prevIsPremium === isPremium) return;
|
||||
|
||||
showConfetti();
|
||||
}, [isPremium]);
|
||||
}, [isPremium, showConfetti]);
|
||||
|
||||
if (!promo) return undefined;
|
||||
|
||||
|
||||
@ -120,7 +120,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
forceUpdate();
|
||||
}, [forceUpdate]);
|
||||
|
||||
const selectMediaDebounced = useDebouncedCallback(selectMedia, [], DEBOUNCE_MESSAGE, true);
|
||||
const selectMediaDebounced = useDebouncedCallback(selectMedia, [selectMedia], DEBOUNCE_MESSAGE, true);
|
||||
const clearSwipeDirectionDebounced = useDebouncedCallback(() => {
|
||||
swipeDirectionRef.current = undefined;
|
||||
}, [], DEBOUNCE_SWIPE, true);
|
||||
|
||||
@ -50,7 +50,7 @@ import resetScroll, { patchChromiumScroll } from '../../util/resetScroll';
|
||||
import fastSmoothScroll, { isAnimatingScroll } from '../../util/fastSmoothScroll';
|
||||
import renderText from '../common/helpers/renderText';
|
||||
|
||||
import useOnChange from '../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||
import useStickyDates from './hooks/useStickyDates';
|
||||
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
|
||||
import useLang from '../../hooks/useLang';
|
||||
@ -196,7 +196,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const areMessagesLoaded = Boolean(messageIds);
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
// We only need it first time when message list appears
|
||||
if (areMessagesLoaded) {
|
||||
onTickEnd(() => {
|
||||
@ -206,24 +206,24 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
||||
}, [areMessagesLoaded]);
|
||||
|
||||
// Updated every time (to be used from intersection callback closure)
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
memoFirstUnreadIdRef.current = firstUnreadId;
|
||||
}, [firstUnreadId]);
|
||||
|
||||
useOnChange(() => {
|
||||
useEffect(() => {
|
||||
if (!isCurrentUserPremium && isChannelChat && isReady && lastSyncTime) {
|
||||
loadSponsoredMessages({ chatId });
|
||||
}
|
||||
}, [isCurrentUserPremium, chatId, isReady, isChannelChat, lastSyncTime]);
|
||||
}, [isCurrentUserPremium, chatId, isReady, isChannelChat, lastSyncTime, loadSponsoredMessages]);
|
||||
|
||||
// Updated only once when messages are loaded (as we want the unread divider to keep its position)
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
if (areMessagesLoaded) {
|
||||
memoUnreadDividerBeforeIdRef.current = memoFirstUnreadIdRef.current;
|
||||
}
|
||||
}, [areMessagesLoaded]);
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
memoFocusingIdRef.current = focusingId;
|
||||
}, [focusingId]);
|
||||
|
||||
@ -344,7 +344,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
||||
}, [isChatLoaded, messageIds, loadMoreAround, focusingId, isRestricted]);
|
||||
|
||||
// Remember scroll position before repositioning it
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
if (!messageIds || !listItemElementsRef.current) {
|
||||
return;
|
||||
}
|
||||
@ -488,7 +488,8 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
||||
// eslint-disable-next-line no-console
|
||||
console.timeEnd('scrollTop');
|
||||
}
|
||||
// This should match deps for `useOnChange` above
|
||||
// This should match deps for `useSyncEffect` above
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- `as const` not yet supported by linter
|
||||
}, [messageIds, isViewportNewest, containerHeight, hasTools] as const);
|
||||
|
||||
useEffectWithPrevDeps(([prevIsSelectModeActive]) => {
|
||||
|
||||
@ -61,7 +61,7 @@ import useLang from '../../hooks/useLang';
|
||||
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||
import usePrevious from '../../hooks/usePrevious';
|
||||
import useForceUpdate from '../../hooks/useForceUpdate';
|
||||
import useOnChange from '../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||
import useAppLayout from '../../hooks/useAppLayout';
|
||||
|
||||
import Transition from '../ui/Transition';
|
||||
@ -243,7 +243,7 @@ const MiddleColumn: FC<OwnProps & StateProps> = ({
|
||||
: undefined;
|
||||
}, [chatId, openChat]);
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
setDropAreaState(DropAreaState.None);
|
||||
setIsNotchShown(undefined);
|
||||
}, [chatId]);
|
||||
@ -704,7 +704,7 @@ function useIsReady(
|
||||
}
|
||||
}
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
if (!withAnimations) {
|
||||
setIsReady(true);
|
||||
}
|
||||
|
||||
@ -88,7 +88,7 @@ import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useSendMessageAction from '../../../hooks/useSendMessageAction';
|
||||
import useInterval from '../../../hooks/useInterval';
|
||||
import useOnChange from '../../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||
import { useStateRef } from '../../../hooks/useStateRef';
|
||||
import useVoiceRecording from './hooks/useVoiceRecording';
|
||||
import useClipboardPaste from './hooks/useClipboardPaste';
|
||||
@ -338,7 +338,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
}, [chat, chatId, isReady, lastSyncTime, loadSendAs, sendAsPeerIds]);
|
||||
|
||||
const shouldAnimateSendAsButtonRef = useRef(false);
|
||||
useOnChange(([prevChatId, prevSendAsPeerIds]) => {
|
||||
useSyncEffect(([prevChatId, prevSendAsPeerIds]) => {
|
||||
// We only animate send-as button if `sendAsPeerIds` was missing when opening the chat
|
||||
shouldAnimateSendAsButtonRef.current = Boolean(chatId === prevChatId && sendAsPeerIds && !prevSendAsPeerIds);
|
||||
}, [chatId, sendAsPeerIds]);
|
||||
|
||||
@ -185,7 +185,7 @@ const MessageInput: FC<OwnProps & StateProps> = ({
|
||||
if (prevHtml !== undefined && prevHtml !== html) {
|
||||
updateInputHeight(!html.length);
|
||||
}
|
||||
}, [html]);
|
||||
}, [html, updateInputHeight]);
|
||||
|
||||
const chatIdRef = useRef(chatId);
|
||||
chatIdRef.current = chatId;
|
||||
|
||||
@ -9,7 +9,7 @@ import type { ISettings } from '../../../types';
|
||||
import { RE_LINK_TEMPLATE } from '../../../config';
|
||||
import { selectTabState, selectNoWebPage, selectTheme } from '../../../global/selectors';
|
||||
import parseMessageInput from '../../../util/parseMessageInput';
|
||||
import useOnChange from '../../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||
import useShowTransition from '../../../hooks/useShowTransition';
|
||||
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
|
||||
import useDebouncedMemo from '../../../hooks/useDebouncedMemo';
|
||||
@ -78,10 +78,10 @@ const WebPagePreview: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
}, [chatId, toggleMessageWebPage, clearWebPagePreview, link, loadWebPagePreview, threadId]);
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
clearWebPagePreview();
|
||||
toggleMessageWebPage({ chatId, threadId });
|
||||
}, [chatId]);
|
||||
}, [chatId, clearWebPagePreview, threadId, toggleMessageWebPage]);
|
||||
|
||||
const isShown = Boolean(webPagePreview && messageText.length && !noWebPage && !disabled);
|
||||
const { shouldRender, transitionClassNames } = useShowTransition(isShown);
|
||||
|
||||
@ -56,6 +56,7 @@ const useEditing = (
|
||||
focusEditableElement(messageInput, true);
|
||||
}
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- `as const` not yet supported by linter
|
||||
}, [editedMessage, replyingToId, setHtml] as const);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -9,7 +9,7 @@ import { LOCAL_MESSAGE_MIN_ID, MESSAGE_LIST_SLICE } from '../../../config';
|
||||
import { IS_SCROLL_PATCH_NEEDED, MESSAGE_LIST_SENSITIVE_AREA } from '../../../util/environment';
|
||||
import { debounce } from '../../../util/schedulers';
|
||||
import { useIntersectionObserver, useOnIntersect } from '../../../hooks/useIntersectionObserver';
|
||||
import useOnChange from '../../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||
|
||||
const FAB_THRESHOLD = 50;
|
||||
const NOTCH_THRESHOLD = 1; // Notch has zero height so we at least need a 1px margin to intersect
|
||||
@ -136,14 +136,16 @@ export default function useScrollHooks(
|
||||
|
||||
useOnIntersect(fabTriggerRef, observeIntersectionForNotch);
|
||||
|
||||
useOnChange(() => {
|
||||
const toggleScrollToolsRef = useRef<typeof toggleScrollTools>();
|
||||
toggleScrollToolsRef.current = toggleScrollTools;
|
||||
useSyncEffect(() => {
|
||||
if (isReady) {
|
||||
toggleScrollTools();
|
||||
toggleScrollToolsRef.current!();
|
||||
}
|
||||
}, [isReady]);
|
||||
|
||||
// Workaround for FAB and notch flickering with tall incoming message
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
freezeForFab();
|
||||
freezeForNotch();
|
||||
|
||||
@ -151,7 +153,7 @@ export default function useScrollHooks(
|
||||
unfreezeForNotch();
|
||||
unfreezeForFab();
|
||||
}, TOOLS_FREEZE_TIMEOUT);
|
||||
}, [messageIds]);
|
||||
}, [freezeForFab, freezeForNotch, messageIds, unfreezeForFab, unfreezeForNotch]);
|
||||
|
||||
return { backwardsTriggerRef, forwardsTriggerRef, fabTriggerRef };
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ const Invoice: FC<OwnProps> = ({
|
||||
contentEl.setAttribute(CUSTOM_APPENDIX_ATTRIBUTE, '');
|
||||
});
|
||||
}
|
||||
}, [shouldAffectAppendix, photoUrl, isInSelectMode, isSelected, theme] as const);
|
||||
}, [shouldAffectAppendix, photoUrl, isInSelectMode, isSelected, theme]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@ -161,7 +161,7 @@ const Location: FC<OwnProps> = ({
|
||||
contentEl.setAttribute(CUSTOM_APPENDIX_ATTRIBUTE, '');
|
||||
});
|
||||
}
|
||||
}, [shouldRenderText, isOwn, isInSelectMode, isSelected, theme, mapBlobUrl] as const);
|
||||
}, [shouldRenderText, isOwn, isInSelectMode, isSelected, theme, mapBlobUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
// Prevent map refetching for slight location changes
|
||||
|
||||
@ -161,7 +161,7 @@ const Photo: FC<OwnProps> = ({
|
||||
} else {
|
||||
contentEl.classList.add('has-appendix-thumb');
|
||||
}
|
||||
}, [shouldAffectAppendix, fullMediaData, isOwn, isInSelectMode, isSelected, theme] as const);
|
||||
}, [shouldAffectAppendix, fullMediaData, isOwn, isInSelectMode, isSelected, theme]);
|
||||
|
||||
const { width, height, isSmall } = dimensions || calculateMediaDimensions(message, asForwarded, noAvatars, isMobile);
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef } from '../../../lib/teact/teact';
|
||||
|
||||
import useOnChange from '../../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||
import useForceUpdate from '../../../hooks/useForceUpdate';
|
||||
|
||||
export default function useAsyncRendering<T extends any[]>(dependencies: T, delay?: number) {
|
||||
@ -9,7 +9,7 @@ export default function useAsyncRendering<T extends any[]>(dependencies: T, dela
|
||||
const timeoutRef = useRef<number>();
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
@ -20,6 +20,7 @@ export default function useAsyncRendering<T extends any[]>(dependencies: T, dela
|
||||
clearTimeout(timeoutRef.current);
|
||||
timeoutRef.current = undefined;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, dependencies);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -38,7 +38,7 @@ export default function useProfileState(
|
||||
}, PROGRAMMATIC_SCROLL_TIMEOUT_MS);
|
||||
}
|
||||
}
|
||||
}, [tabType, isFirstTab, onProfileStateChange]);
|
||||
}, [tabType, isFirstTab, onProfileStateChange, containerRef]);
|
||||
|
||||
// Scroll to top
|
||||
useEffectWithPrevDeps(([prevProfileState]) => {
|
||||
@ -70,7 +70,7 @@ export default function useProfileState(
|
||||
}, PROGRAMMATIC_SCROLL_TIMEOUT_MS);
|
||||
|
||||
onProfileStateChange(profileState);
|
||||
}, [profileState]);
|
||||
}, [profileState, containerRef, onProfileStateChange]);
|
||||
|
||||
const determineProfileState = useCallback(() => {
|
||||
const container = containerRef.current;
|
||||
|
||||
@ -7,7 +7,7 @@ import type { ProfileTabType, SharedMediaType } from '../../../types';
|
||||
|
||||
import { MEMBERS_SLICE, MESSAGE_SEARCH_SLICE, SHARED_MEDIA_SLICE } from '../../../config';
|
||||
import { getMessageContentIds, sortChatIds, sortUserIds } from '../../../global/helpers';
|
||||
import useOnChange from '../../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
||||
|
||||
export default function useProfileViewportIds(
|
||||
@ -150,11 +150,11 @@ function useInfiniteScrollForSharedMedia(
|
||||
) {
|
||||
const messageIdsRef = useRef<number[]>();
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
messageIdsRef.current = undefined;
|
||||
}, [topicId]);
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
if (currentResultType === forSharedMediaType && chatMessages && foundIds) {
|
||||
messageIdsRef.current = getMessageContentIds(
|
||||
chatMessages,
|
||||
|
||||
@ -18,7 +18,7 @@ import InputText from '../../ui/InputText';
|
||||
import RadioGroup from '../../ui/RadioGroup';
|
||||
import Button from '../../ui/Button';
|
||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||
import useOnChange from '../../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||
import CalendarModal from '../../common/CalendarModal';
|
||||
|
||||
const DEFAULT_USAGE_LIMITS = [1, 10, 100];
|
||||
@ -64,7 +64,7 @@ const ManageInvite: FC<OwnProps & StateProps> = ({
|
||||
onBack: onClose,
|
||||
});
|
||||
|
||||
useOnChange(([oldEditingInvite]) => {
|
||||
useSyncEffect(([oldEditingInvite]) => {
|
||||
if (oldEditingInvite === editingInvite) return;
|
||||
if (!editingInvite) {
|
||||
setTitle('');
|
||||
|
||||
@ -3,7 +3,7 @@ import React, { memo, useCallback, useRef } from '../../lib/teact/teact';
|
||||
import useVideoAutoPause from '../middle/message/hooks/useVideoAutoPause';
|
||||
import useVideoCleanup from '../../hooks/useVideoCleanup';
|
||||
import useBuffering from '../../hooks/useBuffering';
|
||||
import useOnChange from '../../hooks/useOnChange';
|
||||
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||
|
||||
type OwnProps =
|
||||
{
|
||||
@ -40,13 +40,13 @@ function OptimizedVideo({
|
||||
// This is only needed for browsers not allowing autoplay
|
||||
const { isBuffered, bufferingHandlers } = useBuffering(true, onTimeUpdate);
|
||||
const { onPlaying: handlePlayingForBuffering, ...otherBufferingHandlers } = bufferingHandlers;
|
||||
useOnChange(([prevIsBuffered]) => {
|
||||
useSyncEffect(([prevIsBuffered]) => {
|
||||
if (prevIsBuffered === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleReady();
|
||||
}, [isBuffered]);
|
||||
}, [isBuffered, handleReady]);
|
||||
|
||||
const handlePlaying = useCallback((e) => {
|
||||
handlePlayingForAutoPause();
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
import { selectTabState } from '../global/selectors';
|
||||
|
||||
import useEffectWithPrevDeps from './useEffectWithPrevDeps';
|
||||
import useOnChange from './useOnChange';
|
||||
import useSyncEffect from './useSyncEffect';
|
||||
|
||||
type Handler = (e: Event) => void;
|
||||
|
||||
@ -47,7 +47,7 @@ const useAudioPlayer = (
|
||||
if (onTrackChange) onTrackChange();
|
||||
}, [onTrackChange]);
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
controllerRef.current = register(trackId, trackType, (eventName, e) => {
|
||||
switch (eventName) {
|
||||
case 'onPlay': {
|
||||
@ -105,6 +105,8 @@ const useAudioPlayer = (
|
||||
|
||||
if (!isPlaying && !proxy.paused) {
|
||||
setIsPlaying(true);
|
||||
// `isPlayingSync` is only needed to help `setIsPlaying` because it is asynchronous
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
isPlayingSync = true;
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useRef } from '../lib/teact/teact';
|
||||
|
||||
import fastBlur from '../lib/fastBlur';
|
||||
import useOnChange from './useOnChange';
|
||||
import useSyncEffect from './useSyncEffect';
|
||||
import useBlur from './useBlur';
|
||||
import { imgToCanvas } from '../util/files';
|
||||
|
||||
@ -13,7 +13,8 @@ export default function useBlurSync(dataUri: string | false | undefined) {
|
||||
|
||||
let isChanged = false;
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
isChanged = true;
|
||||
|
||||
blurredRef.current = undefined;
|
||||
|
||||
@ -2,7 +2,7 @@ import { useEffect, useRef } from '../lib/teact/teact';
|
||||
|
||||
import { IS_CANVAS_FILTER_SUPPORTED } from '../util/environment';
|
||||
import fastBlur from '../lib/fastBlur';
|
||||
import useOnChange from './useOnChange';
|
||||
import useSyncEffect from './useSyncEffect';
|
||||
|
||||
const RADIUS = 2;
|
||||
const ITERATIONS = 2;
|
||||
@ -19,7 +19,7 @@ export default function useCanvasBlur(
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const isStarted = useRef();
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
if (!isDisabled) {
|
||||
isStarted.current = false;
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useCallback, useRef, useState } from '../lib/teact/teact';
|
||||
|
||||
import useRunDebounced from './useRunDebounced';
|
||||
import useOnChange from './useOnChange';
|
||||
import useSyncEffect from './useSyncEffect';
|
||||
import useHeavyAnimationCheck, { isHeavyAnimating } from './useHeavyAnimationCheck';
|
||||
import useForceUpdate from './useForceUpdate';
|
||||
|
||||
@ -12,7 +12,7 @@ export default function useDebouncedMemo<R extends any, D extends any[]>(
|
||||
const { isFrozen, updateWhenUnfrozen } = useHeavyAnimationFreeze();
|
||||
const runDebounced = useRunDebounced(ms, true);
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
if (isFrozen) {
|
||||
updateWhenUnfrozen();
|
||||
return;
|
||||
@ -21,6 +21,7 @@ export default function useDebouncedMemo<R extends any, D extends any[]>(
|
||||
runDebounced(() => {
|
||||
setValue(resolverFn());
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [...dependencies, isFrozen]);
|
||||
|
||||
return value;
|
||||
|
||||
8
src/hooks/useEffectOnce.ts
Normal file
8
src/hooks/useEffectOnce.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { useEffect } from '../lib/teact/teact';
|
||||
|
||||
function useEffectOnce(effect: React.EffectCallback) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
useEffect(effect, []);
|
||||
}
|
||||
|
||||
export default useEffectOnce;
|
||||
@ -1,13 +1,13 @@
|
||||
import { useCallback, useRef } from '../lib/teact/teact';
|
||||
|
||||
import useForceUpdate from './useForceUpdate';
|
||||
import useOnChange from './useOnChange';
|
||||
import useSyncEffect from './useSyncEffect';
|
||||
|
||||
export default function useForumPanelRender(isForumPanelOpen = false) {
|
||||
const shouldRenderForumPanelRef = useRef(isForumPanelOpen);
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
if (isForumPanelOpen) {
|
||||
shouldRenderForumPanelRef.current = true;
|
||||
}
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import useOnChange from './useOnChange';
|
||||
import { useEffect, useRef } from '../lib/teact/teact';
|
||||
import { useCallback, useRef } from '../lib/teact/teact';
|
||||
import { getActions } from '../lib/teact/teactn';
|
||||
|
||||
import { IS_TEST } from '../config';
|
||||
import { fastRaf } from '../util/schedulers';
|
||||
import { IS_IOS } from '../util/environment';
|
||||
import { getActions } from '../lib/teact/teactn';
|
||||
|
||||
import useSyncEffect from './useSyncEffect';
|
||||
import useEffectOnce from './useEffectOnce';
|
||||
|
||||
export const LOCATION_HASH = window.location.hash;
|
||||
const PATH_BASE = `${window.location.pathname}${window.location.search}`;
|
||||
@ -241,7 +244,7 @@ export default function useHistoryBack({
|
||||
|
||||
const isFirstRender = useRef(true);
|
||||
|
||||
const pushState = (forceReplace = false) => {
|
||||
const pushState = useCallback((forceReplace = false) => {
|
||||
// Check if the old state should be replaced
|
||||
const shouldReplace = forceReplace || historyState[historyCursor].shouldBeReplaced;
|
||||
indexRef.current = shouldReplace ? historyCursor : ++historyCursor;
|
||||
@ -276,9 +279,9 @@ export default function useHistoryBack({
|
||||
},
|
||||
hash: hash ? `#${hash}` : undefined,
|
||||
});
|
||||
};
|
||||
}, [hash, onBack, shouldBeReplaced]);
|
||||
|
||||
const processBack = () => {
|
||||
const processBack = useCallback(() => {
|
||||
// Only process back on open records
|
||||
if (indexRef.current && historyState[indexRef.current] && !wasReplaced.current) {
|
||||
historyState[indexRef.current].isClosed = true;
|
||||
@ -287,19 +290,19 @@ export default function useHistoryBack({
|
||||
historyCursor -= cleanupClosed();
|
||||
}
|
||||
}
|
||||
};
|
||||
}, [shouldBeReplaced]);
|
||||
|
||||
// Process back navigation when element is unmounted
|
||||
useEffect(() => {
|
||||
useEffectOnce(() => {
|
||||
isFirstRender.current = false;
|
||||
return () => {
|
||||
if (!isActive || wasReplaced.current) return;
|
||||
processBack();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
});
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(([prevIsActive]) => {
|
||||
if (prevIsActive === isActive) return;
|
||||
if (isFirstRender.current && !isActive) return;
|
||||
|
||||
if (isActive) {
|
||||
@ -307,5 +310,5 @@ export default function useHistoryBack({
|
||||
} else {
|
||||
processBack();
|
||||
}
|
||||
}, [isActive]);
|
||||
}, [isActive, processBack, pushState]);
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
} from '../lib/teact/teact';
|
||||
|
||||
import { throttle, debounce } from '../util/schedulers';
|
||||
import useEffectOnce from './useEffectOnce';
|
||||
import useHeavyAnimationCheck from './useHeavyAnimationCheck';
|
||||
|
||||
type TargetCallback = (entry: IntersectionObserverEntry) => void;
|
||||
@ -155,11 +156,10 @@ export function useIntersectionObserver({
|
||||
export function useOnIntersect(
|
||||
targetRef: RefObject<HTMLDivElement>, observe?: ObserveFn, callback?: TargetCallback,
|
||||
) {
|
||||
useEffect(() => {
|
||||
useEffectOnce(() => {
|
||||
return observe ? observe(targetRef.current!, callback) : undefined;
|
||||
// Arguments should never change
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
});
|
||||
}
|
||||
|
||||
export function useIsIntersecting(
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import * as langProvider from '../util/langProvider';
|
||||
import useForceUpdate from './useForceUpdate';
|
||||
import useOnChange from './useOnChange';
|
||||
import useSyncEffect from './useSyncEffect';
|
||||
|
||||
export type LangFn = langProvider.LangFn;
|
||||
|
||||
const useLang = (): LangFn => {
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
return langProvider.addCallback(forceUpdate);
|
||||
}, [forceUpdate]);
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import { useRef } from '../lib/teact/teact';
|
||||
|
||||
import usePrevious from './usePrevious';
|
||||
import useForceUpdate from './useForceUpdate';
|
||||
import useOnChange from './useOnChange';
|
||||
import useSyncEffect from './useSyncEffect';
|
||||
|
||||
export default function usePrevDuringAnimation(current: any, duration?: number) {
|
||||
const prev = usePrevious(current, true);
|
||||
@ -18,7 +18,7 @@ export default function usePrevDuringAnimation(current: any, duration?: number)
|
||||
timeoutRef.current = undefined;
|
||||
}
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
// When `current` becomes empty
|
||||
if (duration && !isCurrentPresent && isPrevPresent && !timeoutRef.current) {
|
||||
timeoutRef.current = window.setTimeout(() => {
|
||||
@ -26,7 +26,7 @@ export default function usePrevDuringAnimation(current: any, duration?: number)
|
||||
forceUpdate();
|
||||
}, duration);
|
||||
}
|
||||
}, [current]);
|
||||
}, [duration, forceUpdate, isCurrentPresent, isPrevPresent]);
|
||||
|
||||
return !timeoutRef.current || !duration || isCurrentPresent ? current : prev;
|
||||
}
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { useRef } from '../lib/teact/teact';
|
||||
|
||||
import useOnChange from './useOnChange';
|
||||
import useSyncEffect from './useSyncEffect';
|
||||
|
||||
// Allows to use state value as "silent" dependency in hooks (not causing updates).
|
||||
// Useful for state values that update frequently (such as controlled input value).
|
||||
export function useStateRef<T>(value: T) {
|
||||
const ref = useRef<T>(value);
|
||||
|
||||
useOnChange(() => {
|
||||
useSyncEffect(() => {
|
||||
ref.current = value;
|
||||
}, [value]);
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import usePrevious from './usePrevious';
|
||||
|
||||
const useOnChange = <T extends readonly any[]>(cb: (args: T | readonly []) => void, dependencies: T) => {
|
||||
const useSyncEffect = <T extends readonly any[]>(cb: (args: T | readonly []) => void, dependencies: T) => {
|
||||
const prevDeps = usePrevious<T>(dependencies);
|
||||
if (!prevDeps || dependencies.some((d, i) => d !== prevDeps[i])) {
|
||||
cb(prevDeps || []);
|
||||
}
|
||||
};
|
||||
|
||||
export default useOnChange;
|
||||
export default useSyncEffect;
|
||||
@ -10,7 +10,7 @@ const THROTTLE = 250;
|
||||
const useWindowSize = () => {
|
||||
const [size, setSize] = useState<ApiDimensions>(windowSize.get());
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
const setIsResizingDebounced = useDebouncedCallback(setIsResizing, [], THROTTLE, true);
|
||||
const setIsResizingDebounced = useDebouncedCallback(setIsResizing, [setIsResizing], THROTTLE, true);
|
||||
|
||||
const result = useMemo(() => ({ ...size, isResizing }), [isResizing, size]);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user