来自 JSON 的变量赋值未同时设置所有值(React)并为一个变量返回 undefined

Variable assignment from JSON not setting all the values at the same time (React) and returning undefined for one variable

我正在对 API 进行异步调用,然后在 useEffect 函数中设置返回到我的状态变量的数据。在该函数之外,我然后将值解构为变量并将它们呈现到屏幕上。

问题是 instructions 变量在渲染时仍然未定义,我有点困惑为什么其他人渲染正常。

(指令变量也是一个对象数组)

顶级组件

const RecipePage = () => {

const [isLoading, setIsLoading] = useState(true);
const [data, setData] = useState([]);
const params = useParams();
const recipeSlug = params.recipeSlug;
const recipeID = params.id;

useEffect(() => {

    const options = {
        method: 'GET',
        url: 'https://tasty.p.rapidapi.com/recipes/detail',
        params: { id: recipeID },
        headers: {
            'x-rapidapi-host': 'tasty.p.rapidapi.com',
            'x-rapidapi-key': process.env.REACT_APP_RAPID_API_KEY
        }
    };

    const fetchData = async () => {
        const recipeInstructions = await axios.request(options);
        setData(recipeInstructions.data);
        console.log(recipeInstructions.data);
        console.log(data);
    }
    fetchData().catch(console.error);

}, [recipeID]);

const { name, country, slug, instructions, beauty_url } = data;

console.log(data.instructions);
console.log(name);

return (
    
    <>
        <header className="topBar">
            <Link to={`/`} className='normal'>
                <div className='title'>
                    <SectionTitle titleText="Test Test" />
                    <SectionTitle titleText="Cooking Club" />
                </div>
            </Link>
        </header>
        <div>
            <SectionTitle titleText={name} />
            <div className="image">
                <img src={beauty_url} alt="recipe" />
            </div>
        </div>
        <section>
            <p>Serves {data.num_servings} </p>
            <p>Prep Time {data.num_servings} </p>
        </section>
        <section>
            <h3>Ingredients</h3>
        </section>
        <div>{country}</div>
        <div>{slug}</div>
        <div>slug: {recipeSlug}</div>
        <RecipeInstructions instructions={instructions} />         
    </>
)

}

说明组件

const RecipeInstructions = ({instructions}) => {
console.log(instructions);
const instructionList = instructions.map((instruction, i) =>{
    return <li key={instruction.id} id ={instruction.id}>{instruction.display_text}</li>
});

return(
    <ul className="instructionList">
       {instructionList}   
    </ul>
)

}

导出默认配方说明;

JSON 形状

