React类组件项目localStorage单例封装前缀异步初始化问题求解
解决方案
方案1:改造LocalStorage封装,增加异步初始化兼容
直接改造单例类,支持前缀未就绪时操作入队,前缀设置完成后自动执行队列里的操作,同时设置默认兜底前缀避免报错,侵入性最低无需修改业务组件代码:
let instance; class LocalStorage { constructor() { if(instance){ return instance; } instance = this; // 初始化默认前缀兜底 instance.cachePrefix = 'main'; // 初始化就绪状态 instance.isReady = false; // 未就绪时的操作队列 instance.pendingActions = []; } _getKey(key, usePrefix) { return usePrefix ? `${this.cachePrefix}:${key}` : key; } setPrefix(prefix) { this.cachePrefix = prefix; // 前缀设置完成,标记就绪,执行所有待执行操作 this.isReady = true; this.pendingActions.forEach(action => action()); this.pendingActions = []; } set(key, value, usePrefix = true) { if(key == null) return; // 未就绪则入队 if(!this.isReady) { this.pendingActions.push(() => this.set(key, value, usePrefix)); return; } localStorage.setItem(this._getKey(key, usePrefix), value); } get(key, usePrefix = true) { // 读操作如果未就绪可选择直接用默认前缀读取,或者也加入队列按需调整 if(!this.isReady) { return localStorage.getItem(this._getKey(key, usePrefix)); } return localStorage.getItem(this._getKey(key, usePrefix)); } } export const LocalStorageWrapper = new LocalStorage()
方案2:顶层Provider增加加载状态,前缀初始化完成前不渲染子应用
在已有的WalletContextProvider中新增初始化状态,异步获取网络类型前渲染加载占位,从根源避免子组件提前执行:
function WalletContextProvider({ children }) { // 原有状态保留,新增前缀初始化状态 const [prefixInitialized, setPrefixInitialized] = useState(false) // ...其他原有状态 useEffect(() => { let cancel = false if (!ethereum) { LocalStorageWrapper.setPrefix(NETWORK_TYPE_DEFAULT) setPrefixInitialized(true) return } const walletWeb3 = getWeb3(ethereum) setWalletWeb3(walletWeb3) walletWeb3.eth.net .getNetworkType() .then(networkType => { if (!cancel) { setNetworkType(networkType) LocalStorageWrapper.setPrefix(networkType) setPrefixInitialized(true) } return null }) .catch(() => { setNetworkType(NETWORK_TYPE_DEFAULT) LocalStorageWrapper.setPrefix(NETWORK_TYPE_DEFAULT) setPrefixInitialized(true) }) return () => { cancel = true setWalletWeb3(null) setNetworkType(NETWORK_TYPE_DEFAULT) LocalStorageWrapper.setPrefix(NETWORK_TYPE_DEFAULT) setPrefixInitialized(false) } }, [ethereum, chainId]) // 前缀未初始化完成时渲染加载占位,可替换为项目统一的加载组件 if(!prefixInitialized) { return <div>加载中...</div> } // 原有返回逻辑保留 return ( <WalletContext.Provider value={wallet}>{children}</WalletContext.Provider> ) }
方案3:将LocalStorage实例注入Context,类组件通过Context获取
如果需要严格保证组件获取到实例时前缀已经初始化完成,可以把LocalStorage实例放到WalletContext里,类组件通过static contextType或者WalletContext.Consumer获取实例:
- 先修改Context的value定义:
const wallet = useMemo( () => ({ // 原有所有属性保留 localStorage: LocalStorageWrapper, }), [ // 原有依赖项保留 ] )
- 类组件使用示例:
import { WalletContext } from '对应路径' class DemoClassComponent extends React.Component { static contextType = WalletContext; componentDidMount() { // 此时获取到的localStorage已经完成前缀初始化 const value = this.context.localStorage.get('自定义key'); } render() { // 原有渲染逻辑 } }
内容的提问来源于stack exchange,提问作者Nika Kurashvili
相关产品推荐
相关产品推荐

