From 50cbe39fe24208d8bf57152615741c8ad7785ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E7=88=B1=E5=90=83=E7=99=BD=E8=90=9D?= =?UTF-8?q?=E5=8D=9C?= Date: Mon, 17 Jun 2024 14:33:27 +0800 Subject: [PATCH] fix: useSyncState logic (#52) --- package.json | 2 +- src/hooks/useStatus.ts | 88 +++++++++++++++++++++++------------------- 2 files changed, 49 insertions(+), 41 deletions(-) diff --git a/package.json b/package.json index a287bfd..6216cbe 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "dependencies": { "@babel/runtime": "^7.11.1", "classnames": "^2.2.1", - "rc-util": "^5.39.3" + "rc-util": "^5.43.0" }, "devDependencies": { "@rc-component/father-plugin": "^1.0.1", diff --git a/src/hooks/useStatus.ts b/src/hooks/useStatus.ts index fa79cc8..b040028 100644 --- a/src/hooks/useStatus.ts +++ b/src/hooks/useStatus.ts @@ -1,5 +1,6 @@ import { useEvent } from 'rc-util'; import useState from 'rc-util/lib/hooks/useState'; +import useSyncState from 'rc-util/lib/hooks/useSyncState'; import * as React from 'react'; import { useEffect, useRef } from 'react'; import type { CSSMotionProps } from '../CSSMotion'; @@ -51,9 +52,11 @@ export default function useStatus( ): [MotionStatus, StepStatus, React.CSSProperties, boolean] { // Used for outer render usage to avoid `visible: false & status: none` to render nothing const [asyncVisible, setAsyncVisible] = useState(); - const [status, setStatus] = useState(STATUS_NONE); + const [getStatus, setStatus] = useSyncState(STATUS_NONE); const [style, setStyle] = useState(null); + const currentStatus = getStatus(); + const mountedRef = useRef(false); const deadlineRef = useRef(null); @@ -69,11 +72,12 @@ export default function useStatus( * Clean up status & style */ function updateMotionEndStatus() { - setStatus(STATUS_NONE, true); + setStatus(STATUS_NONE); setStyle(null, true); } const onInternalMotionEnd = useEvent((event: MotionEvent) => { + const status = getStatus(); // Do nothing since not in any transition status. // This may happen when `motionDeadline` trigger. if (status === STATUS_NONE) { @@ -140,44 +144,48 @@ export default function useStatus( [STEP_PREPARE]?: MotionPrepareEventHandler; [STEP_START]?: MotionEventHandler; [STEP_ACTIVE]?: MotionEventHandler; - }>(() => getEventHandlers(status), [status]); - - const [startStep, step] = useStepQueue(status, !supportMotion, newStep => { - // Only prepare step can be skip - if (newStep === STEP_PREPARE) { - const onPrepare = eventHandlers[STEP_PREPARE]; - if (!onPrepare) { - return SkipStep; + }>(() => getEventHandlers(currentStatus), [currentStatus]); + + const [startStep, step] = useStepQueue( + currentStatus, + !supportMotion, + newStep => { + // Only prepare step can be skip + if (newStep === STEP_PREPARE) { + const onPrepare = eventHandlers[STEP_PREPARE]; + if (!onPrepare) { + return SkipStep; + } + + return onPrepare(getDomElement()); } - return onPrepare(getDomElement()); - } - - // Rest step is sync update - if (step in eventHandlers) { - setStyle(eventHandlers[step]?.(getDomElement(), null) || null); - } + // Rest step is sync update + if (step in eventHandlers) { + setStyle(eventHandlers[step]?.(getDomElement(), null) || null); + } - if (step === STEP_ACTIVE && status !== STATUS_NONE) { - // Patch events when motion needed - patchMotionEvents(getDomElement()); - - if (motionDeadline > 0) { - clearTimeout(deadlineRef.current); - deadlineRef.current = setTimeout(() => { - onInternalMotionEnd({ - deadline: true, - } as MotionEvent); - }, motionDeadline); + if (step === STEP_ACTIVE && currentStatus !== STATUS_NONE) { + // Patch events when motion needed + patchMotionEvents(getDomElement()); + + if (motionDeadline > 0) { + clearTimeout(deadlineRef.current); + deadlineRef.current = setTimeout(() => { + onInternalMotionEnd({ + deadline: true, + } as MotionEvent); + }, motionDeadline); + } } - } - if (step === STEP_PREPARED) { - updateMotionEndStatus(); - } + if (step === STEP_PREPARED) { + updateMotionEndStatus(); + } - return DoStep; - }); + return DoStep; + }, + ); const active = isActive(step); activeRef.current = active; @@ -231,11 +239,11 @@ export default function useStatus( useEffect(() => { if ( // Cancel appear - (status === STATUS_APPEAR && !motionAppear) || + (currentStatus === STATUS_APPEAR && !motionAppear) || // Cancel enter - (status === STATUS_ENTER && !motionEnter) || + (currentStatus === STATUS_ENTER && !motionEnter) || // Cancel leave - (status === STATUS_LEAVE && !motionLeave) + (currentStatus === STATUS_LEAVE && !motionLeave) ) { setStatus(STATUS_NONE); } @@ -257,14 +265,14 @@ export default function useStatus( firstMountChangeRef.current = true; } - if (asyncVisible !== undefined && status === STATUS_NONE) { + if (asyncVisible !== undefined && currentStatus === STATUS_NONE) { // Skip first render is invisible since it's nothing changed if (firstMountChangeRef.current || asyncVisible) { onVisibleChanged?.(asyncVisible); } firstMountChangeRef.current = true; } - }, [asyncVisible, status]); + }, [asyncVisible, currentStatus]); // ============================ Styles ============================ let mergedStyle = style; @@ -275,5 +283,5 @@ export default function useStatus( }; } - return [status, step, mergedStyle, asyncVisible ?? visible]; + return [currentStatus, step, mergedStyle, asyncVisible ?? visible]; }