Skip to content

Commit

Permalink
fix(View): add useLayoutEffectCall (#7164)
Browse files Browse the repository at this point in the history
- see #6920

---

> This mutates a variable that React considers immutable

Компилятор ругается на мутацию afterTransition

* refactor: call to add
  • Loading branch information
SevereCloud authored Jul 15, 2024
1 parent e1dcb63 commit cdf2646
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 13 deletions.
22 changes: 9 additions & 13 deletions packages/vkui/src/components/View/View.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import * as React from 'react';
import { classNames, noop } from '@vkontakte/vkjs';
import { classNames } from '@vkontakte/vkjs';
import { usePlatform } from '../../hooks/usePlatform';
import { usePrevious } from '../../hooks/usePrevious';
import { blurActiveElement, canUseDOM, useDOM } from '../../lib/dom';
import { getNavId, NavIdProps } from '../../lib/getNavId';
import { useIsomorphicLayoutEffect } from '../../lib/useIsomorphicLayoutEffect';
import { warnOnce } from '../../lib/warnOnce';
import { HTMLAttributesWithRootRef } from '../../types';
import { useScroll } from '../AppRoot/ScrollContext';
Expand All @@ -14,6 +13,7 @@ import { NavTransitionProvider } from '../NavTransitionContext/NavTransitionCont
import { NavTransitionDirectionProvider } from '../NavTransitionDirectionContext/NavTransitionDirectionContext';
import { useSplitCol } from '../SplitCol/SplitColContext';
import { Touch, TouchEvent } from '../Touch/Touch';
import { useLayoutEffectCall } from './useLayoutEffectCall';
import {
getSwipeBackPredicates,
hasHorizontalScrollableElementWithScrolledToLeft,
Expand Down Expand Up @@ -72,7 +72,7 @@ export const View = ({
}: ViewProps): React.ReactNode => {
const id = getNavId({ nav, id: restProps.id });
const scrolls = React.useRef(scrollsCache[id as string] || {});
const afterTransition = React.useRef(noop);
const layoutEffectCall = useLayoutEffectCall();

React.useEffect(() => () => {
if (id) {
Expand Down Expand Up @@ -143,24 +143,19 @@ export const View = ({
setAnimated(false);
setIsBack(isBackTransition);

afterTransition.current = () => {
layoutEffectCall(() => {
scroll?.scrollTo(0, isBackTransition ? scrolls.current[activePanelProp] : 0);
onTransition &&
onTransition({
isBack: isBackTransition,
from: prevPanel,
to: activePanelProp,
});
};
});
},
[activePanelProp, onTransition, scroll],
[activePanelProp, layoutEffectCall, onTransition, scroll],
);

useIsomorphicLayoutEffect(() => {
afterTransition.current();
afterTransition.current = noop;
}, [afterTransition.current]);

const onAnimationEnd = React.useCallback(() => {
if (prevPanel !== null) {
flushTransition(prevPanel, Boolean(isBack));
Expand Down Expand Up @@ -384,7 +379,7 @@ export const View = ({
setVisiblePanels([nextPanel]);
setIsBack(true);

afterTransition.current = () => {
layoutEffectCall(() => {
if (nextPanel !== null) {
scroll?.scrollTo(0, scrolls.current[nextPanel]);
}
Expand All @@ -394,7 +389,7 @@ export const View = ({
from: prevPanel,
to: nextPanel,
});
};
});
}

// Началась анимация завершения свайпа назад.
Expand Down Expand Up @@ -426,6 +421,7 @@ export const View = ({
scroll,
swipeBackNextPanel,
swipeBackResult,
layoutEffectCall,
]);

React.useEffect(
Expand Down
42 changes: 42 additions & 0 deletions packages/vkui/src/components/View/useLayoutEffectCall.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as React from 'react';
import { useIsomorphicLayoutEffect } from '../../lib/useIsomorphicLayoutEffect';

class LayoutEffectCall {
#fns: Array<() => void> = [];

/**
* Выполняет переданные функции
*/
run() {
for (const fn of this.#fns) {
fn();
}

this.#fns = [];
}

/**
* Вызовет функцию после изменения DOM, но до того как пользователь увидит
* изменения
*/
add = (fn: () => void) => {
this.#fns.push(fn);
};
}

/**
* Возвращает функцию которая вызывает callback после изменения DOM, но до того
* как пользователь увидит изменения
*/
export function useLayoutEffectCall() {
const ref = React.useRef<LayoutEffectCall | null>(null);
if (!ref.current) {
ref.current = new LayoutEffectCall();
}

useIsomorphicLayoutEffect(() => {
ref.current!.run();
});

return ref.current.add;
}

0 comments on commit cdf2646

Please sign in to comment.