从理智CMS获取数据时,React状态不确定

发布于 2025-01-31 20:44:04 字数 3856 浏览 2 评论 0原文

我最近关注了一个有关将CMS集成到您的网站的教程。教程使用了理智的CMS,使过程非常直观。完成教程后,我准备在自己的项目中使用它。

但是,当我尝试使用使用效果挂钩获取数据时,我会发现一个错误:无法读取未定义的属性。我知道这是因为获取数据已完成异步。但是我无法缠绕我的头是与教程完全相同的方式。他没有使用任何状态来加载或取消。所以我的问题是我做什么与教程不同,该如何解决?

我真的不想使用加载状态,因为那看起来并不好...

这是我从API中收到的JSON对象:

[{…}]
0:
buttonlabel: "Start learning"
description: "Ranging from beginner to pro level tricks. Wanna know the best way to learn a trick? You can search for it down below and find a tutorial from one of our trainers as well as a detailed explanation. Still stuck? Come ask us at a Westsite training moment."
intro: "Welcome to the Westsite trick progression guide. Here you can find a collection of all the wakeboarding tricks you can think of.  "
_createdAt: "2022-05-24T16:26:13Z"
_id: "a4f8cf02-4b86-44d5-a63d-c95a3a7d3293"
_rev: "QYLgvM20Eo53w3noOOj0MB"
_type: "hero"
_updatedAt: "2022-05-24T17:29:10Z"

这是教程组件:

import React, { useState, useEffect } from "react";
import { motion } from "framer-motion";
import { urlFor, client } from "../../client";
import { AppWrap, MotionWrap } from "../../wrapper";

import "./About.scss";

const About = () => {
  const [abouts, setAbouts] = useState([]);

  useEffect(() => {
    const query = '*[_type == "abouts"]';

    client.fetch(query).then((data) => setAbouts(data));
  }, []);

  return (
    <div>
      <h2 className="head-text">
        I know that
        <span> Good Design </span>
        <br />
        means
        <span> Good Business</span>
      </h2>

      <div className="app__profiles">
        {abouts.map((about, index) => {
          return (
            <motion.div
              whileInView={{ opacity: 1 }}
              whileHover={{ scale: 1.1 }}
              transition={{ duration: 0.5, type: "tween" }}
              className="app__profile-item"
              key={about.title + index}
            >
              <img src={urlFor(about.imgUrl)} alt={about.title} />
              <h2 className="bold-text" style={{ marginTop: 20 }}>
                {about.title}
              </h2>
              <p className="p-text" style={{ marginTop: 10 }}>
                {about.description}
              </p>
            </motion.div>
          );
        })}
      </div>
    </div>
  );
};

export default AppWrap(
  MotionWrap(About, "app__about"),
  "about",
  "app__whitebg"
);

这是我的:

import React, { useState, useEffect } from "react";
import { motion } from "framer-motion";
import { BiRightArrowAlt } from "react-icons/bi";

import { client } from "../../client";
import "./Hero.scss";

const Hero = () => {
  const [heroContent, setHeroContent] = useState([]);

  useEffect(() => {
    const query = '*[_type == "hero"]';

    client.fetch(query).then((data) => setHeroContent(data));
  }, []);

  const content = heroContent[0];

  return (
    <div className="app__hero">
      <motion.div
        className="app__hero-content-container"
        whileInView={{ opacity: [0, 1], x: [500, 0] }}
        transition={{ duration: 1, ease: "easeOut" }}
      >
        <div className="app__hero-content">
          <h2 className="heading-text">
            Learn
            <span className="highlighted"> wakeboarding </span>
            the right way
          </h2>
          <p className="p-text">{content.intro}</p>
          <p className="p-text">{content.description}</p>
          <button className="primary-btn p-text app__flex">
            {content.buttonlabel}
            <BiRightArrowAlt />
          </button>
        </div>
      </motion.div>
    </div>
  );
};

export default Hero;

I followed a tutorial recently about integrating a cms into your website. The tutorial used sanity cms which made the process very intuitive. Once I was done with the tutorial I was ready to use it in my own projects.

