我想测试我的应用程序,让用户输入一个向 Google Books API 发送请求的查询,然后显示响应。
我已经将数据获取逻辑分离到它自己的自定义钩子中。我想用 Mock Service Worker 来模拟 fetch。我在运行测试时收到无效的挂钩调用。
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
3 |
4 | export const useBooksFetch = (initialUrl, initialData) => {
> 5 | const [url, setUrl] = useState(initialUrl);
| ^
6 | const [data, setData] = useState(initialData);
7 | const [isLoading, setIsLoading] = useState(false);
8 | const [isError, setIsError] = useState(false);
at resolveDispatcher (node_modules/react/cjs/react.development.js:1476:13)
at useState (node_modules/react/cjs/react.development.js:1507:20)
at useBooksFetch (src/hooks/useBooksFetch/useBooksFetch.js:5:25)
at Object.<anonymous> (src/hooks/useBooksFetch/useBooksFetch.test.js:78:24)
console.warn
[MSW] Found a redundant usage of query parameters in the request handler URL for "GET https://www.googleapis.com/books/v1/volumes?q=travel". Please match against a path instead, and access query parameters in the response resolver function:
rest.get("/books/v1/volumes", (req, res, ctx) => {
const query = req.url.searchParams
const q = query.get("q")
})
4 |
5 | const server = setupServer(
> 6 | rest.get(
| ^
7 | "https://www.googleapis.com/books/v1/volumes?q=travel",
8 | (req, res, ctx) => {
9 | return res(
确认
- 在运行
npm ls react-dom
and时确认匹配的 React 版本npm ls react
,并返回react-dom@17.0.2
andreact@17.0.2
。 - 未在类组件、事件处理程序、useMemo、useReducer 或 useEffect 中调用。
我究竟做错了什么?代码沙盒
// useBooksFetch.js
export const useBooksFetch = (initialUrl, initialData) => {
const [url, setUrl] = useState(initialUrl);
const [data, setData] = useState(initialData);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
useEffect(() => {
const fetchBooks = async () => {
setIsError(false);
setIsLoading(true);
try {
const result = await axios(url);
setData(result.data);
} catch (isError) {
setIsError(true);
}
setIsLoading(false);
};
fetchBooks();
}, [url]);
return [{ data, isLoading, isError }, setUrl];
};
// useBooksFetch.test.js
const server = setupServer(
rest.get(
"https://www.googleapis.com/books/v1/volumes?q=travel",
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
data: {
items: [
{
volumeInfo: {
title: "Travels"
}
},
{
volumeInfo: {
title: "The Travel Book"
}
},
{
volumeInfo: {
title: "Two Arabic Travel Books"
}
},
{
volumeInfo: {
title: "Around India in 80 Trains"
}
},
{
volumeInfo: {
title: "World Travel"
}
},
{
volumeInfo: {
title:
"The ‘Book’ of Travels: Genre, Ethnology, and Pilgrimage, 1250-1700"
}
},
{
volumeInfo: {
title: "The Impossible Collection of Chinese Art"
}
},
{
volumeInfo: {
title: "Travel Home"
}
},
{
volumeInfo: {
title: "Maximum City"
}
},
{
volumeInfo: {
title: "The Art of Travel"
}
}
]
}
})
);
}
)
);
beforeAll(() => server.listen());
afterAll(() => server.close());
afterEach(() => server.resetHandlers());
it("fetches successfully", async () => {
const result = await useBooksFetch();
expect(result.data).toHaveLength(10);
});