React State Update 正在为 First State Update 工作,但无法进行任何进一步的更新

React State Update Working for First State Update but Failing on any further updates

所以我有一个 React 组件,它会每隔一段时间调用一个函数来获取帐户余额。组件的第一次加载从加载中成功更新,未准备就绪并提取用户帐户余额。

完成交易并在下一次账户余额轮询时,账户余额会在状态和日志中更新为正确的值,但不会在组件 UI(或任何子组件)上更新。我还尝试提取其中一个变量,如下所示为 pcDaiValue。这也会在第一次加载和第一次获取账户余额时更新,但是不会进一步更新,例如在交易之后。状态确实会更新并记录,但是 UI 不会更新以反映此状态。

我最初认为这是之前 post 中的子组件问题,但现在我意识到这是父组件的状态未更新。

如果您查看 pcDaiValue 状态 - 它将在状态而不是 UI 中更新。想知道一个可能的解决方案。

如有任何帮助,我们将不胜感激

下面的代码删除了不相关的部分:

function ProtektDepositCard({
  children,
  item,
  lendingMarketMetrics,
  tokenPrices,
}: Props): React.Node {
 

  const [accountBalances, setAccountBalances] = useState({ready:false})
  const [pcDaiValue, setPCDaiValue] = useState(0)

  useInterval(async () => {
    (async function(){
      const newAccountBalances = await GetAccountBalances(
        web3Context.address,
        tokenPrices,
        contracts,
        [item.underlyingTokenSymbol, item.pTokenSymbol, item.reserveTokenSymbol, item.shieldTokenSymbol, item.coreTokenSymbol],
        [item.underlyingTokenDecimals, item.pTokenDecimals, item.reserveTokenDecimals, item.shieldTokenDecimals, item.coreTokenDecimals],
        [item.pTokenAddress, item.pTokenAddress, item.shieldTokenAddress, item.shieldTokenAddress, item.pTokenAddress],
        [null, item.underlyingTokenSymbol, null, item.reserveTokenSymbol, null]
      )
      console.log('retrieved account balances')
      console.log(newAccountBalances)
      console.log('retrieved pcdai')
      console.log(pcDaiValue)
      if(newAccountBalances['pcdai']){
        const pTokenValue = newAccountBalances['pcdai']['token']
        setPCDaiValue(pTokenValue)
      }
      
      setAccountBalances({...newAccountBalances})
    })();
  }, 5000)


  console.log('logging account balances')
  console.log(accountBalances)

  return ( (coverage.loading) ? <Card><Card.Body><Dimmer active loader /></Card.Body></Card> : 
    <AccordionItem
      key={accountBalances}
    >
     <div>PCDAI VALUE: {String(pcDaiValue)}</div>
      <Card className="mb-1">
        <AccordionItemHeading>
          <AccordionItemButton>
            <Card.Body>
              <Grid.Row alignItems="center" justifyContent="center">
                <Grid.Col width={2}>
                  <Avatar
                    imageURL={`assets/${item.coreTokenLogo}.png`}
                    style={{"verticalAlign":"middle"}}
                  />
                  <Text size="h4" align="center" RootComponent="span" className="ml-2">{item.coreToken.toUpperCase()}</Text>
                </Grid.Col>
                <Grid.Col width={3}>
                  <Avatar
                    imageURL={`assets/${item.protocolLogo}.png`}
                    style={{"verticalAlign":"middle"}}
                    size="lg"
                  />
                  <Text size="h4" align="center" RootComponent="span" className="ml-1">{item.underlyingProtocol.toUpperCase()}</Text>
                </Grid.Col>
                <Grid.Col width={2}>
                  <Text size="h4" align="center" className="mb-0">{`${numeral(coverage.netAdjustedAPR).format('0.00')}%`}</Text>
                </Grid.Col>
                <Grid.Col width={2}>
                  <Text align="center">
                    {`${numeral(coverage.pTokenTotalDepositUsd).format('[=10=],0a')}`}
                  </Text>
                  <Text align="center" size="sm" muted>
                    {`${numeral(parseFloat(ethers.utils.formatUnits(coverage.pTokenTotalDepositTokens,item.underlyingTokenDecimals))).format('0,0a')} ${item.underlyingTokenSymbol.toUpperCase()}`}
                  </Text>
                </Grid.Col>
                <Grid.Col width={3} className="text-center">
                  <Tag.List>
                    <Tag rounded color="purple">{item.riskTag}</Tag>
                  </Tag.List>
                </Grid.Col>
              </Grid.Row>
            </Card.Body>
          </AccordionItemButton>
        </AccordionItemHeading>
        <AccordionItemPanel>
          <ProtektHoldingSection
            item={item}
            tokenPrices={tokenPrices}
            web3Context={web3Context}
            gasPrice={gasPrice}
            contracts={contracts}
            coverage={coverage}
            claimsManager={claimsManager}
            accountBalances={accountBalances}
            onRequery={()=>{
                console.log('forcing update')
                setRequery(prevState=>prevState + 1)
              }}
            actionCount={requery}
            key={accountBalances}
          />
          <Card.Body>
            <Grid.Row>
              <Grid.Col width={6}>
                <h5 className="m-0 text-muted">{`COST`}</h5>
                <p>{`${numeral(coverage.coverageFeeAPR).format('0.00')}% for ${coverage.coverageRatioDisplay} coverage`}</p>
                <h5 className="m-0 text-muted">{`BACKED BY`}</h5>
                <p>{`${item.backedByDisplay}`}</p>
              </Grid.Col>
              <Grid.Col width={6}>
                <h5 className="m-0 text-muted">{`CLAIMS`}</h5>
                <p>{`${item.claimsManagerDisplay}`}</p>
              </Grid.Col>
            </Grid.Row>
            <Grid.Row>
              <Grid.Col width={12}>
                <h5 className="m-0 text-muted">{`COVERAGE FOR`}</h5>
                <p>{`${item.coverageDisplay}`}</p>
              </Grid.Col>
            </Grid.Row>
          </Card.Body>
          { !web3Context.ready ?
              (<Card.Body><Text className="text-center font-italic">Connect Wallet Above<span role="img"></span></Text></Card.Body>) : 
                !accountBalances.ready ? <Card.Body><Dimmer active loader /></Card.Body> : 
                  accountBalances[item.pTokenSymbol]["token"] === "0" ?
                    renderDepositCard() :
                      (<div></div>)
          }
        </AccordionItemPanel>
      </Card>
    </AccordionItem>
  )
}

/** @component */
export default ProtektDepositCard;

非常感谢您的帮助/反馈。

更新

感谢您到目前为止的评论 - 我已经尝试在 useInterval 函数中增加一个基本计数器,但 UI 仍然没有更新相关状态:



function ProtektDepositCard({
  children,
  item,
  lendingMarketMetrics,
  tokenPrices,
}: Props): React.Node {

  const [counter, setCounter] = useState(0)

  useInterval(async () => {
    (async function(){
      /* other logic */
      setCounter(counter+1)
    })();
  }, 5000)


  console.log(`logging counter: ${counter}`)


  return ( 
/* Other logic */
     <div>counter: ${counter}</div>
/* Other logic */
     )

/** @component */
export default ProtektDepositCard;

最小化的示例,但似乎仍然存在状态错误,状态每 5 秒正确记录一次更新计数器值,但它没有反映在 UI。

很遗憾,您正在使用的库 react-accessible-accordion 中似乎存在错误:

https://github.com/springload/react-accessible-accordion/issues/305

如果你把计数器放在手风琴的外面,你会看到它愉快地更新。或者,如果您切换手风琴,它也会更新!