however when I try to fetch data with the useEffect hook I get an error: Cannot read properties of undefined. I know this is because fetching data is done async. But the thing I can't wrap my head around is I did it the exact same way as the tutorial. He didn't use any state for loading or isFetched. So my question is what did I do different than the tutorial and how should I solve it?

I don't really want to use a loading state because that doesn't really look that good...

This is the JSON object I receive from the api:

[{…}]
0:
buttonlabel: "Start learning"
description: "Ranging from beginner to pro level tricks. Wanna know the best way to learn a trick? You can search for it down below and find a tutorial from one of our trainers as well as a detailed explanation. Still stuck? Come ask us at a Westsite training moment."
intro: "Welcome to the Westsite trick progression guide. Here you can find a collection of all the wakeboarding tricks you can think of.  "
_createdAt: "2022-05-24T16:26:13Z"
_id: "a4f8cf02-4b86-44d5-a63d-c95a3a7d3293"
_rev: "QYLgvM20Eo53w3noOOj0MB"
_type: "hero"
_updatedAt: "2022-05-24T17:29:10Z"

This is the tutorial component:

import React, { useState, useEffect } from "react";
import { motion } from "framer-motion";
import { urlFor, client } from "../../client";
import { AppWrap, MotionWrap } from "../../wrapper";

import "./About.scss";

const About = () => {
  const [abouts, setAbouts] = useState([]);

  useEffect(() => {
    const query = '*[_type == "abouts"]';

    client.fetch(query).then((data) => setAbouts(data));
  }, []);

  return (
    <div>
      <h2 className="head-text">
        I know that
        <span> Good Design </span>
        <br />
        means
        <span> Good Business</span>
      </h2>

      <div className="app__profiles">
        {abouts.map((about, index) => {
          return (
            <motion.div
              whileInView={{ opacity: 1 }}
              whileHover={{ scale: 1.1 }}
              transition={{ duration: 0.5, type: "tween" }}
              className="app__profile-item"
              key={about.title + index}
            >
              <img src={urlFor(about.imgUrl)} alt={about.title} />
              <h2 className="bold-text" style={{ marginTop: 20 }}>
                {about.title}
              </h2>
              <p className="p-text" style={{ marginTop: 10 }}>
                {about.description}
              </p>
            </motion.div>
          );
        })}
      </div>
    </div>
  );
};

export default AppWrap(
  MotionWrap(About, "app__about"),
  "about",
  "app__whitebg"
);

And this is mine:

import React, { useState, useEffect } from "react";
import { motion } from "framer-motion";
import { BiRightArrowAlt } from "react-icons/bi";

import { client } from "../../client";
import "./Hero.scss";

const Hero = () => {
  const [heroContent, setHeroContent] = useState([]);

  useEffect(() => {
    const query = '*[_type == "hero"]';

    client.fetch(query).then((data) => setHeroContent(data));
  }, []);

  const content = heroContent[0];

  return (
    <div className="app__hero">
      <motion.div
        className="app__hero-content-container"
        whileInView={{ opacity: [0, 1], x: [500, 0] }}
        transition={{ duration: 1, ease: "easeOut" }}
      >
        <div className="app__hero-content">
          <h2 className="heading-text">
            Learn
            <span className="highlighted"> wakeboarding </span>
            the right way
          </h2>
          <p className="p-text">{content.intro}</p>
          <p className="p-text">{content.description}</p>
          <button className="primary-btn p-text app__flex">
            {content.buttonlabel}
            <BiRightArrowAlt />
          </button>
        </div>
      </motion.div>
    </div>
  );
};

export default Hero;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

紙鸢 2025-02-07 20:44:04

在将数据应用于

const content = heroContent[0];

初始渲染上的状态异步之前,该行将引起问题,Herocontent是一个空数组,因此content> content将不确定直到加载数据为止。几个选项 -
1-渲染某种加载状态,直到Herocontent已填充-2-

if (!heroContent.length) return <LoadingSpinner />

将试图使用content与Guard子句一起使用的部分

{content && (
  <p className="p-text">{content.intro}</p>
  <p className="p-text">{content.description}</p>
  <button className="primary-btn p-text app__flex">
    {content.buttonlabel}
    <BiRightArrowAlt />
  </button>
)}

