Skip to content

Commit 9d47258

Browse files
zombieJclaude
andcommitted
refactor: add NONE state for styleReady to distinguish initial mount
On initial mount when status is STATUS_NONE, return 'NONE' instead of true to prevent rendering children until style is ready. This improves the appear animation behavior by ensuring style synchronization. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 634cdf2 commit 9d47258

3 files changed

Lines changed: 31 additions & 2 deletions

File tree

src/CSSMotion.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ export function genCSSMotion(config: CSSMotionConfig) {
190190

191191
// We should render children when motionStyle is sync with stepStatus
192192
return React.useMemo(() => {
193+
if (styleReady === 'NONE') {
194+
return null;
195+
}
196+
193197
let motionChildren: React.ReactNode;
194198
const mergedProps = { ...eventProps, visible };
195199

src/hooks/useStatus.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export default function useStatus(
5353
stepStatus: StepStatus,
5454
style: React.CSSProperties,
5555
visible: boolean,
56-
styleReady: boolean,
56+
styleReady: 'NONE' | boolean,
5757
] {
5858
// Used for outer render usage to avoid `visible: false & status: none` to render nothing
5959
const [asyncVisible, setAsyncVisible] = React.useState<boolean>();
@@ -311,6 +311,13 @@ export default function useStatus(
311311
step,
312312
mergedStyle,
313313
asyncVisible ?? visible,
314-
step === STEP_START || step === STEP_ACTIVE ? styleStep === step : true,
314+
315+
!mountedRef.current && currentStatus === STATUS_NONE
316+
? // Appear
317+
'NONE'
318+
: // Enter or Leave
319+
step === STEP_START || step === STEP_ACTIVE
320+
? styleStep === step
321+
: true,
315322
];
316323
}

tests/CSSMotion.spec.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,24 @@ describe('CSSMotion', () => {
492492
expect(activeBoxNode).toHaveClass(`animation-leave-active`);
493493
});
494494

495+
it('styleReady returns NONE on first mount when status is STATUS_NONE', () => {
496+
const mockRender = jest.fn(() => null);
497+
498+
render(
499+
<CSSMotion visible motionAppear motionName="test">
500+
{mockRender}
501+
</CSSMotion>,
502+
);
503+
504+
expect(mockRender).toHaveBeenCalledTimes(1);
505+
expect(mockRender).toHaveBeenCalledWith(
506+
expect.objectContaining({
507+
// TODO: update with correct className
508+
className: '',
509+
}),
510+
);
511+
});
512+
495513
describe('immediately', () => {
496514
it('motionLeaveImmediately', async () => {
497515
const { container } = render(

0 commit comments

Comments
 (0)