Skip to content

Commit 06853f9

Browse files
committed
extract fetch to page getStaticProps
1 parent c172fd0 commit 06853f9

File tree

3 files changed

+134
-165
lines changed

3 files changed

+134
-165
lines changed

src/components/Staking/StakingStatsBox.tsx

Lines changed: 56 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,54 @@
1-
import { ReactNode, useEffect, useState } from "react"
21
import { useRouter } from "next/router"
32
import { useTranslation } from "next-i18next"
43
import { MdInfoOutline } from "react-icons/md"
5-
import { Code, Flex, Icon, Spinner, VStack } from "@chakra-ui/react"
4+
import { Code, Flex, Icon, VStack } from "@chakra-ui/react"
65

7-
import type { Lang } from "@/lib/types"
6+
import type { BeaconchainData, ChildOnlyProp, Lang } from "@/lib/types"
87

98
import InlineLink from "@/components/Link"
109
import Text from "@/components/OldText"
1110
import Tooltip from "@/components/Tooltip"
1211

1312
import { getLocaleForNumberFormat } from "@/lib/utils/translations"
1413

15-
const NA_ERROR = "n/a"
16-
const ZERO = "0"
17-
18-
const Cell: React.FC<{ children: ReactNode }> = ({ children }) => {
19-
return (
20-
<VStack
21-
spacing={2}
22-
py={4}
23-
px={8}
24-
borderInlineStart={{ md: "1px" }}
25-
borderTop={{ base: "1px", md: "none" }}
26-
// `!important` needed to force an override of the user-agent
27-
borderColor="preBorder !important"
28-
_first={{
29-
borderInlineStart: "none",
30-
borderTop: "none",
31-
}}
32-
>
33-
{children}
34-
</VStack>
35-
)
36-
}
14+
const Cell = ({ children }: ChildOnlyProp) => (
15+
<VStack
16+
spacing={2}
17+
py={4}
18+
px={8}
19+
borderInlineStart={{ md: "1px" }}
20+
borderTop={{ base: "1px", md: "none" }}
21+
// `!important` needed to force an override of the user-agent
22+
borderColor="preBorder !important"
23+
_first={{
24+
borderInlineStart: "none",
25+
borderTop: "none",
26+
}}
27+
>
28+
{children}
29+
</VStack>
30+
)
3731

38-
const Value: React.FC<{ children: ReactNode; title: string }> = ({
39-
children,
40-
title,
41-
}) => {
42-
return (
43-
<Code
44-
title={title}
45-
fontWeight="bold"
46-
fontSize="2rem"
47-
background="none"
48-
color="primary.base"
49-
p={0}
50-
>
51-
{children}
52-
</Code>
53-
)
54-
}
32+
const Value = ({ children }: ChildOnlyProp) => (
33+
<Code
34+
fontWeight="bold"
35+
fontSize="2rem"
36+
background="none"
37+
color="primary.base"
38+
p={0}
39+
>
40+
{children}
41+
</Code>
42+
)
5543

56-
const Label: React.FC<{ children: ReactNode }> = ({ children }) => {
57-
return (
58-
<Flex alignItems="center" textTransform="uppercase" fontSize="sm">
59-
{children}
60-
</Flex>
61-
)
62-
}
44+
const Label = ({ children }: ChildOnlyProp) => (
45+
<Flex alignItems="center" textTransform="uppercase" fontSize="sm">
46+
{children}
47+
</Flex>
48+
)
6349

