From 4a0429c0167aa10d5aeff9368cbb9518518339d4 Mon Sep 17 00:00:00 2001 From: katspaugh Date: Fri, 29 Sep 2023 10:18:11 +0200 Subject: [PATCH] Refactor: map props to hooks --- .../address-book/AddressBookHeader/index.tsx | 16 ++++++--- .../address-book/AddressBookTable/index.tsx | 18 +++++++--- .../address-book/EntryDialog/index.tsx | 8 +++-- .../address-book/ExportDialog/index.tsx | 18 +++++++--- src/components/tx-flow/index.tsx | 2 +- src/utils/mad-props.tsx | 36 +++++++++++++++++++ 6 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 src/utils/mad-props.tsx diff --git a/src/components/address-book/AddressBookHeader/index.tsx b/src/components/address-book/AddressBookHeader/index.tsx index ba2baed186..42874196ff 100644 --- a/src/components/address-book/AddressBookHeader/index.tsx +++ b/src/components/address-book/AddressBookHeader/index.tsx @@ -9,10 +9,11 @@ import { ADDRESS_BOOK_EVENTS } from '@/services/analytics/events/addressBook' import PageHeader from '@/components/common/PageHeader' import { ModalType } from '../AddressBookTable' import { useAppSelector } from '@/store' -import { selectAllAddressBooks } from '@/store/addressBookSlice' +import { type AddressBookState, selectAllAddressBooks } from '@/store/addressBookSlice' import ImportIcon from '@/public/images/common/import.svg' import ExportIcon from '@/public/images/common/export.svg' import AddCircleIcon from '@/public/images/common/add-outlined.svg' +import mapProps from '@/utils/mad-props' const HeaderButton = ({ icon, @@ -35,13 +36,18 @@ const HeaderButton = ({ } type Props = { + allAddressBooks: AddressBookState handleOpenModal: (type: ModalType) => () => void searchQuery: string onSearchQueryChange: (searchQuery: string) => void } -const AddressBookHeader = ({ handleOpenModal, searchQuery, onSearchQueryChange }: Props): ReactElement => { - const allAddressBooks = useAppSelector(selectAllAddressBooks) +const AddressBookHeader = ({ + allAddressBooks, + handleOpenModal, + searchQuery, + onSearchQueryChange, +}: Props): ReactElement => { const canExport = Object.values(allAddressBooks).some((addressBook) => Object.keys(addressBook || {}).length > 0) return ( @@ -96,4 +102,6 @@ const AddressBookHeader = ({ handleOpenModal, searchQuery, onSearchQueryChange } ) } -export default AddressBookHeader +export default mapProps(AddressBookHeader, { + allAddressBooks: () => useAppSelector(selectAllAddressBooks), +}) diff --git a/src/components/address-book/AddressBookTable/index.tsx b/src/components/address-book/AddressBookTable/index.tsx index 743f74c806..a52d54efd8 100644 --- a/src/components/address-book/AddressBookTable/index.tsx +++ b/src/components/address-book/AddressBookTable/index.tsx @@ -1,5 +1,7 @@ import { useContext, useMemo, useState } from 'react' import { Box } from '@mui/material' +import type { ChainInfo } from '@safe-global/safe-gateway-typescript-sdk' + import EnhancedTable from '@/components/common/EnhancedTable' import type { AddressEntry } from '@/components/address-book/EntryDialog' import EntryDialog from '@/components/address-book/EntryDialog' @@ -21,9 +23,10 @@ import PagePlaceholder from '@/components/common/PagePlaceholder' import NoEntriesIcon from '@/public/images/address-book/no-entries.svg' import { useCurrentChain } from '@/hooks/useChains' import tableCss from '@/components/common/EnhancedTable/styles.module.css' -import { TxModalContext } from '@/components/tx-flow' +import { TxModalContext, type TxModalContextType } from '@/components/tx-flow' import TokenTransferFlow from '@/components/tx-flow/flows/TokenTransfer' import CheckWallet from '@/components/common/CheckWallet' +import madProps from '@/utils/mad-props' const headCells = [ { id: 'name', label: 'Name' }, @@ -45,10 +48,12 @@ const defaultOpen = { [ModalType.REMOVE]: false, } -const AddressBookTable = () => { - const chain = useCurrentChain() - const { setTxFlow } = useContext(TxModalContext) +type AddressBookTableProps = { + chain?: ChainInfo + setTxFlow: TxModalContextType['setTxFlow'] +} +const AddressBookTable = ({ chain, setTxFlow }: AddressBookTableProps) => { const [open, setOpen] = useState(defaultOpen) const [searchQuery, setSearchQuery] = useState('') const [defaultValues, setDefaultValues] = useState(undefined) @@ -170,4 +175,7 @@ const AddressBookTable = () => { ) } -export default AddressBookTable +export default madProps(AddressBookTable, { + chain: useCurrentChain, + setTxFlow: () => useContext(TxModalContext).setTxFlow, +}) diff --git a/src/components/address-book/EntryDialog/index.tsx b/src/components/address-book/EntryDialog/index.tsx index fe63ae7b08..d2247c9199 100644 --- a/src/components/address-book/EntryDialog/index.tsx +++ b/src/components/address-book/EntryDialog/index.tsx @@ -8,6 +8,7 @@ import NameInput from '@/components/common/NameInput' import useChainId from '@/hooks/useChainId' import { useAppDispatch } from '@/store' import { upsertAddressBookEntry } from '@/store/addressBookSlice' +import madProps from '@/utils/mad-props' export type AddressEntry = { name: string @@ -22,14 +23,15 @@ const EntryDialog = ({ }, disableAddressInput = false, chainId, + currentChainId, }: { handleClose: () => void defaultValues?: AddressEntry disableAddressInput?: boolean chainId?: string + currentChainId: string }): ReactElement => { const dispatch = useAppDispatch() - const currentChainId = useChainId() const methods = useForm({ defaultValues, @@ -81,4 +83,6 @@ const EntryDialog = ({ ) } -export default EntryDialog +export default madProps(EntryDialog, { + currentChainId: useChainId, +}) diff --git a/src/components/address-book/ExportDialog/index.tsx b/src/components/address-book/ExportDialog/index.tsx index 9407e32058..a7f1442712 100644 --- a/src/components/address-book/ExportDialog/index.tsx +++ b/src/components/address-book/ExportDialog/index.tsx @@ -12,6 +12,7 @@ import { useAppSelector } from '@/store' import { trackEvent, ADDRESS_BOOK_EVENTS } from '@/services/analytics' import ExternalLink from '@/components/common/ExternalLink' import { HelpCenterArticle } from '@/config/constants' +import madProps from '@/utils/mad-props' const COL_1 = 'address' const COL_2 = 'name' @@ -35,14 +36,19 @@ export const _getCsvData = (addressBooks: AddressBookState): CsvData => { return csvData } -const ExportDialog = ({ handleClose }: { handleClose: () => void }): ReactElement => { - const addressBooks: AddressBookState = useAppSelector(selectAllAddressBooks) - const length = Object.values(addressBooks).reduce((acc, entries) => acc + Object.keys(entries).length, 0) +const ExportDialog = ({ + allAddressBooks, + handleClose, +}: { + allAddressBooks: AddressBookState + handleClose: () => void +}): ReactElement => { + const length = Object.values(allAddressBooks).reduce((acc, entries) => acc + Object.keys(entries).length, 0) const { CSVDownloader } = useCSVDownloader() // safe-address-book-1970-01-01 const filename = `safe-address-book-${new Date().toISOString().slice(0, 10)}` - const csvData = useMemo(() => _getCsvData(addressBooks), [addressBooks]) + const csvData = useMemo(() => _getCsvData(allAddressBooks), [allAddressBooks]) const onSubmit = (e: SyntheticEvent) => { e.preventDefault() @@ -87,4 +93,6 @@ const ExportDialog = ({ handleClose }: { handleClose: () => void }): ReactElemen ) } -export default ExportDialog +export default madProps(ExportDialog, { + allAddressBooks: () => useAppSelector(selectAllAddressBooks), +}) diff --git a/src/components/tx-flow/index.tsx b/src/components/tx-flow/index.tsx index eebcbb0338..0cf7a993aa 100644 --- a/src/components/tx-flow/index.tsx +++ b/src/components/tx-flow/index.tsx @@ -5,7 +5,7 @@ import useSafeInfo from '@/hooks/useSafeInfo' const noop = () => {} -type TxModalContextType = { +export type TxModalContextType = { txFlow: JSX.Element | undefined setTxFlow: (txFlow: TxModalContextType['txFlow'], onClose?: () => void, shouldWarn?: boolean) => void setFullWidth: (fullWidth: boolean) => void diff --git a/src/utils/mad-props.tsx b/src/utils/mad-props.tsx new file mode 100644 index 0000000000..518b620156 --- /dev/null +++ b/src/utils/mad-props.tsx @@ -0,0 +1,36 @@ +import React, { ComponentType, FC, useEffect, useState } from 'react' + +type SinglePropHook = () => T +type HookMap

= { + [K in keyof P]?: SinglePropHook +} + +const madProps =

, H extends keyof P>( + Component: ComponentType

, + useHook: SinglePropHook> | HookMap>, +): FC> => { + return (externalProps: Omit) => { + const [computedProps, setComputedProps] = useState

({ ...(externalProps as P) }) + + useEffect(() => { + let newProps: P = { ...computedProps } + + if (typeof useHook === 'function') { + newProps = { ...newProps, ...useHook() } as P + } else { + for (const key in useHook) { + const hook = useHook[key] + if (hook !== undefined) { + newProps[key as H] = hook() + } + } + } + + setComputedProps(newProps) + }, [externalProps, useHook]) + + return + } +} + +export default madProps