From efe8d190e53295fafd10e3f71945c8eb50a7b61e Mon Sep 17 00:00:00 2001 From: Noah Zinsmeister Date: Mon, 15 Aug 2022 13:15:41 -0400 Subject: [PATCH] fix: downstream referential equality errors (#144) * improve key logic * address comments * fix e2e tests * style(lint): lint action with ESLint Co-authored-by: Jordan Frankfurt Co-authored-by: Lint Action --- e2e/connect.test.tsx | 72 ++++++++++---------------- src/hooks/connectWeb3/useWeb3React.tsx | 28 ++++++++-- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/e2e/connect.test.tsx b/e2e/connect.test.tsx index 8dc1afd8a..d046c51d5 100644 --- a/e2e/connect.test.tsx +++ b/e2e/connect.test.tsx @@ -5,10 +5,12 @@ import '@ethersproject/providers' import 'jest-environment-hardhat' -import { render, RenderResult, waitFor } from '@testing-library/react' +import { waitFor } from '@testing-library/react' import { tokens } from '@uniswap/default-token-list' +import React from 'react' import { SwapWidget } from '../src' +import { renderWidget } from '../src/test' const HARDHAT_ACCOUNT_DISPLAY_STRING = `${hardhat.account.address?.substring( 0, @@ -16,77 +18,59 @@ const HARDHAT_ACCOUNT_DISPLAY_STRING = `${hardhat.account.address?.substring( )}...${hardhat.account.address?.substring(hardhat.account.address.length - 4)}` describe('connect', () => { - let component: RenderResult - let account: HTMLElement - let connectWallet: HTMLElement - let toolbar: HTMLElement - let tokenSelect: HTMLElement - - beforeEach(async () => { - component = render() - connectWallet = await component.findByTestId('connect-wallet') - toolbar = await component.findByTestId('toolbar') - tokenSelect = (await component.findAllByTestId('token-select'))[0] - }) - describe('with no params, using fallback JSON RPC URL', () => { it('prompts for wallet connection in the Wallet and Toolbar', async () => { - expect(toolbar.textContent).toBe('Connecting…') - await waitFor(() => expect(toolbar.textContent).not.toBe('Connecting…')) - expect(connectWallet.textContent).toBe('Connect wallet to swap') + const component = renderWidget() + const toolbar = await component.findByTestId('toolbar') expect(toolbar.textContent).toBe('Connect wallet to swap') + const connectWallet = await component.findByTestId('connect-wallet') + expect(connectWallet.textContent).toBe('Connect wallet to swap') }) it('expects widget not to be disabled', async () => { - expect(tokenSelect).toHaveProperty('disabled', true) - await waitFor(() => expect(toolbar.textContent).not.toBe('Connecting…')) - expect(tokenSelect).toHaveProperty('disabled', false) + const component = renderWidget() + const tokenSelect = (await component.findAllByTestId('token-select'))[0] + await waitFor(() => { + expect(tokenSelect).toHaveProperty('disabled', false) + }) }) }) describe('with valid jsonRpcUrlMap', () => { it('prompts for wallet connection in the Wallet and Toolbar', async () => { - component = render() - expect(connectWallet.textContent).toBe('Connect wallet to swap') - expect(toolbar.textContent).toBe('Connecting…') - await waitFor(() => expect(toolbar.textContent).not.toBe('Connecting…')) + const component = renderWidget() + const connectWallet = await component.findByTestId('connect-wallet') + const toolbar = await component.findByTestId('toolbar') expect(connectWallet.textContent).toBe('Connect wallet to swap') expect(toolbar.textContent).toBe('Connect wallet to swap') }) it('expects widget not to be disabled', async () => { - component = render() + const component = renderWidget() + let tokenSelect = (await component.findAllByTestId('token-select'))[0] expect(tokenSelect).toHaveProperty('disabled', true) + const toolbar = await component.findByTestId('toolbar') await waitFor(() => expect(toolbar.textContent).not.toBe('Connecting…')) - expect(tokenSelect).toHaveProperty('disabled', false) + tokenSelect = (await component.findAllByTestId('token-select'))[0] + await waitFor(() => expect(tokenSelect).toHaveProperty('disabled', false)) }) }) describe('with wallet provider', () => { it('displays connected account chip', async () => { - component = render() + const component = renderWidget() + const toolbar = await component.findByTestId('toolbar') await waitFor(() => expect(toolbar.textContent).not.toBe('Connecting…')) - account = await component.findByTestId('account') - expect(account.textContent?.toLowerCase()).toBe(HARDHAT_ACCOUNT_DISPLAY_STRING) - }) - - it('does not prompt for wallet connection in toolbar', async () => { - component = render() - expect(toolbar.textContent).toBe('Connecting…') - await waitFor( - async () => { - toolbar = (await component.findAllByTestId('toolbar'))[1] - expect(toolbar.textContent).toBe('Enter an amount') - }, - { timeout: 10000 } - ) + const account = await component.findByTestId('account') + await waitFor(() => expect(account.textContent?.toLowerCase()).toBe(HARDHAT_ACCOUNT_DISPLAY_STRING)) }) it('expects widget not to be disabled', async () => { - component = render() - expect(tokenSelect).toHaveProperty('disabled', true) + const component = renderWidget() + const toolbar = await component.findByTestId('toolbar') await waitFor(() => expect(toolbar.textContent).not.toBe('Connecting…')) - expect(tokenSelect).toHaveProperty('disabled', false) + const tokenSelect = (await component.findAllByTestId('token-select'))[0] + await waitFor(() => expect(tokenSelect).toHaveProperty('disabled', false)) }) }) }) diff --git a/src/hooks/connectWeb3/useWeb3React.tsx b/src/hooks/connectWeb3/useWeb3React.tsx index 296ff4f20..cced569d4 100644 --- a/src/hooks/connectWeb3/useWeb3React.tsx +++ b/src/hooks/connectWeb3/useWeb3React.tsx @@ -7,7 +7,7 @@ import { Connector, Provider as Eip1193Provider, Web3ReactStore } from '@web3-re import { WalletConnect } from '@web3-react/walletconnect' import { SupportedChainId } from 'constants/chains' import { atom, useAtom } from 'jotai' -import { PropsWithChildren, useEffect, useMemo } from 'react' +import { PropsWithChildren, useEffect, useMemo, useRef } from 'react' import JsonRpcConnector from 'utils/JsonRpcConnector' export type Web3Connection = [Connector, Web3ReactHooks] @@ -104,12 +104,30 @@ export function ActiveWeb3Provider({ [jsonRpcUrlMap, defaultChainId] ) - connections = [metaMaskConnection, walletConnectConnectionQR, walletConnectConnectionPopup, networkConnection] - if (integratorConnection) connections = [integratorConnection, ...connections] + const key = useRef(0) + connections = useMemo(() => { + // while react warns against triggering side effects in useMemo, + // in this instance we're only using the mutated value to generate a key, + // so tightly coupling the key update with the memo update shouldn't cause any issues, + // and most clearly expresses the intent + key.current += 1 + return [ + integratorConnection, + metaMaskConnection, + walletConnectConnectionQR, + walletConnectConnectionPopup, + networkConnection, + ].filter((connection): connection is Web3Connection => Boolean(connection)) + }, [ + integratorConnection, + metaMaskConnection, + walletConnectConnectionQR, + walletConnectConnectionPopup, + networkConnection, + ]) - const key = `${connections.length}+${Object.entries(jsonRpcUrlMap)}+${propsDefaultChainId}+${defaultChainId}` return ( - + {children} )