React+Redux+Apollo项目出现无效Hook调用错误该如何解决?
问题根因
错误来源于你在Redux Thunk的回调函数里调用了Apollo自定义Hook useRecipesQuery,违反了React Hook的核心使用规则:Hook只能在React函数组件的顶层、或者自定义Hook的顶层调用。
你写的fetchRecipes返回的Thunk函数是在组件的useEffect回调里执行的,不属于组件顶层调用场景,直接触发了非法Hook调用报错。
解决方案
方案1:组件内调用Hook,同步数据到Redux(更推荐,符合Hook使用规则)
把useRecipesQuery移到RecipesList组件的顶层调用,拿到数据后再同步到Redux,原有fetchRecipes Thunk可直接删除:
import React, { useEffect } from "react"; import { Flex, Container, Heading, Text } from "@chakra-ui/react"; import { useSelector, useDispatch } from "react-redux"; import { State } from "../state/store"; import { fetchRecipesPending, fetchRecipesSuccess, fetchRecipesFailure } from "../state/recipe/actions"; // 替换为你实际的graphql-codegen生成路径 import { useRecipesQuery } from '../generated/graphql'; interface RecipesListProps {} const RecipesList: React.FC<RecipesListProps> = () => { const recipes = useSelector<State>( (state) => state.recipe.recipes ) as State["recipe"]["recipes"]; const loading = useSelector<State>( (state) => state.recipe.loading ) as State["recipe"]["loading"]; const dispatch = useDispatch(); // 组件顶层调用Apollo Hook const { data, loading: queryLoading, error } = useRecipesQuery(); useEffect(() => { dispatch(fetchRecipesPending()); }, [dispatch]); useEffect(() => { if (error) { dispatch(fetchRecipesFailure([error.message])); return } if (data) { const errors = data?.recipes.errors; const recipes = data?.recipes.recipes; if (errors?.length) { dispatch(fetchRecipesFailure(errors)); } else { dispatch(fetchRecipesSuccess(recipes)); } } }, [data, error, dispatch]); if (loading || queryLoading) { return <h1>Loading....</h1>; } return ( <Flex m="auto" mt="5rem" w="50%" direction="column" justifyContent="center" alignItems="center" > <Heading>Your Recipes</Heading> <Flex mt="2rem" direction="column" w="100%" padding="0" gridGap="2rem"> {recipes && recipes.map((recipe) => ( <Container key={recipe.id} bg="orange.100" borderRadius="0.2rem" padding="1rem" maxW="100%" > <Text fontSize="xl" fontWeight="bold"> {recipe.title} </Text> <Text>{recipe.description}</Text> </Container> ))} </Flex> </Flex> ); }; export default RecipesList;
方案2:Thunk内直接调用Apollo Client实例的query方法
如果一定要保留Thunk的写法,就不要用Hook形式的查询,改为注入Apollo Client实例直接调用client.query方法:
// 修改fetchRecipes实现 import { ApolloClient, gql } from '@apollo/client'; // 替换为你实际的查询语句 const RECIPES_QUERY = gql` query GetRecipes { recipes { errors recipes { id title description } } } `; export const fetchRecipes = (client: ApolloClient<any>) => { return async (dispatch: Dispatch) => { dispatch(fetchRecipesPending()); try { const { data } = await client.query({ query: RECIPES_QUERY }); const errors = data?.recipes.errors; const recipes = data?.recipes.recipes; if (errors?.length) { dispatch(fetchRecipesFailure(errors)); } else { dispatch(fetchRecipesSuccess(recipes)); } } catch (e) { dispatch(fetchRecipesFailure([(e as Error).message])); } }; };
组件调用时传入Apollo Client实例即可:
import { useApolloClient } from '@apollo/client'; const RecipesList: React.FC<RecipesListProps> = () => { // ...其他原有代码 const client = useApolloClient(); useEffect(() => { dispatch(fetchRecipes(client)); }, [dispatch, client]); // ...其他原有代码 }
内容的提问来源于stack exchange,提问作者chai
相关产品推荐
相关产品推荐

