You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 03:45:05