This line will cause issues before the data is applied to state asynchronously

const content = heroContent[0];

On the initial render, heroContent is an empty array, so content will be undefined until your data is loaded. A couple options -
1 - render some sort of loading state until heroContent has been populated -

if (!heroContent.length) return <LoadingSpinner />

2 - wrap the portion that is trying to use content with a guard clause

{content && (
  <p className="p-text">{content.intro}</p>
  <p className="p-text">{content.description}</p>
  <button className="primary-btn p-text app__flex">
    {content.buttonlabel}
    <BiRightArrowAlt />
  </button>
)}
尸血腥色 2025-02-07 20:44:04

当您尝试从content未定义时尝试访问属性时,问题就到了。如果您不想显示任何加载指示器,则在未定义内容时,我会随时显示一些后备。例如

<p className="p-text">{content.intro}</p>

您可以使用:

<p className="p-text">{content?.intro ?? '-'}</p>

或类似的东西。

The issue comes when you try to access properties from content when it's undefined. If you don't want to show any loading indicator, I would go with showing some fallback for when content is not defined. e.g.

Instead of:

<p className="p-text">{content.intro}</p>

You could go with:

<p className="p-text">{content?.intro ?? '-'}</p>

or something like that.

勿忘初心 2025-02-07 20:44:04

问题是,您在不同级别上分解了状态,从而在状态更改中造成问题。所以,你必须这样做
您将状态称为地图功能,或使用Specfic索引0。

    import React, { useState, useEffect } from "react";
    import { motion } from "framer-motion";
    import { BiRightArrowAlt } from "react-icons/bi";
    
    import { client } from "../../client";
    import "./Hero.scss";
    
    const Hero = () => {
      const [heroContent, setHeroContent] = useState([]);
    
      useEffect(() => {
        const query = '*[_type == "hero"]';
    // this is giving response as array
        client.fetch(query).then((data) => setHeroContent(data));
      }, []);
    
      return (
        <div className="app__hero">
          {heroContent.map((content,index)=>
          <motion.div
            className="app__hero-content-container"
            whileInView={{ opacity: [0, 1], x: [500, 0] }}
            transition={{ duration: 1, ease: "easeOut" }}
            key={index}
          >

            <div className="app__hero-content">
              <h2 className="heading-text">
                Learn
                <span className="highlighted"> wakeboarding </span>
                the right way
              </h2>
              <p className="p-text">{content.intro}</p>
              <p className="p-text">{content.description}</p>
              <button className="primary-btn p-text app__flex">
                {content.buttonlabel}
                <BiRightArrowAlt />
              </button>
            </div>

          </motion.div>}
        </div>
      );
    
    };
    
    export default Hero;

Problem is that you breakdown your state on different level that create problem with state changes. So, you have to do this
Either you call state as map function or save your state with specfic index 0.

    import React, { useState, useEffect } from "react";
    import { motion } from "framer-motion";
    import { BiRightArrowAlt } from "react-icons/bi";
    
    import { client } from "../../client";
    import "./Hero.scss";
    
    const Hero = () => {
      const [heroContent, setHeroContent] = useState([]);
    
      useEffect(() => {
        const query = '*[_type == "hero"]';
    // this is giving response as array
        client.fetch(query).then((data) => setHeroContent(data));
      }, []);
    
      return (
        <div className="app__hero">
          {heroContent.map((content,index)=>
          <motion.div
            className="app__hero-content-container"
            whileInView={{ opacity: [0, 1], x: [500, 0] }}
            transition={{ duration: 1, ease: "easeOut" }}
            key={index}
          >

            <div className="app__hero-content">
              <h2 className="heading-text">
                Learn
                <span className="highlighted"> wakeboarding </span>
                the right way
              </h2>
              <p className="p-text">{content.intro}</p>
              <p className="p-text">{content.description}</p>
              <button className="primary-btn p-text app__flex">
                {content.buttonlabel}
                <BiRightArrowAlt />
              </button>
            </div>

          </motion.div>}
        </div>
      );
    
    };
    
    export default Hero;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文