为啥我在刷新页面时没有显示来自 apollo 服务器的数据?

Posted

技术标签:

【中文标题】为啥我在刷新页面时没有显示来自 apollo 服务器的数据?【英文标题】:Why is my data that is coming from apollo server not showing up when I refresh the page?为什么我在刷新页面时没有显示来自 apollo 服务器的数据? 【发布时间】:2021-12-29 20:17:14 【问题描述】:

我正在使用 React、Apollo 和 React Router 构建一个简单的应用程序。此应用程序允许您创建食谱,以及编辑和删除它们(您的标准 CRUD 网站)。

我考虑了如何呈现我的问题,我认为最好的方式是视觉化。

这里是主页(localhost:3000):

当您点击食谱的标题时,您会看到以下内容 (localhost:3000/recipe/15):

如果您单击主页上的“创建食谱”按钮,您会看到以下内容(localhost:3000/create-recipe):

如果您在主页上单击食谱上的删除按钮,您会看到以下内容 (localhost:3000):

如果你点击主页上菜谱上的编辑按钮,你看到的就是这个(localhost:3000/recipe/15/update):

此更新表单是问题的开始。如您所见,表单已填充配方的旧值。一切都会按计划进行。但是,当我刷新页面时,您会看到以下内容:

全是空白。我 67% 确信这与 React 渲染组件的方式或我查询我的 apollo 服务器的方式有关。我不完全理解 React 渲染组件的过程。

这是 UpdateRecipe 页面的代码(您可能一直在等待):

import React,  useState  from "react";
import  Button  from "@chakra-ui/react";
import 
  useUpdateRecipeMutation,
  useRecipeQuery,
  useIngredientsQuery,
  useStepsQuery,
 from "../../types/graphql";
import  useNavigate, useParams  from "react-router-dom";
import  SimpleFormControl  from "../../shared/SimpleFormControl";
import  MultiFormControl  from "../../shared/MultiFormControl";

interface UpdateRecipeProps 

export const UpdateRecipe: React.FC<UpdateRecipeProps> = () => 
  let  id: recipeId  = useParams() as  id: string ;

  const intRecipeId = parseInt(recipeId);
  const  data: recipeData  = useRecipeQuery(
    variables:  id: intRecipeId ,
  );

  const  data: ingredientsData  = useIngredientsQuery(
    variables:  recipeId: intRecipeId ,
  );

  const  data: stepsData  = useStepsQuery(
    variables:  recipeId: intRecipeId ,
  );

  const originalTitle = recipeData?.recipe.recipe?.title || "";
  const originalDescription = recipeData?.recipe.recipe?.description || "";
  const originalIngredients =
    ingredientsData?.ingredients?.ingredients?.map((ing) => ing.text) || [];
  const originalSteps = stepsData?.steps?.steps?.map((stp) => stp.text) || [];

  const [updateRecipe] = useUpdateRecipeMutation();
  const navigate = useNavigate();

  const [formValues, setFormValues] = useState(
    title: originalTitle,
    description: originalDescription,
    ingredients: originalIngredients,
    steps: originalSteps,
  );
  return (
    <form
      onSubmit=(e) => 
        e.preventDefault();
      
    >
      <SimpleFormControl
        label="Title"
        name="title"
        type="text"
        placeholder="Triple Chocolate Cake"
        value=formValues.title
        onChange=(e) => 
          setFormValues( ...formValues, title: e.target.value );
        
      />
      <SimpleFormControl
        label="Description"
        name="description"
        type="text"
        placeholder="A delicious combination of cake and chocolate that's bound to mesmerize your tastebuds!"
        value=formValues.description
        onChange=(e) => 
          setFormValues( ...formValues, description: e.target.value );
        
      />
      <MultiFormControl
        label="Ingredients"
        name="ingredients"
        type="text"
        placeholder="Eggs"
        values=formValues.ingredients
        onAdd=(newValue) => 
          setFormValues(
            ...formValues,
            ingredients: [...formValues.ingredients, newValue],
          );
        
        onDelete=(_, index) => 
          setFormValues(
            ...formValues,
            ingredients: formValues.ingredients.filter(
              (__, idx) => idx !== index
            ),
          );
        
      />
      <MultiFormControl
        ordered
        label="Steps"
        name="steps"
        type="text"
        placeholder="Pour batter into cake tray"
        color="orange.100"
        values=formValues.steps
        onAdd=(newValue) => 
          setFormValues(
            ...formValues,
            steps: [...formValues.steps, newValue],
          );
        
        onDelete=(_, index) => 
          setFormValues(
            ...formValues,
            steps: formValues.steps.filter((__, idx) => idx !== index),
          );
        
      />
      <Button type="submit">Update Recipe</Button>
    </form>
  );
