Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/CSSMotion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ export function genCSSMotion(config: CSSMotionConfig) {

// We should render children when motionStyle is sync with stepStatus
return React.useMemo(() => {
if (styleReady === 'NONE') {
return null;
}

let motionChildren: React.ReactNode;
const mergedProps = { ...eventProps, visible };

Expand Down
11 changes: 9 additions & 2 deletions src/hooks/useStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default function useStatus(
stepStatus: StepStatus,
style: React.CSSProperties,
visible: boolean,
styleReady: boolean,
styleReady: 'NONE' | boolean,
] {
// Used for outer render usage to avoid `visible: false & status: none` to render nothing
const [asyncVisible, setAsyncVisible] = React.useState<boolean>();
Expand Down Expand Up @@ -311,6 +311,13 @@ export default function useStatus(
step,
mergedStyle,
asyncVisible ?? visible,
step === STEP_START || step === STEP_ACTIVE ? styleStep === step : true,

!mountedRef.current && currentStatus === STATUS_NONE
? // Appear
'NONE'
: // Enter or Leave
step === STEP_START || step === STEP_ACTIVE
? styleStep === step
: true,
Comment on lines +315 to +321

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

styleReady 设置为 'NONE' 的条件 !mountedRef.current && currentStatus === STATUS_NONE 过于宽泛。如果组件在挂载时 visible={true}motionAppear={false},这会导致组件渲染为 null 并且卡在这个状态。

该条件应该更具体,仅适用于 "appear" 动画。它应该检查 motionAppearvisible 属性,类似于为 STATUS_APPEAR 确定 nextStatus 的方式。

    !mountedRef.current && visible && motionAppear
      ? 'NONE'
      : step === STEP_START || step === STEP_ACTIVE
      ? styleStep === step
      : true,

];
}
18 changes: 18 additions & 0 deletions tests/CSSMotion.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,24 @@ describe('CSSMotion', () => {
expect(activeBoxNode).toHaveClass(`animation-leave-active`);
});

it('styleReady returns NONE on first mount when status is STATUS_NONE', () => {
const mockRender = jest.fn(() => null) as jest.Mock;
(mockRender as any).mock.calls = [] as any;

render(
<CSSMotion visible motionAppear motionName="test">
{mockRender}
</CSSMotion>,
);

Comment on lines +495 to +504

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

这个测试的标题和注释有点误导性。该测试并未直接检查 styleReady 的值,而是检查其导致跳过第一次渲染的后果。一个更具描述性的标题和注释将提高代码的清晰度和可维护性。

例如:

标题: it('should skip initial render frame when motionAppear is enabled')

注释: // The first render of children should be in the 'prepare' stage

Suggested change
it('styleReady returns NONE on first mount when status is STATUS_NONE', () => {
const mockRender = jest.fn(() => null) as jest.Mock;
(mockRender as any).mock.calls = [] as any;
render(
<CSSMotion visible motionAppear motionName="test">
{mockRender}
</CSSMotion>,
);
it('should skip initial render frame when motionAppear is enabled', () => {
const mockRender = jest.fn(() => null);
render(
<CSSMotion visible motionAppear motionName="test">
{mockRender}
</CSSMotion>,
);
// The first render of children should be in the 'prepare' stage

// First render (prepare stage)
expect(mockRender.mock.calls[0][0]).toEqual(
expect.objectContaining({
className: 'test-appear test-appear-prepare test',
}),
);
});
Comment on lines 495 to 511
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

CI 构建失败:TypeScript 类型错误 TS2493,需要为 mock 函数添加参数类型。

流水线报错 Tuple type '[]' of length '0' has no element at index '0',原因是 jest.fn(() => null) 没有声明参数,TypeScript 推断 mock.calls 中每次调用的参数元组为 [],导致 calls[0][0] 无法通过类型检查。

🔧 修复建议
- const mockRender = jest.fn(() => null);
+ const mockRender = jest.fn((_props: Record<string, any>, _ref?: any) => null);

或者使用类型断言:

- expect(mockRender.mock.calls[0][0]).toEqual(
+ expect((mockRender.mock.calls[0] as [any, any])[0]).toEqual(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('styleReady returns NONE on first mount when status is STATUS_NONE', () => {
const mockRender = jest.fn(() => null);
render(
<CSSMotion visible motionAppear motionName="test">
{mockRender}
</CSSMotion>,
);
// First render (prepare stage)
expect(mockRender.mock.calls[0][0]).toEqual(
expect.objectContaining({
className: 'test-appear test-appear-prepare test',
}),
);
});
it('styleReady returns NONE on first mount when status is STATUS_NONE', () => {
const mockRender = jest.fn((_props: Record<string, any>, _ref?: any) => null);
render(
<CSSMotion visible motionAppear motionName="test">
{mockRender}
</CSSMotion>,
);
// First render (prepare stage)
expect(mockRender.mock.calls[0][0]).toEqual(
expect.objectContaining({
className: 'test-appear test-appear-prepare test',
}),
);
});
🧰 Tools
🪛 GitHub Actions: ✅ test

[error] 505-505: Command 'bunx tsc --noEmit' failed with TS2493: Tuple type '[]' of length '0' has no element at index '0'.

🤖 Prompt for AI Agents
In `@tests/CSSMotion.spec.tsx` around lines 495 - 510, The TypeScript error comes
from using jest.fn(() => null) without a parameter type so TypeScript infers the
mock's call tuple as empty; update the mock declaration for mockRender to
include an explicit call signature (e.g., use jest.fn<(arg: any) => any>(() =>
null) or jest.fn<[any], any>(() => null) or type it as jest.Mock<any, any>) so
that mockRender.mock.calls[0][0] is correctly typed; change the jest.fn
instantiation where mockRender is declared in the CSSMotion test.


describe('immediately', () => {
it('motionLeaveImmediately', async () => {
const { container } = render(
Expand Down