我想使用 useActionState 挂钩测试表单。它确实使用简单的异步函数,而不是服务器操作。我想断言状态会增加,但在我做出这样的断言之前测试就失败了。
import React from "react";
import { useActionState } from "react";
import { describe, test, expect } from "vitest";
import { act, render, screen } from "@testing-library/react";
import { userEvent } from "@testing-library/user-event";
describe("useActionState", () => {
test("it works", async () => {
async function increment(previousState: number, formData: any) {
return previousState + 1;
}
function StatefulForm({}) {
const [state, formAction] = useActionState(increment, 0);
return (
<>
<p data-testid="state">{state}</p>
<form action={formAction}>
<button type="submit" data-testid="submit">
Increment
</button>
</form>
</>
);
}
const { debug } = render(<StatefulForm />);
expect(screen.getByTestId("state")).toBeInTheDocument();
debug();
const submit = screen.getByTestId("submit");
await act(() => userEvent.click(submit));
// throws error:
// Error: A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you're trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().
});
});
事实证明,“action”属性创建了一个隐式的 Action Context(它会抛出错误)。 我们可以通过使用
useTransition
钩子创建显式的 Action Context 来避免这种情况:
import { type FormEvent, useTransition} from "react";
function StatefulForm({}) {
const [state, formAction] = useActionState(increment, 0);
const [,startTransition] = useTransition();
return (
<>
<p data-testid="state">{state}</p>
<form onSubmit={(event: FormEvent<HTMLFormElement>) => {
// by using custom event handler, we can prevent the reset of form
event.preventDefault();
const form = event.currentTarget;
// the formAction must be called in an ActionContext - transition
startTransition(() => {
formAction(new FormData(form));
});
}
}>
<button type="submit" data-testid="submit">
Increment
</button>
</form>
</>
);
}