reactjs - რატომ არ ჩანს ჩემი მონაცემები, რომელიც მოდის apollo სერვერიდან, როცა გვერდს ახლიდან ვატვირთავ?
მე ვაშენებ მარტივ აპლიკაციას, რომელიც იყენებს 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>
);
};
მეც ვეცდები ამის საუკეთესო გაკეთებას.
პირველად ვიღებ id პარამეტრს url-იდან. ამ id-თ, ვიღებ შესაბამის რეცეპტს, მის ინგრედიენტებს და ეტაპებს.
შემდეგ მე რეცეპტის სათაურს, რეცეპტის აღწერას, რეცეპტის ინგრედიენტებს და ნაბიჯებს ვამატებ ოთხ ცვლადში: originalTitle, originalDescription, originalIngredients და originalSteps, შესაბამისად.
შემდეგ მე ვაყენებ ზოგიერთ სტატუსს useState()-ით, რომელსაც ვუწოდებ formValues. ეს ასე გამოიყურება:
{
title: originalTitle,
description: originalDescription,
ingredients: originalIngredients,
steps: originalSteps,
}
ბოლო მხრივ, მე ვამარჯვებ form-ს, რომელიც შეიცავს 4 კომპონენტს:
პირველი კომპონენტი არის SimpleFormControl და ის სათაურისთვის არის. დააკვირდით, როგორ დავაყენე ამ კომპონენტის value პროპი formValues.title-ზე.
მეორე კომპონენტი ასევე არის SimpleFormControl და ის განკუთვნილია აღწერისთვის, რომელსაც აქვს value თვისება, რომელიც დაყენებულია formValues.description.
მესამე კომპონენტი არის MultiFormControl და ის განკუთვნილია ინგრედიენტებისთვის. ეს კომპონენტი შეიცავს თავის value მახასიათებელს, რომელიც არის formValues.ingredients.
მეოთხე კომპონენტი ასევე არის MultiFormControl და ეს ნაბიჯებისთვის არის. ამ კომპონენტს აქვს თავისი 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 ]);





