为啥我在刷新页面时没有显示来自 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
,我抓取了相应的食谱、它的成分和它的步骤。
接下来我把菜谱的标题、菜谱的描述、菜谱的成分和步骤分别放入四个变量:originalTitle
、originalDescription
、originalIngredients
和originalSteps
。
接下来我用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 页面后,originalTitle
、originalDescripion
、originalIngredients
和 originalSteps
要么是空字符串,要么是空数组。这是由于每个变量都附加了||
运算符。
提前感谢您的任何反馈和帮助。 如果您需要什么,请告诉我。
【问题讨论】:
【参考方案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 服务器的数据?的主要内容,如果未能解决你的问题,请参考以下文章
Apollo 查询在页面刷新时运行,但不使用反应路由器导航?