From d8107d6686dcaca29e926ff5934acd964de6d904 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Wed, 23 Sep 2020 19:37:46 +0200 Subject: [PATCH 1/2] fix hooks dependencies, prevent some rerenders --- src/hooks/useCompute/useCompute.ts | 3 +- src/hooks/useMetadata/useMetadata.ts | 34 ++++--- src/hooks/usePublish/usePublish.ts | 4 +- src/providers/OceanProvider/OceanProvider.tsx | 96 ++++++++++--------- 4 files changed, 73 insertions(+), 64 deletions(-) diff --git a/src/hooks/useCompute/useCompute.ts b/src/hooks/useCompute/useCompute.ts index 6fd658c..8fef8b8 100644 --- a/src/hooks/useCompute/useCompute.ts +++ b/src/hooks/useCompute/useCompute.ts @@ -1,8 +1,7 @@ import { useState } from 'react' import { useOcean } from '../../providers' import { ComputeValue } from './ComputeOptions' -import { feedback } from './../../utils' -import { DID, Logger } from '@oceanprotocol/lib' +import { Logger } from '@oceanprotocol/lib' import { MetadataAlgorithm } from '@oceanprotocol/lib/dist/node/ddo/interfaces/MetadataAlgorithm' import { ComputeJob } from '@oceanprotocol/lib/dist/node/ocean/interfaces/ComputeJob' import { checkAndBuyDT } from '../../utils/dtUtils' diff --git a/src/hooks/useMetadata/useMetadata.ts b/src/hooks/useMetadata/useMetadata.ts index fe9e7fb..5d20cf3 100644 --- a/src/hooks/useMetadata/useMetadata.ts +++ b/src/hooks/useMetadata/useMetadata.ts @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react' +import { useState, useEffect, useCallback } from 'react' import { DID, DDO, Metadata, Logger } from '@oceanprotocol/lib' import { useOcean } from '../../providers' import ProviderStatus from '../../providers/OceanProvider/ProviderStatus' @@ -25,23 +25,29 @@ function useMetadata(asset?: DID | string | DDO): UseMetadata { const [isLoaded, setIsLoaded] = useState(false) const [price, setPrice] = useState() - async function getDDO(did: DID | string): Promise { - if (status !== ProviderStatus.CONNECTED) return null + const getDDO = useCallback( + async (did: DID | string): Promise => { + if (status !== ProviderStatus.CONNECTED) return null - const ddo = await ocean.metadatastore.retrieveDDO(did) - return ddo - } + const ddo = await ocean.metadatastore.retrieveDDO(did) + return ddo + }, + [ocean?.metadatastore, status] + ) - async function getPrice(dataTokenAddress?: string): Promise { - if (!dataTokenAddress) dataTokenAddress = internalDdo.dataToken - return await getBestDataTokenPrice(ocean, dataTokenAddress, accountId) - } + const getPrice = useCallback( + async (dataTokenAddress?: string): Promise => { + if (!dataTokenAddress) dataTokenAddress = internalDdo.dataToken + return await getBestDataTokenPrice(ocean, dataTokenAddress, accountId) + }, + [ocean, accountId, internalDdo?.dataToken] + ) - async function getMetadata(): Promise { + const getMetadata = useCallback(async (): Promise => { if (!internalDdo) return null const metadata = internalDdo.findServiceByType('metadata') return metadata.attributes - } + }, [internalDdo]) useEffect(() => { async function init(): Promise { @@ -60,7 +66,7 @@ function useMetadata(asset?: DID | string | DDO): UseMetadata { } } init() - }, [ocean, status]) + }, [ocean, status, asset, getDDO]) useEffect(() => { if (!accountId) return @@ -83,7 +89,7 @@ function useMetadata(asset?: DID | string | DDO): UseMetadata { setPrice(price) }, 10000) return () => clearInterval(interval) - }, [accountId, internalDdo]) + }, [accountId, internalDdo, getMetadata, getPrice]) return { ddo: internalDdo, diff --git a/src/hooks/usePublish/usePublish.ts b/src/hooks/usePublish/usePublish.ts index 638832c..8095d30 100644 --- a/src/hooks/usePublish/usePublish.ts +++ b/src/hooks/usePublish/usePublish.ts @@ -49,7 +49,7 @@ function usePublish(): UsePublish { ): Promise { switch (priceOptions.type) { case 'dynamic': { - const pool = await ocean.pool.createDTPool( + await ocean.pool.createDTPool( accountId, dataTokenAddress, priceOptions.tokensToMint.toString(), @@ -64,7 +64,7 @@ function usePublish(): UsePublish { return null } - const fixedPriceExchange = await ocean.fixedRateExchange.create( + await ocean.fixedRateExchange.create( dataTokenAddress, priceOptions.price.toString(), accountId diff --git a/src/providers/OceanProvider/OceanProvider.tsx b/src/providers/OceanProvider/OceanProvider.tsx index 4d4c7b7..d64ed9d 100644 --- a/src/providers/OceanProvider/OceanProvider.tsx +++ b/src/providers/OceanProvider/OceanProvider.tsx @@ -3,7 +3,9 @@ import React, { useState, useEffect, createContext, - ReactElement + ReactElement, + useCallback, + ReactNode } from 'react' import Web3 from 'web3' import ProviderStatus from './ProviderStatus' @@ -42,7 +44,7 @@ function OceanProvider({ }: { initialConfig: Config web3ModalOpts?: Partial - children: any + children: ReactNode }): ReactElement { const [web3, setWeb3] = useState() const [web3Provider, setWeb3Provider] = useState() @@ -60,71 +62,74 @@ function OceanProvider({ ProviderStatus.NOT_AVAILABLE ) - async function init() { + const init = useCallback(async () => { Logger.log('Ocean Provider init') window && window.ethereum && (window.ethereum.autoRefreshOnNetworkChange = false) Logger.log('Web3Modal init.') - if (web3ModalOpts === undefined) { - web3ModalOpts = await getDefaultProviders() - } - const web3ModalInstance = new Web3Modal(web3ModalOpts) + + const web3ModalInstance = new Web3Modal( + web3ModalOpts || (await getDefaultProviders()) + ) setWeb3Modal(web3ModalInstance) Logger.log('Web3Modal instance created.', web3ModalInstance) - } + }, [web3ModalOpts]) - async function connect(newConfig?: Config) { - try { - Logger.log('Connecting ...', newConfig) + const connect = useCallback( + async (newConfig?: Config) => { + try { + Logger.log('Connecting ...', newConfig) - newConfig && setConfig(newConfig) + newConfig && setConfig(newConfig) - const provider = await web3Modal?.connect() - setWeb3Provider(provider) + const provider = await web3Modal?.connect() + setWeb3Provider(provider) - const web3 = new Web3(provider) - setWeb3(web3) - Logger.log('Web3 created.', web3) + const web3 = new Web3(provider) + setWeb3(web3) + Logger.log('Web3 created.', web3) - const chainId = web3 && (await web3.eth.getChainId()) - setChainId(chainId) - Logger.log('chain id ', chainId) + const chainId = web3 && (await web3.eth.getChainId()) + setChainId(chainId) + Logger.log('chain id ', chainId) - config.web3Provider = web3 - const ocean = await Ocean.getInstance(config) - setOcean(ocean) - Logger.log('Ocean instance created.', ocean) + config.web3Provider = web3 + const ocean = await Ocean.getInstance(config) + setOcean(ocean) + Logger.log('Ocean instance created.', ocean) - setStatus(ProviderStatus.CONNECTED) + setStatus(ProviderStatus.CONNECTED) - const account = (await ocean.accounts.list())[0] - setAccount(account) - Logger.log('Account ', account) + const account = (await ocean.accounts.list())[0] + setAccount(account) + Logger.log('Account ', account) - const accountId = await getAccountId(web3) - setAccountId(accountId) - Logger.log('account id', accountId) + const accountId = await getAccountId(web3) + setAccountId(accountId) + Logger.log('account id', accountId) - const balance = await getBalance(account) - setBalance(balance) - Logger.log('balance', JSON.stringify(balance)) - } catch (error) { - Logger.error(error) - } - } + const balance = await getBalance(account) + setBalance(balance) + Logger.log('balance', JSON.stringify(balance)) + } catch (error) { + Logger.error(error) + } + }, + [config, web3Modal] + ) // On mount setup Web3Modal instance useEffect(() => { init() - }, []) + }, [init]) // Connect automatically to cached provider if present useEffect(() => { if (!web3Modal) return web3Modal.cachedProvider && connect() - }, [web3Modal]) + }, [web3Modal, connect]) async function refreshBalance() { const balance = account && (await getBalance(account)) @@ -135,14 +140,13 @@ function OceanProvider({ web3Modal?.clearCachedProvider() } - const handleAccountsChanged = async (accounts: string[]) => { - Logger.debug("Handling 'accountsChanged' event with payload", accounts) - connect() - } - // TODO: #68 Refetch balance periodically, or figure out some event to subscribe to useEffect(() => { + const handleAccountsChanged = async (accounts: string[]) => { + Logger.debug("Handling 'accountsChanged' event with payload", accounts) + connect() + } // web3Modal && web3Modal.on('connect', handleConnect) if (web3Provider !== undefined && web3Provider !== null) { @@ -154,7 +158,7 @@ function OceanProvider({ // web3Provider.removeListener('chainChanged', handleNetworkChanged) } } - }, [web3Modal, web3Provider]) + }, [web3Modal, web3Provider, connect]) return ( Date: Wed, 23 Sep 2020 21:06:56 +0200 Subject: [PATCH 2/2] useMetadata refactor for strict type checking --- src/hooks/useCompute/useCompute.ts | 12 ++- src/hooks/useMetadata/useMetadata.ts | 105 +++++++++++++++------------ src/utils/dtUtils.ts | 5 +- tsconfig.json | 6 +- 4 files changed, 70 insertions(+), 58 deletions(-) diff --git a/src/hooks/useCompute/useCompute.ts b/src/hooks/useCompute/useCompute.ts index 8fef8b8..61319cd 100644 --- a/src/hooks/useCompute/useCompute.ts +++ b/src/hooks/useCompute/useCompute.ts @@ -13,7 +13,7 @@ interface UseCompute { dataTokenAddress: string, algorithmRawCode: string, computeContainer: ComputeValue - ) => Promise + ) => Promise computeStep?: number computeStepText?: string computeError?: string @@ -44,7 +44,13 @@ function useCompute(): UseCompute { const [computeError, setComputeError] = useState() const [isLoading, setIsLoading] = useState(false) - function setStep(index: number) { + function setStep(index?: number) { + if (!index) { + setComputeStep(undefined) + setComputeStepText(undefined) + return + } + setComputeStep(index) setComputeStepText(computeFeedback[index]) } @@ -55,7 +61,7 @@ function useCompute(): UseCompute { dataTokenAddress: string, algorithmRawCode: string, computeContainer: ComputeValue - ): Promise { + ): Promise { if (!ocean || !account) return setComputeError(undefined) diff --git a/src/hooks/useMetadata/useMetadata.ts b/src/hooks/useMetadata/useMetadata.ts index 5d20cf3..ade9dc2 100644 --- a/src/hooks/useMetadata/useMetadata.ts +++ b/src/hooks/useMetadata/useMetadata.ts @@ -7,89 +7,100 @@ import { isDDO } from '../../utils' import BestPrice from './BestPrice' interface UseMetadata { - ddo: DDO - did: DID | string - metadata: Metadata - title: string - price: BestPrice + ddo: DDO | undefined + did: DID | string | undefined + metadata: Metadata | undefined + title: string | undefined + price: BestPrice | undefined isLoaded: boolean - getPrice: (dataTokenAddress?: string) => Promise + getPrice: (dataTokenAddress: string) => Promise } function useMetadata(asset?: DID | string | DDO): UseMetadata { - const { ocean, status, accountId } = useOcean() - const [internalDdo, setDDO] = useState() - const [internalDid, setDID] = useState() - const [metadata, setMetadata] = useState() - const [title, setTitle] = useState() + const { ocean, status, accountId, chainId } = useOcean() + const [internalDdo, setDDO] = useState() + const [internalDid, setDID] = useState() + const [metadata, setMetadata] = useState() + const [title, setTitle] = useState() const [isLoaded, setIsLoaded] = useState(false) - const [price, setPrice] = useState() + const [price, setPrice] = useState() const getDDO = useCallback( - async (did: DID | string): Promise => { - if (status !== ProviderStatus.CONNECTED) return null - + async (did: DID | string): Promise => { const ddo = await ocean.metadatastore.retrieveDDO(did) return ddo }, - [ocean?.metadatastore, status] + [ocean?.metadatastore] ) const getPrice = useCallback( - async (dataTokenAddress?: string): Promise => { - if (!dataTokenAddress) dataTokenAddress = internalDdo.dataToken - return await getBestDataTokenPrice(ocean, dataTokenAddress, accountId) + async (dataTokenAddress: string): Promise => { + const price = await getBestDataTokenPrice( + ocean, + dataTokenAddress, + accountId + ) + return price }, - [ocean, accountId, internalDdo?.dataToken] + [ocean, accountId] ) - const getMetadata = useCallback(async (): Promise => { - if (!internalDdo) return null - const metadata = internalDdo.findServiceByType('metadata') + const getMetadata = useCallback(async (ddo: DDO): Promise => { + const metadata = ddo.findServiceByType('metadata') return metadata.attributes - }, [internalDdo]) + }, []) + // + // Get and set DDO based on passed DDO or DID + // useEffect(() => { + if (!asset || !ocean || status !== ProviderStatus.CONNECTED) return + async function init(): Promise { - if (ocean && status === ProviderStatus.CONNECTED) { - if (!asset) return + if (!asset) return - if (isDDO(asset)) { - setDDO(asset) - setDID(asset.id) - } else { - const ddo = await getDDO(asset) - Logger.debug('DDO', ddo) - setDDO(ddo) - setDID(asset) - } + if (isDDO(asset as string | DDO | DID)) { + setDDO(asset as DDO) + setDID((asset as DDO).id) + } else { + // asset is a DID + const ddo = await getDDO(asset as DID) + Logger.debug('DDO', ddo) + setDDO(ddo) + setDID(asset as DID) } } init() }, [ocean, status, asset, getDDO]) + // + // Get metadata for stored DDO + // useEffect(() => { if (!accountId) return async function init(): Promise { - if (internalDdo) { - const metadata = await getMetadata() - setMetadata(metadata) - setTitle(metadata.main.name) - const price = await getPrice() + if (!internalDdo) return - setPrice(price) - setIsLoaded(true) - } + const metadata = await getMetadata(internalDdo) + setMetadata(metadata) + setTitle(metadata.main.name) + const price = await getPrice(internalDdo.dataToken) + price && setPrice(price) + setIsLoaded(true) } init() const interval = setInterval(async () => { - const price = await getPrice() - setPrice(price) + if (!internalDdo) return + const price = await getPrice(internalDdo.dataToken) + price && setPrice(price) }, 10000) - return () => clearInterval(interval) - }, [accountId, internalDdo, getMetadata, getPrice]) + + return () => { + clearInterval(interval) + } + }, [accountId, chainId, internalDdo, getMetadata, getPrice]) return { ddo: internalDdo, diff --git a/src/utils/dtUtils.ts b/src/utils/dtUtils.ts index b7cd6fd..fdfa02e 100644 --- a/src/utils/dtUtils.ts +++ b/src/utils/dtUtils.ts @@ -50,8 +50,7 @@ export async function getCheapestPool( export async function getCheapestExchange( ocean: Ocean, dataTokenAddress: string -): Promise<{ address?: string; price: string } | null> { - if (!ocean || !dataTokenAddress) return +): Promise<{ address?: string; price: string }> { try { const tokenExchanges = await ocean.fixedRateExchange.searchforDT( dataTokenAddress, @@ -94,7 +93,7 @@ export async function getBestDataTokenPrice( ocean: Ocean, dataTokenAddress: string, accountId: string -): Promise { +): Promise { const cheapestPool = await getCheapestPool(ocean, accountId, dataTokenAddress) const cheapestExchange = await getCheapestExchange(ocean, dataTokenAddress) Decimal.set({ precision: 5 }) diff --git a/tsconfig.json b/tsconfig.json index 68ff149..d5e0f81 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,11 +11,7 @@ "sourceMap": true, "declaration": true, "importHelpers": true, - - /* Strict Type-Checking */ - // TODO: remove and adapt for `strictNullChecks` - "strict": true, - "strictNullChecks": false + "strict": true }, "include": ["./src/@types", "./src/index.ts"] }