{
"country": "US",
"name": "Tomato And Ginger Pressure Cooker Short Ribs",
"id": 8115,  
"original_video_url": "https://s3.amazonaws.com/video-api-prod/assets/394e5b5d88d44a8abff0bf146ccda0b8/Campbells_TomatoGingerRibs_BFV88856_SQHero.mp4",
"cook_time_minutes": null,
"description": "",
"slug": "tomato-and-ginger-pressure-cooker-short-ribs",
"instructions": [
    {
        "appliance": null,
        "id": 70698,
        "display_text": "Season the short ribs all over with salt and pepper.",
        "position": 1,
        "start_time": 4166,
        "end_time": 14133,
        "temperature": null
    },
    {
        "display_text": "Turn the pressure cooker on to the Sauté setting. Add the olive oil to the pot and heat until shimmering. Working in batches, add the short ribs and sear on all sides until browned, 2–3 minutes per side. Once all of the short ribs have been seared, return to the pot, nestling to fit in an even layer.",
        "position": 2,
        "start_time": 0,
        "end_time": 0,
        "temperature": null,
        "appliance": null,
        "id": 70699
    },
    {
        "start_time": 16500,
        "end_time": 30333,
        "temperature": null,
        "appliance": null,
        "id": 70700,
        "display_text": "In a small bowl, whisk together the soy sauce, beef broth, sesame oil, red pepper flakes, brown sugar, garlic, and ginger.",
        "position": 3
    },
    {
        "end_time": 61500,
        "temperature": null,
        "appliance": null,
        "id": 70701,
        "display_text": "Pour the sauce and Campbell’s® Tomato Soup into the pot over the short ribs. Secure the lid and turn off the Sauté setting, then set to pressure cook on high for 1 hour.",
        "position": 4,
        "start_time": 50500
    },
    {
        "end_time": 0,
        "temperature": null,
        "appliance": null,
        "id": 70702,
        "display_text": "In a small bowl, whisk together the cornstarch, water, and mirin until smooth.",
        "position": 5,
        "start_time": 0
    },
    {
        "end_time": 47000,
        "temperature": null,
        "appliance": null,
        "id": 70703,
        "display_text": "Release the pressure valve until all the steam has been released. Transfer the short ribs to a bowl and skim off any fat from the surface of the braising liquid. Turn on the Sauté setting and stir the cornstarch slurry into the braising liquid. Bring to a simmer and cook until the sauce has thickened, about 3 minutes. Return the short ribs to the pressure cooker and turn to coat in the sauce.",
        "position": 6,
        "start_time": 35000
    },
    {
        "position": 7,
        "start_time": 93000,
        "end_time": 98333,
        "temperature": null,
        "appliance": null,
        "id": 70704,
        "display_text": "Serve the short ribs over white rice and garnish with sliced scallions and sesame seeds."
    },
    {
        "position": 8,
        "start_time": 100000,
        "end_time": 103000,
        "temperature": null,
        "appliance": null,
        "id": 70705,
        "display_text": "Enjoy!"
    }
], 
"beauty_url": "https://img.buzzfeed.com/video-api-prod/assets/dc16dd0e35f2480ba1d68badac0eec99/InstantPotShortRibs_Pinterest.jpg",
"num_servings": 4,
"facebook_posts": []

}

useEffect 挂钩在第一次初始渲染后首先执行,而且请求是异步的,这意味着在接收到网络响应之前至少会有一个渲染,并且数据对象被填充为指令值。

这是预期的行为,您可以选择延迟渲染部分组件直到获取数据,或者提供一些加载状态。

您已经被重定向到专用页面,因此您是否拥有 recipeId 无需在 useEffect 中使用依赖项数组 你可以使用条件渲染,因为你的调用是异步的,你可以显示一个 gif 加载,例如一旦完成你可以显示信息。

const RecipePage = () => {

const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState([]);
const params = useParams();
const recipeSlug = params.recipeSlug;
const recipeID = params.id;

useEffect(() => {
   setIsLoading(true)
    const options = {
        method: 'GET',
        url: 'https://tasty.p.rapidapi.com/recipes/detail',
        params: { id: recipeID },
        headers: {
            'x-rapidapi-host': 'tasty.p.rapidapi.com',
            'x-rapidapi-key': process.env.REACT_APP_RAPID_API_KEY
        }
    };

    const fetchData = async () => {
        const recipeInstructions = await axios.request(options);
        setData(recipeInstructions.data);
        console.log(recipeInstructions.data);
        console.log(data);
    }
    fetchData().catch(console.error);
    setIsLoading(false)
}, []);

const { name, country, slug, instructions, beauty_url } = data;

console.log(data.instructions);
console.log(name);
if(isLoading) {
return (
<div> here dispolay a gif loading </div>
)};
return (
    
    <>
        <header className="topBar">
            <Link to={`/`} className='normal'>
                <div className='title'>
                    <SectionTitle titleText="Test Test" />
                    <SectionTitle titleText="Cooking Club" />
                </div>
            </Link>
        </header>
        <div>
            <SectionTitle titleText={name} />
            <div className="image">
                <img src={beauty_url} alt="recipe" />
            </div>
        </div>
        <section>
            <p>Serves {data.num_servings} </p>
            <p>Prep Time {data.num_servings} </p>
        </section>
        <section>
            <h3>Ingredients</h3>
        </section>
        <div>{country}</div>
        <div>{slug}</div>
        <div>slug: {recipeSlug}</div>
        <RecipeInstructions instructions={instructions} />         
    </>
)