try 通过React state management更新数组时,会填充状态数组,但用户界面无法更新.用户界面只有在我点击导航栏并重新路由到当前页面后才会更新(在这种情况下,useEffect不会再次运行,但UI会更新).

State Code

 const[isFetched, setIsFetched] = useState(false);
  const[balances, setBalances] = useState<IBalance[]>([]);
  
  const[num, setNum] = useState(0);

  useEffect(() => {
    console.log(balances);
    // LOGS A POPULATED ARRAY
    console.log(balances.length);
    // LOGS 0
  }, [balances]);
  
  useEffect(() => {
      const fetchBalances = async() =>{
        let bals:IBalance[] = await kryptikService.getBalanceAllNetworks(kryptikWallet);
        console.log("RECIEVED BALANCES:");
        console.log(bals);
        console.log(bals.length);
        setBalances(bals);
        setIsFetched(true);
      }
      fetchBalances();
    }, []);

UI Code

      <h2>Your Balances</h2>
      <Divider/>
      {
        !isFetched?<p>Loading Balances.</p>:
        <ul role="list" className="divide-y divide-gray-200 dark:divide-gray-700">
          {balances.map((balance:IBalance) => (
              <ListItem title={balance.fullName} imgSrc={balance.iconPath} subtitle={balance.ticker} amount={balance.amountCrypto}/>
          ))}
        </ul>
      }
    </div>

Fetch Handler (called in UseEffect)

    getBalanceAllNetworks = async(walletUser:IWallet):Promise<IBalance[]> =>{
    let networksFromDb = this.getSupportedNetworkDbs();
    // initialize return array
    let balances:IBalance[] = [];
    networksFromDb.forEach(async nw => {
        let network:Network = new Network(nw.fullName, nw.ticker);
        let kryptikProvider:KryptikProvider = await this.getKryptikProviderForNetworkDb(nw);
        if(network.getNetworkfamily()==NetworkFamily.EVM){
            if(!kryptikProvider.ethProvider) throw Error(`No ethereum provider set up for ${network.fullName}.`);
            let ethNetworkProvider:JsonRpcProvider = kryptikProvider.ethProvider;
            console.log("Processing Network:")
            console.log(nw.fullName);
            // gets all addresses for network
            let allAddys:string[] = await walletUser.seedLoop.getAddresses(network);
            // gets first address for network
            let firstAddy:string = allAddys[0];
            console.log(`${nw.fullName} Addy:`);
            console.log(firstAddy);
            console.log(`Getting balance for ${nw.fullName}...`);
            // get provider for network
            let networkBalance = await ethNetworkProvider.getBalance(firstAddy);
            console.log(`${nw.fullName} Balance:`);
            console.log(networkBalance);
            // prettify ether balance
            let networkBalanceAdjusted:Number = BigNumber.from(networkBalance)
            .div(BigNumber.from("10000000000000000"))
            .toNumber() / 100;
            let networkBalanceString = networkBalanceAdjusted.toString();
            let newBalanceObj:IBalance = {fullName:nw.fullName, ticker:nw.ticker, iconPath:nw.iconPath, 
                amountCrypto:networkBalanceString}
            // add adjusted balance to balances return object
            balances.push(newBalanceObj);
        }
    });
    return balances;
}

注意:数组是另一个引用,因此浅层相等性判断应该没有问题.当代码段显示为0时,数组中的第一个对象也会显示为0.任何帮助都将不胜感激!

推荐答案

Issue

问题是,您正在使用异步回调在forEach循环中迭代networksFromDbarray.异步回调不是问题所在,而是Array.protptype.forEach 105同步,getBalanceAllNetworks回调不能等待循环回调解决.在填充数组之前,它将空数组返回给调用者.

然而,数组101被填充,点击链接就足以触发React Reender,并expose 变异的balances状态array.

Solution

异步回调不使用.forEach循环,而是将networksFromDb映射到一个promise 数组,使用Promise.all,等待它们全部解析,然后返回填充的balancesarray.

例子:

const getBalanceAllNetworks = async (
  walletUser: IWallet
): Promise<IBalance[]> => {
  const networksFromDb = this.getSupportedNetworkDbs();

  const asyncCallbacks = networksFromDb
    .filter((nw) => {
      const network: Network = new Network(nw.fullName, nw.ticker);
      return network.getNetworkfamily() == NetworkFamily.EVM;
    })
    .map(async (nw) => {
      const kryptikProvider: KryptikProvider = await this.getKryptikProviderForNetworkDb(
        nw
      );

      if (!kryptikProvider.ethProvider) {
        throw Error(`No ethereum provider set up for ${network.fullName}.`);
      }

      const ethNetworkProvider: JsonRpcProvider = kryptikProvider.ethProvider;

      // gets all addresses for network
      const allAddys: string[] = await walletUser.seedLoop.getAddresses(
        network
      );

      // gets first address for network
      const firstAddy: string = allAddys[0];

      // get provider for network
      const networkBalance = await ethNetworkProvider.getBalance(firstAddy);

      // prettify ether balance
      const networkBalanceAdjusted: Number =
        BigNumber.from(networkBalance)
          .div(BigNumber.from("10000000000000000"))
          .toNumber() / 100;
      const networkBalanceString = networkBalanceAdjusted.toString();

      const newBalanceObj: IBalance = {
        fullName: nw.fullName,
        ticker: nw.ticker,
        iconPath: nw.iconPath,
        amountCrypto: networkBalanceString
      };
      // add adjusted balance to balances return object
      return newBalanceObj;
    });

  const balances: IBalance[] = await Promise.all(asyncCallbacks);
  return balances;
};

Reactjs相关问答推荐

使用Overpass API Endpoint计算 map 上的面积

更改购物车中的产品数量后,PayPal Reaction/Next JS按钮未更新

react 路由GUGUD Use Effect无法通过useNavigate正常工作

如何访问子集合中的文档?

ReactJS:唯一项目的数量总和

React useEffect 问题...异步函数内的变量正在获取延迟的更新值

React js拖放图像并预览

有没有办法在 React Router v6 中的路由匹配上运行一些逻辑/功能?

如何根据ReactJS中元素列表中的布尔值仅显示一次值

Syncfusion:带有 TabItemsDirective 的选项卡组件不显示任何内容

在添加了useplacesautocomplete后,在构建React/Next.js项目时,NPM抛出类型期望的错误

Zod 验证模式根据另一个数组字段使字段成为必需字段

使用状态与复选框不同步

页面不显示

如何在同一路由路径上重新渲染组件(链接,V6)reactjs

如何在 React map 函数循环中使用
标签

antd 找不到 comments

setState 改变对象引用

可以'读取未定义的属性(读取'路径')

错误(未捕获的类型错误):无法读取未定义的属性(读取参数)