;

我会尽量解释清楚。

首先我从 url 中获取 id 参数。通过这个id,我抓取了相应的食谱、它的成分和它的步骤。

接下来我把菜谱的标题、菜谱的描述、菜谱的成分和步骤分别放入四个变量:originalTitleoriginalDescriptionoriginalIngredientsoriginalSteps

接下来我用useState() 设置一些状态,称为formValues。它看起来像这样:


    title: originalTitle,
    description: originalDescription,
    ingredients: originalIngredients,
    steps: originalSteps,

最后,我返回一个 form,其中包含 4 个组件:

第一个组件是SimpleFormControl,它用于标题。请注意我是如何将此组件的 value 属性设置为 formValues.title

第二个组件也是SimpleFormControl,用于描述,它的value prop 设置为formValues.description

第三个组件是MultiFormControl,它用于配料。该组件的 value 属性设置为 formValues.ingredients

第四个组件也是aMultiFormControl,它用于步骤。该组件的 value 属性设置为 formValues.steps

如果您需要查看这两个组件的代码,请告诉我。

注意:

当我通过主页来到 UpdateRecipe 页面时,它运行良好。刷新 UpdateRecipe 页面后,originalTitleoriginalDescripionoriginalIngredientsoriginalSteps 要么是空字符串,要么是空数组。这是由于每个变量都附加了|| 运算符。

提前感谢您的任何反馈和帮助。 如果您需要什么,请告诉我。

【问题讨论】:

【参考方案1】:

问题是您正在使用一个钩子useRecipeQuery,它将在将来的某个时间返回数据,并且您的表单有第二个钩子useState,它依赖于这些数据。这意味着当 React 渲染这个组件时,useRecipeQuery 将不返回任何数据(因为它仍在获取),因此用于表单的 useState 钩子使用空数据初始化。一旦useRecipeQuery 完成获取,它将重新评估此代码,但这对您的表单的useState 挂钩没有任何影响,因为它已经初始化并在内部缓存了它的状态。它在一种情况下为您工作但在另一种情况下不适合您的原因是,在一种情况下,您的 useRecipeQuery 立即返回缓存中可用的数据,而在另一种情况下,它需要进行实际提取才能获取它。

解决办法是什么?

    假设您在首次加载此组件时没有可用于表单正确呈现的数据。所以用一些可接受的空状态初始化你的表单。 使用useEffect 连接您的钩子,这样当useRecipeQuery 完成加载其数据时,它会相应地更新您的表单状态。
  const  loading, data: recipeData  = useRecipeQuery(
    variables:  id: intRecipeId ,
  );

  const [formValues, setFormValues] = useState(
    title: "",
    description: "",
    ingredients: [],
    steps: [],
  );

  useEffect(() => 
    if (!loading && recipeData ) 
      setFormValues(
        title: recipeData?.recipe.recipe?.title,
        description: recipeData?.recipe.recipe?.description,
        ingredients: ingredientsData?.ingredients?.ingredients?.map((ing) => ing.text),
        steps:  stepsData?.steps?.steps?.map((stp) => stp.text),
      );
    
  , [loading, recipeData ]);

【讨论】:

以上是关于为啥我在刷新页面时没有显示来自 apollo 服务器的数据?的主要内容,如果未能解决你的问题,请参考以下文章

为啥vue的路由正常跳转,但页面没有变化

为啥我在页面刷新时收到网络错误? (获取请求)

JWT, 为啥需要刷新令牌?

Apollo 查询在页面刷新时运行,但不使用反应路由器导航?

为啥在 React 中的 Apollo 客户端中显示错误未显示

为啥.cshtml更改后刷新页面不更新