You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Shopify JS Buy SDK刷新时读取collections[5].products报错求助

问题:Shopify Buy SDK 获取集合产品时刷新报错

我正在用Shopify Buy SDK渲染集合中的产品,控制台能正常打印collections[5],展开后也能看到其products属性对应的GraphModel,但刷新页面时会抛出错误:cannot read properties of undefined (reading 'products')。

可正常运行的代码

const Home = () => {
  const { fetchAllProducts, fetchAllCollections, products, collections } = useContext(ShopContext);

  useEffect(() => {
    fetchAllCollections();
  }, [fetchAllCollections]);

  console.log(collections[5]);
}

刷新时报错的代码

const Home = () => {
  const { fetchAllProducts, fetchAllCollections, products, collections } = useContext(ShopContext);

  useEffect(() => {
    fetchAllCollections();
  }, [fetchAllCollections]);

  console.log(collections[5].products);
}

ShopContext 代码

import React, { Component } from "react";
import Client from "shopify-buy";

const ShopContext = React.createContext();

const client = Client.buildClient({
  domain: process.env.REACT_APP_SHOPIFY_DOMAIN,
  storefrontAccessToken: process.env.REACT_APP_SHOPIFY_API,
});

export class shopProvider extends Component {
  state = {
    collection: {},
    collections: [],
    product: {},
    products: [],
    checkout: {},
    isCartOpen: false,
    isMenuOpen: false,
  };

  componentDidMount() {
    if (localStorage.checkout_id) {
      this.fetchCheckout(localStorage.checkout_id);
    } else {
      this.createCheckout();
    }
  }

  createCheckout = async () => {
    const checkout = await client.checkout.create();
    localStorage.setItem("checkout_id", checkout.id);
    this.setState({ checkout: checkout });
  };

  fetchCheckout = (checkoutId) => {
    client.checkout.fetch(checkoutId).then((checkout) => {
      this.setState({ checkout: checkout });
    });
  };

  addItemToCheckout = async (variantId, quantity) => {
    const lineItemsToAdd = [{ variantId, quantity: parseInt(quantity, 10) }];
    const checkout = await client.checkout.addLineItems(
      this.state.checkout.id,
      lineItemsToAdd
    );
    this.setState({ checkout: checkout });
    this.openCart();
  };

  removeLineItem = async (lineItemIdsToRemove) => {
    const checkout = await client.checkout.removeLineItems(
      this.state.checkout.id,
      lineItemIdsToRemove
    );
    this.setState({ checkout: checkout });
  };

  fetchAllCollections = async () => {
    const collections = await client.collection.fetchAllWithProducts();
    this.setState({ collections: collections });
  }

  fetchAllProducts = async () => {
    const products = await client.product.fetchAll();
    this.setState({ products: products });
  };

  fetchProductWithHandle = async (handle) => {
    const product = await client.product.fetchByHandle(handle);
    this.setState({ product: product });
  };

  closeCart = () => {
    this.setState({ isCartOpen: false });
  };

  openCart = () => {
    this.setState({ isCartOpen: true });
  };

  render() {
    return (
      <ShopContext.Provider
        value={{
          ...this.state,
          fetchAllProducts: this.fetchAllProducts,
          fetchProductWithHandle: this.fetchProductWithHandle,
          addItemToCheckout: this.addItemToCheckout,
          removeLineItem: this.removeLineItem,
          closeCart: this.closeCart,
          openCart: this.openCart,
          closeMenu: this.closeMenu,
          openMenu: this.openMenu,
          fetchAllCollections: this.fetchAllCollections,
        }}
      >
        {this.props.children}
      </ShopContext.Provider>
    );
  }
}

const ShopConsumer = ShopContext.Consumer;

export { ShopConsumer, ShopContext };

export default shopProvider;

原因与解决办法

核心原因

页面初始化时,collections是初始状态的空数组,此时collections[5]为undefined,直接访问undefined.products必然报错。控制台能正常显示collections[5]是因为浏览器会异步更新控制台内容——当API请求完成、collections状态更新后,控制台会展示最新值,但代码执行时还是初始的空数组状态。

解决办法

1. 使用可选链操作符安全访问

用?.语法避免访问undefined的属性:

console.log(collections[5]?.products);

2. 监听状态变化,条件执行

通过useEffect监听collections的变化,确保数据加载完成后再访问:

useEffect(() => {
  // 确保集合数量足够,再访问第6个元素(下标5)
  if (collections.length > 5) {
    console.log(collections[5].products);
  }
}, [collections]);

3. 添加加载状态判断

在shopProvider的state中新增加载状态,组件中判断加载完成后再执行逻辑:

  • 在shopProvider的state里添加isLoading: true
  • 更新fetchAllCollections方法:
fetchAllCollections = async () => {
  const collections = await client.collection.fetchAllWithProducts();
  this.setState({ collections: collections, isLoading: false });
}
  • 在Home组件中处理加载状态:
const Home = () => {
  const { fetchAllCollections, collections, isLoading } = useContext(ShopContext);

  useEffect(() => {
    fetchAllCollections();
  }, [fetchAllCollections]);

  // 加载中时展示占位内容
  if (isLoading) return <div>加载中...</div>;

  console.log(collections[5].products);
  // 后续渲染逻辑
}

内容的提问来源于stack exchange,提问作者Noah Bruce

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 17:30:21