From 2c33a88009e73299fd80ed650deac5d5500dacee Mon Sep 17 00:00:00 2001 From: Thebora Kompanioni Date: Thu, 19 Sep 2024 11:18:12 +0200 Subject: [PATCH] perf: optimize api request (#846) * dev: react strict mode in dev mode * perf: load display data only if necessary * chore: 'force' reload on wallet unlock * ui(earn): validate minsize on mount of form --- src/components/App.tsx | 14 ++++++------ src/components/Earn.tsx | 10 ++++++++- src/context/WalletContext.tsx | 41 +++++++++++++++++++++++++++++++---- src/index.tsx | 33 +++++++++++++++++----------- 4 files changed, 73 insertions(+), 25 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 915a75f2..d8328d08 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -67,14 +67,14 @@ export default function App() { }, [clearCurrentWallet]) const reloadWalletInfo = useCallback( - (delay: Milliseconds) => { + ({ delay, force }: { delay: Milliseconds; force: boolean }) => { setReloadingWalletInfoCounter((current) => current + 1) console.info('Reloading wallet info...') return new Promise((resolve, reject) => setTimeout(() => { + const reload = force ? reloadCurrentWalletInfo.reloadAllForce : reloadCurrentWalletInfo.reloadAll const abortCtrl = new AbortController() - reloadCurrentWalletInfo - .reloadAll({ signal: abortCtrl.signal }) + reload({ signal: abortCtrl.signal }) .then((result) => resolve(result)) .catch((error) => reject(error)) .finally(() => { @@ -286,7 +286,7 @@ const MAX_RECURSIVE_WALLET_INFO_RELOADS = 10 interface WalletInfoAutoReloadProps { currentWallet: CurrentWallet | null - reloadWalletInfo: (delay: Milliseconds) => Promise + reloadWalletInfo: ({ delay, force }: { delay: Milliseconds; force: boolean }) => Promise } /** @@ -316,7 +316,7 @@ const WalletInfoAutoReload = ({ currentWallet, reloadWalletInfo }: WalletInfoAut function reloadAfterUnlock() { if (!currentWallet) return - reloadWalletInfo(RELOAD_WALLET_INFO_DELAY.AFTER_UNLOCK).catch((err) => console.error(err)) + reloadWalletInfo({ delay: RELOAD_WALLET_INFO_DELAY.AFTER_UNLOCK, force: true }).catch((err) => console.error(err)) }, [currentWallet, reloadWalletInfo], ) @@ -335,14 +335,14 @@ const WalletInfoAutoReload = ({ currentWallet, reloadWalletInfo }: WalletInfoAut callCounter: number = 0, ) => { if (callCounter >= maxCalls) return - const info = await reloadWalletInfo(delay) + const info = await reloadWalletInfo({ delay, force: false }) const newBalance = info.balanceSummary.calculatedTotalBalanceInSats if (newBalance > currentBalance) { await reloadWhileBalanceChangesRecursively(newBalance, delay, maxCalls, callCounter++) } } - reloadWalletInfo(RELOAD_WALLET_INFO_DELAY.AFTER_RESCAN) + reloadWalletInfo({ delay: RELOAD_WALLET_INFO_DELAY.AFTER_RESCAN, force: false }) .then((info) => reloadWhileBalanceChangesRecursively( info.balanceSummary.calculatedTotalBalanceInSats, diff --git a/src/components/Earn.tsx b/src/components/Earn.tsx index 17c2683a..9c9b3dcc 100644 --- a/src/components/Earn.tsx +++ b/src/components/Earn.tsx @@ -276,7 +276,15 @@ const EarnForm = ({ } return ( - + {(props) => { const { handleSubmit, setFieldValue, handleBlur, values, touched, errors, isSubmitting } = props const minsizeField = props.getFieldProps('minsize') diff --git a/src/context/WalletContext.tsx b/src/context/WalletContext.tsx index 4a6f59ad..ed322f83 100644 --- a/src/context/WalletContext.tsx +++ b/src/context/WalletContext.tsx @@ -1,4 +1,4 @@ -import { createContext, useEffect, useCallback, useState, useContext, PropsWithChildren, useMemo } from 'react' +import { createContext, useEffect, useCallback, useState, useContext, PropsWithChildren, useMemo, useRef } from 'react' import { getSession, setSession } from '../session' import * as fb from '../components/fb/utils' import * as Api from '../libs/JmWalletApi' @@ -143,6 +143,7 @@ interface WalletContextEntry { currentWalletInfo: WalletInfo | undefined reloadCurrentWalletInfo: { reloadAll: ({ signal }: { signal: AbortSignal }) => Promise + reloadAllForce: ({ signal }: { signal: AbortSignal }) => Promise reloadUtxos: ({ signal }: { signal: AbortSignal }) => Promise } } @@ -280,7 +281,7 @@ const WalletProvider = ({ children }: PropsWithChildren) => { [fetchDisplay], ) - const reloadAll = useCallback( + const reloadAllForce = useCallback( ({ signal }: { signal: AbortSignal }): Promise => Promise.all([reloadUtxos({ signal }), reloadDisplay({ signal })]) .then((data) => toCombinedRawData(data[0], data[1])) @@ -298,12 +299,44 @@ const WalletProvider = ({ children }: PropsWithChildren) => { return toWalletInfo(combinedRawData) }, [combinedRawData]) + const currentWalletInfoRef = useRef(currentWalletInfo) + + useEffect(() => { + currentWalletInfoRef.current = currentWalletInfo + }, [currentWalletInfoRef, currentWalletInfo]) + + const reloadAllIfNecessary = useCallback( + ({ signal }: { signal: AbortSignal }): Promise => + reloadUtxos({ signal }).then((utxoResponse) => { + const needsDisplayReload = + currentWalletInfoRef.current === undefined || + !!utxoResponse.utxos.find( + (utxo) => + // reload "display" data if: + // no address summary could be found for a returned UTXO... + currentWalletInfoRef.current!.addressSummary[utxo.address] === undefined || + // ...or if the address is still considered "new" + currentWalletInfoRef.current!.addressSummary[utxo.address].status === 'new', + ) + + if (!needsDisplayReload) { + return currentWalletInfoRef.current! + } + + return reloadDisplay({ signal }) + .then((displayResponse) => toCombinedRawData(utxoResponse, displayResponse)) + .then((raw) => toWalletInfo(raw)) + }), + [currentWalletInfoRef, reloadUtxos, reloadDisplay], + ) + const reloadCurrentWalletInfo = useMemo( () => ({ - reloadAll, + reloadAll: reloadAllIfNecessary, + reloadAllForce, reloadUtxos, }), - [reloadAll, reloadUtxos], + [reloadAllIfNecessary, reloadUtxos, reloadAllForce], ) useEffect(() => { diff --git a/src/index.tsx b/src/index.tsx index 119ee514..d8779c71 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -7,25 +7,32 @@ import { WebsocketProvider } from './context/WebsocketContext' import { ServiceInfoProvider } from './context/ServiceInfoContext' import { WalletProvider } from './context/WalletContext' import { ServiceConfigProvider } from './context/ServiceConfigContext' +import { isDevMode } from './constants/debugFeatures' import 'bootstrap/dist/css/bootstrap.min.css' import './index.css' import './i18n/config' +const ENBALE_STRICT_MODE = isDevMode() + const container = document.getElementById('root') const root = createRoot(container!) root.render( - - - - - - - - - - - - - , + (() => { + const children = ( + + + + + + + + + + + + ) + + return ENBALE_STRICT_MODE ? {children} : children + })(), )