我正试图找出如何以最好的方式解决以下问题:
我有多个组件都需要一个全局状态(我使用的是后坐力,因为我有许多不同的“原子”状态)。只有在加载了需要该状态的组件时,它才会执行代价高昂的计算来获取数据。这种情况只应在初始化时发生一次。其他需要相同数据的组件不应该重新触发数据获取,除非它们显式地调用updateState函数。
理想情况下,我的实现应该如下所示:
const initialState = {
uri: '',
balance: '0',
};
const fetchExpensiveState = () => {
uri: await fetchURI(),
balance: await fetchBalance(),
});
const MyExpensiveData = atom({
key: 'expensiveData',
default: initialState,
updateState: fetchExpensiveState,
});
function Component1() {
const data = useRecoilMemo(MyExpensiveData); // triggers `fetchExpensiveState` upon first call
return ...
}
function Component2() {
const data = useRecoilMemo(MyExpensiveData); // doesn't trigger `fetchExpensiveState` a second time
return ...
}我可以通过在上下文中使用useRecoilState和其他变量来解决这个问题,这些变量告诉我是否已经初始化了,如下所示:
export function useExpensiveState() {
const [context, setContext] = useRecoilState(MyExpensiveData);
const updateState = useCallback(async () => {
setContext({...fetchExpensiveState(), loaded: true});
}, []);
useEffect(() => {
if (!context.loaded) {
setContext({...context, loaded: true});
updateState();
}
}, []);
return { ...context, updateState };
}可以使此解决方案更加优雅(例如,不要将loaded与状态混合)。虽然,因为这应该是海事组织的必要和基本的,它似乎我错过了一些解决方案,我还没有找到。
发布于 2021-12-28 16:57:59
我首先通过使用更多的loaded和loading状态来解决这个问题。但是,当安装使用相同状态的其他组件的组件时,我意识到使用反冲状态是行不通的,因为更新只在下一个勾号上执行。因此,我选择使用全局范围的字典(这看起来可能不太漂亮,但对于这个用例来说工作得很好)。
完整的代码,以防有人遇到这样的问题。
useContractState.js
import { useWeb3React } from '@web3-react/core';
import { useEffect, useState } from 'react';
import { atomFamily, useRecoilState } from 'recoil';
const contractState = atomFamily({
key: 'ContractState',
default: {},
});
var contractStateInitialized = {};
var contractStateLoading = {};
export function useContractState(key, fetchState, initialState, initializer) {
const [state, setState] = useRecoilState(contractState(key));
const [componentDidMount, setComponentMounting] = useState(false);
const { library } = useWeb3React();
const provider = library?.provider;
const updateState = () => {
fetchState()
.then(setState)
.then(() => {
contractStateInitialized[key] = true;
contractStateLoading[key] = false;
});
};
useEffect(() => {
// ensures that this will only be called once per init or per provider update
// doesn't re-trigger when a component re-mounts
if (provider != undefined && !contractStateLoading[key] && (componentDidMount || !contractStateInitialized[key])) {
console.log('running expensive fetch:', key);
contractStateLoading[key] = true;
if (initializer != undefined) initializer();
updateState();
setComponentMounting(true);
}
}, [provider]);
if (!contractStateInitialized[key] && initialState != undefined) return [initialState, updateState];
return [state, updateState];
}useSerumContext.js
import { useSerumContract } from '../lib/ContractConnector';
import { useContractState } from './useContractState';
export function useSerumContext() {
const { contract } = useSerumContract();
const fetchState = async () => ({
owner: await contract.owner(),
claimActive: await contract.claimActive(),
});
return useContractState('serumContext', fetchState);
}我有这么多额外检查的原因是我不想在组件重新挂载时重新获取状态,但是状态已经初始化了。但是,状态应该订阅有关provider更改的更新,如果已经更改,则重新获取。
https://stackoverflow.com/questions/70506959
复制相似问题