Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions packages/app/src/library/GenerateNominations/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { pluginEnabled } from 'global-bus'
import { useFetchMethods } from 'hooks/useFetchMethods'
import { ValidatorList } from 'library/ValidatorList'
import { Subheading } from 'pages/Nominate/Wrappers'
import { useEffect } from 'react'
import { useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import type { AnyFunction, AnyJson, Validator } from 'types'
import { Confirm } from '../Prompt/Confirm'
Expand Down Expand Up @@ -66,6 +66,16 @@ export const GenerateNominations = ({

const defaultNominationsCount = defaultNominations.length

// Memoize stringified nominations for comparison
const nominationsString = useMemo(
() => JSON.stringify(nominations),
[nominations],
)
const defaultNominationsString = useMemo(
() => JSON.stringify(defaultNominations),
[defaultNominations],
)

const resizeCallback = () => {
setHeight(null)
}
Expand Down Expand Up @@ -193,15 +203,20 @@ export const GenerateNominations = ({
// Update nominations on account switch, or if `defaultNominations` change
useEffect(() => {
if (
JSON.stringify(nominations) !== JSON.stringify(defaultNominations) &&
nominationsString !== defaultNominationsString &&
defaultNominationsCount > 0
) {
setNominations([...(defaultNominations || [])])
if (defaultNominationsCount) {
setMethod('manual')
}
}
}, [activeAddress, defaultNominations])
}, [
activeAddress,
nominationsString,
defaultNominationsString,
defaultNominationsCount,
])

// Refetch if fetching is triggered
useEffect(() => {
Expand Down
7 changes: 4 additions & 3 deletions packages/app/src/library/NominationList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { MotionContainer } from 'library/List/MotionContainer'
import { motion } from 'motion/react'
import { fetchValidatorEraPointsBatch } from 'plugin-staking-api'
import type { ValidatorEraPointsBatch } from 'plugin-staking-api/types'
import { useEffect, useRef, useState } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import type { NominationStatus } from 'types'
import { useOverlay } from 'ui-overlay'
Expand Down Expand Up @@ -101,8 +101,9 @@ export const NominationList = ({
)

// A unique key for the current page of items
const pageKey = JSON.stringify(
validators.map(({ address }, i) => `${i}${address}`),
const pageKey = useMemo(
() => JSON.stringify(validators.map(({ address }, i) => `${i}${address}`)),
[validators],
)

// If in modal, handle resize
Expand Down
5 changes: 4 additions & 1 deletion packages/app/src/library/ValidatorList/Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Remove } from 'library/ListItem/Buttons/Remove'
import { APY } from 'library/ListItem/Labels/APY'
import { Quartile } from 'library/ListItem/Labels/Quartile'
import { Wrapper } from 'library/ListItem/Wrappers'
import { memo } from 'react'
import type { Validator } from 'types'
import { HeaderButtonRow, LabelRow, Separator } from 'ui-core/list'
import { FavoriteValidator } from '../ListItem/Buttons/FavoriteValidator'
Expand All @@ -24,7 +25,7 @@ import { EraStatus } from '../ListItem/Labels/EraStatus'
import { Identity } from '../ListItem/Labels/Identity'
import type { ItemProps } from './types'

export const Item = ({
const ItemComponent = ({
validator,
toggleFavorites,
displayFor,
Expand Down Expand Up @@ -114,3 +115,5 @@ export const Item = ({
</Wrapper>
)
}

export const Item = memo(ItemComponent)
32 changes: 24 additions & 8 deletions packages/app/src/library/ValidatorList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import { Pagination } from 'library/List/Pagination'
import { SearchInput } from 'library/List/SearchInput'
import { motion } from 'motion/react'
import { fetchValidatorEraPointsBatch } from 'plugin-staking-api'
import type { ValidatorEraPointsBatch } from 'plugin-staking-api/types'
import type {
ValidatorEraPoints,
ValidatorEraPointsBatch,
} from 'plugin-staking-api/types'
import type { FormEvent } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
Expand All @@ -33,6 +36,9 @@ import { FilterHeaders } from './Filters/FilterHeaders'
import { Item } from './Item'
import type { ValidatorListProps } from './types'

// Constant empty array to ensure stable reference for React.memo optimization
const EMPTY_ERA_POINTS: ValidatorEraPoints[] = []

export const ValidatorListInner = ({
// Default list values.
nominator: initialNominator,
Expand Down Expand Up @@ -130,6 +136,15 @@ export const ValidatorListInner = ({
[],
)

// Create a memoized map of validator addresses to era points for stable references
const performanceMap = useMemo(
() =>
Object.fromEntries(
performances.map((entry) => [entry.validator, entry.points]),
),
[performances],
)

// Pagination
const pageLength: number = itemsPerPage || validators.length
const totalPages = Math.ceil(validators.length / pageLength)
Expand Down Expand Up @@ -176,12 +191,15 @@ export const ValidatorListInner = ({
}
}

// Get validator reward rates
const { rates } = useValidatorRewardRateBatch(
listItems.map(({ address }) => address),
pageKey,
// Memoize address array to prevent unnecessary hook calls
const validatorAddresses = useMemo(
() => listItems.map(({ address }) => address),
[listItems],
)

// Get validator reward rates
const { rates } = useValidatorRewardRateBatch(validatorAddresses, pageKey)

const handleSearchChange = (e: FormEvent<HTMLInputElement>) => {
const newValue = e.currentTarget.value

Expand Down Expand Up @@ -375,9 +393,7 @@ export const ValidatorListInner = ({
bondFor={bondFor}
displayFor={displayFor}
eraPoints={
performances.find(
(entry) => entry.validator === validator.address,
)?.points || []
performanceMap[validator.address] ?? EMPTY_ERA_POINTS
}
rate={rates[pageKey]?.[validator.address]}
nominationStatus={nominationStatus.current[validator.address]}
Expand Down