Files
vf_react/src/contexts/IndustryContext.js

177 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/contexts/IndustryContext.js
// 行业分类数据全局上下文 - 使用API获取 + 缓存机制
import React, { createContext, useContext, useState, useEffect, useRef } from 'react';
import { industryData as staticIndustryData } from '../data/industryData';
import { industryService } from '../services/industryService';
import { logger } from '../utils/logger';
const IndustryContext = createContext();
// 缓存配置
const CACHE_KEY = 'industry_classifications_cache';
const CACHE_DURATION = 24 * 60 * 60 * 1000; // 1天24小时
/**
* useIndustry Hook
* 在任何组件中使用行业数据
*/
export const useIndustry = () => {
const context = useContext(IndustryContext);
if (!context) {
throw new Error('useIndustry must be used within IndustryProvider');
}
return context;
};
/**
* 从 localStorage 读取缓存
*/
const loadFromCache = () => {
try {
const cached = localStorage.getItem(CACHE_KEY);
if (!cached) return null;
const { data, timestamp } = JSON.parse(cached);
const now = Date.now();
// 检查缓存是否过期1天
if (now - timestamp > CACHE_DURATION) {
localStorage.removeItem(CACHE_KEY);
logger.debug('IndustryContext', '缓存已过期,清除缓存');
return null;
}
logger.debug('IndustryContext', '从缓存加载行业数据', {
count: data?.length || 0,
age: Math.round((now - timestamp) / 1000 / 60) + ' 分钟前'
});
return data;
} catch (error) {
logger.error('IndustryContext', 'loadFromCache', error);
return null;
}
};
/**
* 保存到 localStorage
*/
const saveToCache = (data) => {
try {
localStorage.setItem(CACHE_KEY, JSON.stringify({
data,
timestamp: Date.now()
}));
logger.debug('IndustryContext', '行业数据已缓存', {
count: data?.length || 0
});
} catch (error) {
logger.error('IndustryContext', 'saveToCache', error);
}
};
/**
* IndustryProvider 组件
* 提供全局行业数据管理 - 使用API获取 + 缓存机制
*/
export const IndustryProvider = ({ children }) => {
const [industryData, setIndustryData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const hasLoadedRef = useRef(false);
const isLoadingRef = useRef(false);
/**
* 加载行业数据
*/
const loadIndustryData = async () => {
// 防止重复加载(处理 StrictMode 双重调用)
if (hasLoadedRef.current || isLoadingRef.current) {
logger.debug('IndustryContext', '跳过重复加载', {
hasLoaded: hasLoadedRef.current,
isLoading: isLoadingRef.current
});
return industryData;
}
try {
isLoadingRef.current = true;
setLoading(true);
setError(null);
logger.debug('IndustryContext', '开始加载行业数据');
// 1. 先尝试从缓存加载
const cachedData = loadFromCache();
if (cachedData && cachedData.length > 0) {
setIndustryData(cachedData);
hasLoadedRef.current = true;
return cachedData;
}
// 2. 缓存不存在或过期,调用 API
logger.debug('IndustryContext', '缓存无效调用API获取数据');
const response = await industryService.getClassifications();
if (response.success && response.data && response.data.length > 0) {
setIndustryData(response.data);
saveToCache(response.data);
hasLoadedRef.current = true;
logger.debug('IndustryContext', 'API数据加载成功', {
count: response.data.length
});
return response.data;
} else {
throw new Error('API返回数据为空');
}
} catch (err) {
// 3. API 失败,回退到静态数据
logger.warn('IndustryContext', 'API加载失败使用静态数据', {
error: err.message
});
setError(err.message);
setIndustryData(staticIndustryData);
hasLoadedRef.current = true;
return staticIndustryData;
} finally {
setLoading(false);
isLoadingRef.current = false;
}
};
/**
* 刷新行业数据(清除缓存并重新加载)
*/
const refreshIndustryData = async () => {
logger.debug('IndustryContext', '刷新行业数据,清除缓存');
localStorage.removeItem(CACHE_KEY);
hasLoadedRef.current = false;
isLoadingRef.current = false;
return loadIndustryData();
};
// 组件挂载时自动加载数据
useEffect(() => {
loadIndustryData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const value = {
industryData,
loading,
error,
loadIndustryData,
refreshIndustryData
};
return (
<IndustryContext.Provider value={value}>
{children}
</IndustryContext.Provider>
);
};