首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >useMemo使用reactJS和recoilJS实现有效的全局数据可用性

useMemo使用reactJS和recoilJS实现有效的全局数据可用性
EN

Stack Overflow用户
提问于 2021-12-28 12:18:00
回答 1查看 212关注 0票数 0

我正试图找出如何以最好的方式解决以下问题:

我有多个组件都需要一个全局状态(我使用的是后坐力,因为我有许多不同的“原子”状态)。只有在加载了需要该状态的组件时,它才会执行代价高昂的计算来获取数据。这种情况只应在初始化时发生一次。其他需要相同数据的组件不应该重新触发数据获取,除非它们显式地调用updateState函数。

理想情况下,我的实现应该如下所示:

代码语言:javascript
复制
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和其他变量来解决这个问题,这些变量告诉我是否已经初始化了,如下所示:

代码语言:javascript
复制
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与状态混合)。虽然,因为这应该是海事组织的必要和基本的,它似乎我错过了一些解决方案,我还没有找到。

EN

回答 1

Stack Overflow用户

发布于 2021-12-28 16:57:59

我首先通过使用更多的loadedloading状态来解决这个问题。但是,当安装使用相同状态的其他组件的组件时,我意识到使用反冲状态是行不通的,因为更新只在下一个勾号上执行。因此,我选择使用全局范围的字典(这看起来可能不太漂亮,但对于这个用例来说工作得很好)。

完整的代码,以防有人遇到这样的问题。

useContractState.js

代码语言:javascript
复制
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

代码语言:javascript
复制
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更改的更新,如果已经更改,则重新获取。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70506959

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档