6450
// BeaconchainTooltip component
65-
interface BeaconchainTooltipProps {
66-
children: ReactNode
67-
}
68-
const BeaconchainTooltip: React.FC<BeaconchainTooltipProps> = ({
69-
children,
70-
}) => (
51+
const BeaconchainTooltip = ({ children }: ChildOnlyProp) => (
7152
<Tooltip content={children}>
7253
<Icon
7354
as={MdInfoOutline}
@@ -81,101 +62,35 @@ const BeaconchainTooltip: React.FC<BeaconchainTooltipProps> = ({
8162
</Tooltip>
8263
)
8364

84-
// Interfaces
85-
interface EthStoreResponse {
86-
data: {
87-
apr: number
88-
effective_balances_sum_wei: number
89-
}
90-
}
91-
92-
interface EpochResponse {
93-
data: {
94-
validatorscount: number
95-
}
96-
}
97-
9865
// StatsBox component
99-
const StakingStatsBox = () => {
66+
type StakingStatsBoxProps = {
67+
data: BeaconchainData
68+
}
69+
const StakingStatsBox = ({ data }: StakingStatsBoxProps) => {
10070
const { locale } = useRouter()
10171
const { t } = useTranslation("page-staking")
102-
/**
103-
* State variables:
104-
* - ZERO is default string, "0", representing loading state
105-
* - null is error state
106-
*/
107-
const [totalEth, setTotalEth] = useState<string | null>(ZERO)
108-
const [totalValidators, setTotalValidators] = useState<string | null>(ZERO)
109-
const [currentApr, setCurrentApr] = useState<string | null>(ZERO)
11072

111-
// TODO: Confirm data fetch approach/frequency
112-
useEffect(() => {
113-
const localeForStatsBoxNumbers = getLocaleForNumberFormat(locale! as Lang)
73+
const localeForStatsBoxNumbers = getLocaleForNumberFormat(locale! as Lang)
11474

115-
// Helper functions
116-
const formatInteger = (amount: number): string =>
117-
new Intl.NumberFormat(localeForStatsBoxNumbers).format(amount)
75+
// Helper functions
76+
const formatInteger = (amount: number): string =>
77+
new Intl.NumberFormat(localeForStatsBoxNumbers).format(amount)
11878

119-
const formatPercentage = (amount: number): string =>
120-
new Intl.NumberFormat(localeForStatsBoxNumbers, {
121-
style: "percent",
122-
minimumSignificantDigits: 2,
123-
maximumSignificantDigits: 2,
124-
}).format(amount)
79+
const formatPercentage = (amount: number): string =>
80+
new Intl.NumberFormat(localeForStatsBoxNumbers, {
81+
style: "percent",
82+
minimumSignificantDigits: 2,
83+
maximumSignificantDigits: 2,
84+
}).format(amount)
12585

126-
// API calls, data formatting, and state setting
127-
const base = "https://beaconcha.in"
128-
const { href: ethstore } = new URL("api/v1/ethstore/latest", base)
129-
const { href: epoch } = new URL("api/v1/epoch/latest", base)
130-
// Get total ETH staked and current APR from ethstore endpoint
131-
;(async () => {
132-
try {
133-
const ethStoreResponse = await fetch(ethstore)
134-
if (!ethStoreResponse.ok)
135-
throw new Error(
136-
"Network response from Beaconcha.in ETHSTORE was not ok"
137-
)
138-
const ethStoreResponseJson: EthStoreResponse =
139-
await ethStoreResponse.json()
140-
const {
141-
data: { apr, effective_balances_sum_wei },
142-
} = ethStoreResponseJson
143-
const totalEffectiveBalance: number = effective_balances_sum_wei * 1e-18
144-
const valueTotalEth = formatInteger(Math.floor(totalEffectiveBalance))
145-
const valueCurrentApr = formatPercentage(apr)
146-
setTotalEth(valueTotalEth)
147-
setCurrentApr(valueCurrentApr)
148-
} catch (error) {
149-
setTotalEth(null)
150-
setCurrentApr(null)
151-
}
152-
})()
153-
// Get total active validators from latest epoch endpoint
154-
;(async () => {
155-
try {
156-
const epochResponse = await fetch(epoch)
157-
if (!epochResponse.ok)
158-
throw new Error("Network response from Beaconcha.in EPOCH was not ok")
159-
const epochResponseJson: EpochResponse = await epochResponse.json()
160-
const {
161-
data: { validatorscount },
162-
} = epochResponseJson
163-
const valueTotalValidators = formatInteger(validatorscount)
164-
setTotalValidators(valueTotalValidators)
165-
} catch (error) {
166-
setTotalValidators(null)
167-
}
168-
})()
169-
}, [locale])
86+
const totalEth = formatInteger(data.totalEthStaked)
87+
const totalValidators = formatInteger(data.validatorscount)
88+
const currentApr = formatPercentage(data.apr)
17089

17190
return (
17291
<Flex direction={{ base: "column", md: "row" }}>
17392
<Cell>
174-
{totalEth === ZERO ? (
175-
<Spinner />
176-
) : (
177-
<Value title={totalEth ? "" : NA_ERROR}>{totalEth || NA_ERROR}</Value>
178-
)}
93+
<Value>{totalEth}</Value>
17994
<Label>
18095
{t("page-staking-stats-box-metric-1")}
18196
<BeaconchainTooltip>
@@ -186,13 +101,7 @@ const StakingStatsBox = () => {
186101
</Label>
187102
</Cell>
188103
<Cell>
189-
{totalValidators === ZERO ? (
190-
<Spinner />
191-
) : (
192-
<Value title={totalValidators ? "" : NA_ERROR}>
193-
{totalValidators || NA_ERROR}
194-
</Value>
195-
)}
104+
<Value>{totalValidators}</Value>
196105
<Label>
197106
{t("page-staking-stats-box-metric-2")}
198107
<BeaconchainTooltip>
@@ -203,13 +112,7 @@ const StakingStatsBox = () => {
203112
</Label>
204113
</Cell>
205114
<Cell>
206-
{currentApr === ZERO ? (
207-
<Spinner />
208-
) : (
209-
<Value title={currentApr ? "" : NA_ERROR}>
210-
{currentApr || NA_ERROR}
211-
</Value>
212-
)}
115+
<Value>{currentApr}</Value>
213116
<Label>
214117
{t("page-staking-stats-box-metric-3")}
215118
<BeaconchainTooltip>

src/lib/types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,23 @@ export interface LearningTool {
239239
export interface LearningToolsCardGridProps {
240240
category: Array<LearningTool>
241241
}
242+
243+
// Staking stats data fetching
244+
type Data<T> = {
245+
data: T
246+
}
247+
248+
export type EthStoreResponse = Data<{
249+
apr: number
250+
effective_balances_sum_wei: number
251+
}>
252+
253+
export type EpochResponse = Data<{
254+
validatorscount: number
255+
}>
256+
257+
export type BeaconchainData = {
258+
totalEthStaked: number
259+
validatorscount: number
260+
apr: number
261+
}

0 commit comments

Comments
 (0)