При попытке обновить массив с помощью управления состоянием React массив состояний заполняется, но пользовательский интерфейс не обновляется. Пользовательский интерфейс обновляется только после того, как я нажимаю на панель навигации и перенаправляюсь на текущую страницу (в этом случае использованиеЭффект не запускается снова, но пользовательский интерфейс обновляется).
Код штата
const[isFetched, setIsFetched] = useState(false);
const[balances, setBalances] = useState<IBalance[]>([]);
const[num, setNum] = useState(0);
useEffect(() => {
console.info(balances);
// LOGS A POPULATED ARRAY
console.info(balances.length);
// LOGS 0
}, [balances]);
useEffect(() => {
const fetchBalances = async() =>{
let bals:IBalance[] = await kryptikService.getBalanceAllNetworks(kryptikWallet);
console.info("RECIEVED BALANCES:");
console.info(bals);
console.info(bals.length);
setBalances(bals);
setIsFetched(true);
}
fetchBalances();
}, []);
Код пользовательского интерфейса
<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>
Обработчик выборки (вызывается в 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.info("Processing Network:")
console.info(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.info(`${nw.fullName} Addy:`);
console.info(firstAddy);
console.info(`Getting balance for ${nw.fullName}...`);
// get provider for network
let networkBalance = await ethNetworkProvider.getBalance(firstAddy);
console.info(`${nw.fullName} Balance:`);
console.info(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;
}
Примечание. Массив — это другая ссылка, поэтому не должно быть проблем с поверхностными проверками на равенство. Кроме того, обновленный массив balances содержит объекты, но длина регистрируется как нулевая, как показано в первом фрагменте кода. Любая помощь будет высоко оценена!
Все, что вам нужно сделать, это прочитать документы на forEach: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…. Он выделен заметкой на синем фоне.





Вы изменяете балансы вместо того, чтобы обновлять их.
Измените balances.push на setBalances(prevState => [...prevState, newBalance])
Проблема в том, что вы повторяете массив networksFromDb в цикле forEach с асинхронным обратным вызовом. Проблема не в асинхронном обратном вызове, дело в том, что Array.protptype.forEachявляется синхронный, а обратный вызов getBalanceAllNetworks не может дождаться разрешения обратных вызовов цикла. Он возвращает пустой массив balances вызывающей стороне до заполнения массива.
Однако массив все еще заполняется, и щелчка по ссылке достаточно, чтобы вызвать повторную визуализацию React и открыть измененный массив состояний balances.
Вместо использования цикла .forEach для асинхронного обратного вызова сопоставьте networksFromDb с массивом промисов и используйте Promise.all и подождите, пока они все разрешатся, прежде чем возвращать заполненный массив balances.
Пример:
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;
};
Привет, спасибо за обстоятельный ответ! Вы меня на правильном пути. В итоге я использовал простой цикл for, и все проблемы были решены.
Используйте
for..ofдля обработки асинхронного кода в цикле, а неforEach