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

如何优化基于LocalStorage的Apollo查询:避免空变量发起请求

优化最近浏览商品组件的查询逻辑,避免无效请求

我通过LocalStorage存储商品ID实现最近浏览商品组件,当前用useState定义状态存储ID,组件挂载时通过useEffect从LocalStorage获取数据更新状态,再将其作为Apollo Client的useQuery变量传入WP-GraphQL查询。但发现ID为空时会发起无效查询,希望找到更优的实现方式。

当前组件代码

import { useQuery } from "@apollo/client";
import { getRecentlyViewedProductArr } from "@gb-utils/product/product"
import { RECENTLY_VIEWED } from "@gb-utils/queries/product";
import { useEffect, useState } from "react";


export default function RecentlyViewed() {
    
    const [recentIds, setRecentIds] = useState([])
    
    const { loading, error, data } = useQuery(RECENTLY_VIEWED, {
        variables: { ids: recentIds }
    })
    
    useEffect(() => {
        setRecentIds(getRecentlyViewedProductArr())
    }, []);
    
    if (loading) {
        return 'Loading';
    }
    
    if (error) {
        return error.message
    }
    
    return (
        <div>{JSON.stringify(data)}</div>
    )
}

工具函数代码(helper.js)

export const addRecentlyViewedProduct = (productId) => {

    let recent = getRecentlyViewedProduct()

    if (isEmpty(recent)) {
        // If recently viewed is empty then we just save the product id
        saveRecentlyViewedProduct(productId)

        return getRecentlyViewedProduct()
    }

    // if not empty then we check if product id exist and remove
    if (recent.includes(productId)) {
        recent = recent.replace(productId, '')
    }
    // Since we remove the product then we Add the product id again
    recent = "".concat(productId, '|', recent)
    recent = recent.replace('||', '|');
    recent = recent.startsWith('|') ? recent.substring(1) : recent
    recent = recent.endsWith('|') ? recent.substring(0, recent.length - 1) : recent;

    const limit = 5;
    let recentProductIds = recent.split('|')
    if (recentProductIds.length > limit) {
        recentProductIds = recentProductIds.slice(0, limit)
    }

    saveRecentlyViewedProduct(recentProductIds.join('|'))
    return getRecentlyViewedProduct()
}

export const saveRecentlyViewedProduct = (value) => {
    return localStorage.setItem('woo-next-recent-product', value);
}

export const getRecentlyViewedProduct = () => {

    const recentProductsStr = localStorage.getItem('woo-next-recent-product')
    return recentProductsStr
}

export const getRecentlyViewedProductArr = () => {
    const recentProductsStr = getRecentlyViewedProduct()
    if (isEmpty(recentProductsStr)) {
        return ''
    }

    let recentProductIds = recentProductsStr.split('|')
    return Array.from(recentProductIds.slice(1), Number); // get the 2nd to the last element
}

优化方案

1. 初始化状态直接读取LocalStorage

组件挂载时,原代码useState初始值是空数组,会导致useQuery先发起一次带空数组的无效请求,再等待useEffect更新状态。直接在useState初始化时调用getRecentlyViewedProductArr(),让组件一开始就持有正确的ID数据,避免首次无效请求。

2. 控制查询执行时机,跳过无效请求

在useQuery配置中添加skip选项,当recentIds为空数组时跳过查询,彻底避免向服务器发送无效请求。

3. 修复工具函数的类型不一致问题

原getRecentlyViewedProductArr函数在无数据时返回空字符串,和useState的数组类型不匹配,容易引发类型错误。修改为返回空数组,保持类型统一。

4. 监听LocalStorage变化(可选)

如果其他页面或标签页会修改最近浏览记录,可以监听storage事件,同步更新组件状态,提升跨标签页的用户体验。

修改后的代码

组件代码

import { useQuery } from "@apollo/client";
import { getRecentlyViewedProductArr } from "@gb-utils/product/product"
import { RECENTLY_VIEWED } from "@gb-utils/queries/product";
import { useEffect, useState } from "react";


export default function RecentlyViewed() {
    // 初始化直接读取LocalStorage数据,避免初始空数组
    const [recentIds, setRecentIds] = useState(getRecentlyViewedProductArr())
    
    const { loading, error, data } = useQuery(RECENTLY_VIEWED, {
        variables: { ids: recentIds },
        // 当ID数组为空时跳过查询
        skip: recentIds.length === 0
    })
    
    // 监听LocalStorage变化,同步更新状态(可选)
    useEffect(() => {
        const handleStorageUpdate = () => {
            setRecentIds(getRecentlyViewedProductArr())
        };
        window.addEventListener('storage', handleStorageUpdate);
        return () => window.removeEventListener('storage', handleStorageUpdate);
    }, []);
    
    // 无最近浏览记录时展示提示
    if (recentIds.length === 0) {
        return <div>暂无最近浏览商品</div>;
    }
    
    if (loading) {
        return 'Loading';
    }
    
    if (error) {
        return <div>加载失败:{error.message}</div>
    }
    
    return (
        <div>{JSON.stringify(data)}</div>
    )
}

工具函数修改(getRecentlyViewedProductArr)

export const getRecentlyViewedProductArr = () => {
    const recentProductsStr = getRecentlyViewedProduct()
    // 处理空值,返回空数组保持类型统一
    if (!recentProductsStr || recentProductsStr.trim() === '') {
        return []
    }

    let recentProductIds = recentProductsStr.split('|')
    // 保留原业务逻辑:取第2个到最后一个元素并转数字
    return recentProductIds.slice(1).map(Number);
}

说明

  • 初始化直接读取LocalStorage:减少一次不必要的查询,组件渲染时直接使用正确的初始状态。
  • skip选项:彻底避免空ID数组的无效请求,减轻服务器压力。
  • 类型统一:避免因返回值类型不一致导致的渲染错误或逻辑问题。
  • storage事件监听:确保跨标签页操作时,组件能实时同步最近浏览记录。

内容的提问来源于stack exchange,提问作者jameshwart lopez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 21:10:37