React TypeScript 转换问题:渲染数据时出现了问题。

4

这是我的第一篇帖子!

我正在使用GraphQL从Contentful获取产品列表,并通过筛选查找与用户点击匹配的产品。在将组件转换为TypeScript之前,一切都正常。我对TS有一些模糊的了解,而且几个月前才开始使用。

当我记录产品时,我会收到具有正确过滤数据的对象,但是当我尝试访问名称为“imagesCollection”的键时,我会收到以下TS错误消息:

属性“imagesCollection”在类型“{}”上不存在。ts(2339)

如果我添加 // @ts-ignore ,则产品图像将无问题地呈现。这是某种竞争条件吗?

Typescript错误截图

非常感谢任何帮助,因为我已经花了很多时间尝试自己解决它。谢谢!

组件:

const Product = (props: any) => {
  // Custom HOOK for fetching Contentful Data
  const slug = props.match.params.slug;
  const { product } = useContentful(query, slug);
  console.log(product);
  const [jacketColor, setJacketColor] = useState(null);
  const [color, setColor] = useState(null);
  useEffect(() => {
    setJacketColor(
      product.imagesCollection && product.imagesCollection.items[1].url
    );
    setColor(
      // @ts-ignore
      product.imagesCollection && product.imagesCollection.items[1].description
    );
  }, [product]);

  const changeColor = (jacketColor: string) => {
    // @ts-ignore
    const filterHero = product.imagesCollection.items
      .filter((heroJacket: { title: string }) =>
        heroJacket.title.includes("hero")
      )
      .filter(
        (item: { description: string }) => item.description === jacketColor
      );

    setJacketColor(filterHero[0].url);
    setColor(filterHero[0].description);
  };

  const thumbnailImages = () => {
    return (
      // @ts-ignore
      product.imagesCollection &&
      // @ts-ignore
      product.imagesCollection.items
        .filter((jacket: queryProps) => jacket.title.includes("thumbnail"))
        .map((jacket: { url: string; description: string }, index: number) => {
          return (
            <img
              className={styles.thumbnailImages}
              key={`products-${index}`}
              src={jacket.url}
              // @ts-ignore
              alt={product.title}
              onClick={() => changeColor(jacket.description)}
            />
          );
        })
    );
  };

  return (
    <>
      <Header />
      {!product ? (
        <Loading />
      ) : (
        <div className={styles.productsContainer}>
          {/*  @ts-ignore */}
          <p className={styles.title}>{product && product.title}</p>
          {documentToReactComponents(
            // @ts-ignore
            product.description && product.description.json,
            RICHTEXT_OPTIONS
          )}
          {/*  @ts-ignore */}
          <p className={styles.price}>{`$${product && product.price}`}</p>
          {
            <img
              className={styles.productImage}
              //  @ts-ignore
              src={jacketColor}
              //  @ts-ignore
              alt={product && product.title}
            />
          }
          <p className={styles.selectColor}>{`Select a colour: ${color}`}</p>
          <div className={styles.thumbnailImagesWrapper}>
            {thumbnailImages()}
          </div>
        </div>
      )}
    </>
  );
};

export default Product;

获取数据的自定义钩子:
function useContentful(query: string, slug: string | null) {  
  const [products, setProducts] = useState([]);
  const [product, setProduct] = useState({});

  useEffect(() => {
    fetch(graphqlURL, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${REACT_APP_CDA_TOKEN}`,
      },
      body: JSON.stringify({ query }),
    })
      .then((res) => res.json())
      .then(({ data }) => {     
        if (slug) {
          data.arcteryxCollection.items.filter((item: queryProps) => {
            return item.slug === slug && setProduct(item);
          });           
        } else {
          setProducts(data.arcteryxCollection.items);
        }
      })
      .catch((err) => console.error(err));
  }, [query, slug]);
  
  return { product, products };
}

GraphQL 查询结构:

export const query = ` 
query {
  arcteryxCollection {
    items {
      title
      slug
      description {
        json
      }
      price
      imagesCollection {
        items {
          url
          title
          description
        }
        }
      }
    }
  }
`;

我正在导入的 TS 接口:

export interface queryProps {
  title: string;
  slug: string;
  price: number;
  description: {
    json: {
      content: {
        value: string;
      }[];
    };
  };
  imagesCollection: {
    items: {
      url: string;
      title: string;
      description: string;
    }[];
  };
}

仓库链接: https://github.com/danlubbers/arcteryx-graphql


1
最好在问题本身中包含完整的错误文本,而不是链接到外部图像。通常,您只想链接到完全辅助信息,任何相关的内容都应包含在帖子本身中。请参见:https://meta.stackoverflow.com/questions/285551/why-not-upload-images-of-code-errors-when-asking-a-question/285557#285557 否则,这是一个不错的第一个问题 :) - Lyle Underwood
2个回答

0

这是因为您正在使用空对象初始化产品状态。

const [product, setProduct] = useState({});

这使得 TypeScript 推断出您的状态类型是一个空对象。因此,它上面没有 imagesCollection

您可以为其指定类型,或者使用具有所需属性的对象进行初始化。

以下是为产品添加类型的示例。您可以添加需要从 Graph QL 响应中使用的其他属性。

type ImageCollectionItem = {
    url: string;
    title: string;
    description: string;
}

type ProductState = {
    imagesCollection?: {
        items: ImageCollectionItem[]
    }
}

//...

const [product, setProduct] = useState<ProductState>({});

感谢您抽出时间阅读并评论。我无法通过提供的代码找到解决方案,但我确实理解您所写的一切。可能有比可见的更深层次的东西。一旦我找到了解决方案,我会发布我的解决方案! - dlubbers

0
这是我找到的解决方案,最终对我起作用了。在 use-contentful.ts 钩子中,我更改了过滤响应以将数据存储为数组而不是对象的方式。
如下所示:
const [product, setProduct] = useState<queryProps[]>([]);

          let filteredProduct = data.arcteryxCollection.items.filter((item: queryProps) => {                        
            return item.slug === slug;
          });          
          setProduct(filteredProduct);               

这使我能够访问产品组件中的数据并以此方式呈现:
 const { product } = useContentful(query, slug);
 const productObj = product[0];

 const [jacketColor, setJacketColor] = useState("");

 setJacketColor(productObj && productObj.imagesCollection.items[1].url);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接