Skip to content

Commit 496cf62

Browse files
committed
chore(playground): add dynamic import to improve performace
1 parent f00cbbe commit 496cf62

1 file changed

Lines changed: 139 additions & 128 deletions

File tree

src/components/Playground.tsx

Lines changed: 139 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
'use client'
22

3-
import React, { useState, useEffect } from 'react'
3+
import React, { useState, useEffect, useMemo } from 'react'
44
import { useLocalStorage } from '@/lib/hooks/useLocalStorage'
55
import { useCodeRunner } from '@/lib/hooks/useCodeRunner'
66
import { useTheme } from '@/lib/theme/ThemeProvider'
7-
import CodeEditor from './CodeEditor'
8-
import Console from './Console'
9-
import NetworkSelector from './NetworkSelector'
10-
import ExampleSelector from './ExampleSelector'
7+
import dynamic from 'next/dynamic'
118
import { DEFAULT_NETWORK, NETWORKS } from '@/lib/constants/networks'
129
import { DEFAULT_EXAMPLE, EXAMPLES } from '@/lib/constants/examples'
1310
import ActionButton from './ui/ActionButton'
@@ -21,56 +18,51 @@ import NetworkBadge from './ui/NetworkBadge'
2118
import Card from './ui/Card'
2219
import Badge from './ui/Badge'
2320

21+
22+
const CodeEditor = dynamic(() => import('./CodeEditor'), { ssr: false })
23+
const Console = dynamic(() => import('./Console'), { ssr: false })
24+
const NetworkSelector = dynamic(() => import('./NetworkSelector'), { ssr: false })
25+
const ExampleSelector = dynamic(() => import('./ExampleSelector'), { ssr: false })
26+
2427
export default function Playground() {
2528
const { isDarkTheme, isLoaded, currentNetworkId, setCurrentNetworkId } = useTheme()
26-
27-
2829
const [selectedNetworkId, setSelectedNetworkId] = useLocalStorage<string>('selectedNetwork', DEFAULT_NETWORK.id)
2930
const [selectedExampleId, setSelectedExampleId] = useLocalStorage<string>('selectedExample', DEFAULT_EXAMPLE.id)
30-
31-
3231
const [selectedNetwork, setSelectedNetwork] = useState<Network>(DEFAULT_NETWORK)
3332
const [selectedExample, setSelectedExample] = useState<Example>(DEFAULT_EXAMPLE)
34-
35-
3633
const [sidebarCollapsed, setSidebarCollapsed] = useState(false)
37-
38-
34+
const [isMounted, setIsMounted] = useState(false)
3935
const { code, outputs, isRunning, updateCode, setExampleCode, runCode, clearOutput } = useCodeRunner()
4036

41-
4237
useEffect(() => {
38+
setIsMounted(true)
4339
const checkMobile = () => {
4440
setSidebarCollapsed(window.innerWidth < 1024)
4541
}
4642

47-
checkMobile()
43+
checkMobile()
4844
window.addEventListener('resize', checkMobile)
4945

5046
return () => window.removeEventListener('resize', checkMobile)
5147
}, [])
5248

53-
5449
useEffect(() => {
5550
const network = NETWORKS[selectedNetworkId] || DEFAULT_NETWORK
5651
setSelectedNetwork(network)
5752
setCurrentNetworkId(selectedNetworkId)
5853
}, [selectedNetworkId, setCurrentNetworkId])
5954

60-
6155
useEffect(() => {
6256
const example = EXAMPLES.find(e => e.id === selectedExampleId) || DEFAULT_EXAMPLE
6357
setSelectedExample(example)
6458
}, [selectedExampleId])
6559

66-
6760
useEffect(() => {
6861
if (selectedExample && selectedNetwork) {
6962
setExampleCode(selectedExample, selectedNetwork)
7063
}
7164
}, [selectedExample, selectedNetwork, setExampleCode])
7265

73-
7466
const getNetworkColor = () => {
7567
const networkId = NETWORKS[currentNetworkId]?.id as keyof typeof colorMap;
7668

@@ -84,7 +76,6 @@ export default function Playground() {
8476
return colorMap[networkId] || colorMap['polkadot']
8577
}
8678

87-
8879
const getDifficultyColor = (level: 'beginner' | 'intermediate' | 'advanced') => {
8980
const colorMap = {
9081
'beginner': '#22C55E',
@@ -94,93 +85,77 @@ export default function Playground() {
9485
return colorMap[level] || colorMap['beginner']
9586
}
9687

97-
98-
if (!isLoaded) {
99-
return (
100-
<div className="h-screen w-screen flex items-center justify-center">
101-
<div className="animate-spin mr-2">
102-
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
103-
<line x1="12" y1="2" x2="12" y2="6"></line>
104-
<line x1="12" y1="18" x2="12" y2="22"></line>
105-
<line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line>
106-
<line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line>
107-
<line x1="2" y1="12" x2="6" y2="12"></line>
108-
<line x1="18" y1="12" x2="22" y2="12"></line>
109-
<line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>
110-
<line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>
111-
</svg>
112-
</div>
113-
<div className="flex flex-col items-center">
114-
<span>Loading Polkadot API Playground...</span>
115-
<span className="text-xs mt-1 opacity-70">Preparing your development environment</span>
116-
</div>
117-
</div>
118-
);
119-
}
120-
121-
122-
const SidebarContent = () => (
123-
<div className="flex flex-col gap-4">
124-
<NetworkSelector
125-
networks={Object.values(NETWORKS)}
126-
selectedNetworkId={selectedNetwork.id}
127-
onNetworkChange={setSelectedNetworkId}
128-
/>
88+
const SidebarContent = useMemo(() => {
89+
return () => (
90+
<div className="flex flex-col gap-4">
91+
<NetworkSelector
92+
networks={Object.values(NETWORKS)}
93+
selectedNetworkId={selectedNetwork.id}
94+
onNetworkChange={setSelectedNetworkId}
95+
/>
12996

130-
<ExampleSelector
131-
examples={EXAMPLES}
132-
selectedExampleId={selectedExample.id}
133-
onExampleChange={setSelectedExampleId}
134-
/>
97+
<ExampleSelector
98+
examples={EXAMPLES}
99+
selectedExampleId={selectedExample.id}
100+
onExampleChange={setSelectedExampleId}
101+
/>
135102

136-
<InfoPanel network={selectedNetwork} example={selectedExample} />
103+
<InfoPanel network={selectedNetwork} example={selectedExample} />
137104

138-
<Card>
139-
<div className="flex flex-col gap-3">
140-
<ActionButton
141-
onClick={() => runCode(selectedExample, selectedNetwork)}
142-
disabled={isRunning}
143-
isPrimary={true}
144-
icon="play"
145-
fullWidth
146-
size="lg"
147-
>
148-
{isRunning ? 'Running...' : 'Run Code'}
149-
</ActionButton>
105+
<Card>
106+
<div className="flex flex-col gap-3">
107+
<ActionButton
108+
onClick={() => runCode(selectedExample, selectedNetwork)}
109+
disabled={isRunning}
110+
isPrimary={true}
111+
icon="play"
112+
fullWidth
113+
size="lg"
114+
>
115+
{isRunning ? 'Running...' : 'Run Code'}
116+
</ActionButton>
150117

151-
<ActionButton
152-
onClick={clearOutput}
153-
disabled={isRunning || outputs.length === 0}
154-
icon="trash"
155-
fullWidth
156-
size="md"
157-
>
158-
Clear Console
159-
</ActionButton>
118+
<ActionButton
119+
onClick={clearOutput}
120+
disabled={isRunning || outputs.length === 0}
121+
icon="trash"
122+
fullWidth
123+
size="md"
124+
>
125+
Clear Console
126+
</ActionButton>
160127

161-
<div className="flex items-center justify-center mt-2 text-xs" style={{
162-
color: isDarkTheme ? 'rgba(255,255,255,0.5)' : 'rgba(0,0,0,0.5)'
163-
}}>
164-
<span>Example difficulty: </span>
165-
<span className="ml-1 font-medium" style={{
166-
color: getDifficultyColor(selectedExample.level)
128+
<div className="flex items-center justify-center mt-2 text-xs" style={{
129+
color: isDarkTheme ? 'rgba(255,255,255,0.5)' : 'rgba(0,0,0,0.5)'
167130
}}>
168-
{selectedExample.level.toUpperCase()}
169-
</span>
131+
<span>Example difficulty: </span>
132+
<span className="ml-1 font-medium" style={{
133+
color: getDifficultyColor(selectedExample.level)
134+
}}>
135+
{selectedExample.level.toUpperCase()}
136+
</span>
137+
</div>
170138
</div>
171-
</div>
172-
</Card>
173-
</div>
174-
)
139+
</Card>
140+
</div>
141+
);
142+
}, [
143+
selectedNetwork,
144+
selectedExampleId,
145+
selectedExample,
146+
isDarkTheme,
147+
isRunning,
148+
outputs.length,
149+
runCode,
150+
clearOutput
151+
]);
175152

176-
177153
const NetworkIndicator = () => (
178154
<div className="fixed top-0 left-0 right-0 h-1 z-50 opacity-80" style={{
179155
background: `linear-gradient(90deg, ${getNetworkColor()} 0%, rgba(255,255,255,0) 100%)`
180156
}} />
181157
)
182158

183-
184159
const SidebarToggle = () => (
185160
<div className="fixed bottom-6 right-6 lg:hidden z-30">
186161
<button
@@ -209,46 +184,83 @@ export default function Playground() {
209184
</div>
210185
)
211186

212-
213-
const MainContent = () => (
214-
<div className="flex flex-col gap-4 h-full">
215-
<TutorialPanel example={selectedExample} network={selectedNetwork} />
187+
const MainContent = useMemo(() => {
188+
return () => (
189+
<div className="flex flex-col gap-4 h-full">
190+
<TutorialPanel example={selectedExample} network={selectedNetwork} />
216191

217-
<Card className="flex-1" header={
218-
<div className="flex justify-between items-center">
219-
<div className="font-medium flex items-center">
220-
<span>Code Editor</span>
221-
<Badge variant="default" className="ml-2">
222-
TypeScript
223-
</Badge>
192+
<Card className="flex-1" header={
193+
<div className="flex justify-between items-center">
194+
<div className="font-medium flex items-center">
195+
<span>Code Editor</span>
196+
<Badge variant="default" className="ml-2">
197+
TypeScript
198+
</Badge>
199+
</div>
200+
<div className="text-xs px-2 py-1 rounded flex items-center" style={{
201+
backgroundColor: isDarkTheme ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'
202+
}}>
203+
<NetworkBadge network={selectedNetwork} size="sm" showName={false} className="mr-2" />
204+
{selectedExample.name}
205+
</div>
224206
</div>
225-
<div className="text-xs px-2 py-1 rounded flex items-center" style={{
226-
backgroundColor: isDarkTheme ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'
227-
}}>
228-
<NetworkBadge network={selectedNetwork} size="sm" showName={false} className="mr-2" />
229-
{selectedExample.name}
207+
}>
208+
<div className="-m-4">
209+
{isMounted && (
210+
<CodeEditor
211+
code={code}
212+
onChange={updateCode}
213+
disabled={isRunning}
214+
height="400px"
215+
/>
216+
)}
230217
</div>
231-
</div>
232-
}>
233-
<div className="-m-4">
234-
<CodeEditor
235-
code={code}
236-
onChange={updateCode}
237-
disabled={isRunning}
238-
height="400px"
218+
</Card>
219+
220+
{isMounted && (
221+
<Console
222+
outputs={outputs}
223+
onClear={clearOutput}
239224
/>
240-
</div>
241-
</Card>
225+
)}
226+
</div>
227+
);
228+
}, [
229+
selectedExample,
230+
selectedNetwork,
231+
isDarkTheme,
232+
code,
233+
isRunning,
234+
outputs,
235+
updateCode,
236+
clearOutput,
237+
isMounted
238+
]);
242239

243-
<Console
244-
outputs={outputs}
245-
onClear={clearOutput}
246-
/>
247-
</div>
248-
)
240+
if (!isLoaded || !isMounted) {
241+
return (
242+
<div className="h-screen w-screen flex items-center justify-center">
243+
<div className="animate-spin mr-2">
244+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
245+
<line x1="12" y1="2" x2="12" y2="6"></line>
246+
<line x1="12" y1="18" x2="12" y2="22"></line>
247+
<line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line>
248+
<line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line>
249+
<line x1="2" y1="12" x2="6" y2="12"></line>
250+
<line x1="18" y1="12" x2="22" y2="12"></line>
251+
<line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>
252+
<line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>
253+
</svg>
254+
</div>
255+
<div className="flex flex-col items-center">
256+
<span>Loading Polkadot API Playground...</span>
257+
<span className="text-xs mt-1 opacity-70">Preparing your development environment</span>
258+
</div>
259+
</div>
260+
);
261+
}
249262

250-
251-
if (window.innerWidth < 1024) {
263+
if (typeof window !== 'undefined' && window.innerWidth < 1024) {
252264
return (
253265
<>
254266
<NetworkIndicator />
@@ -269,7 +281,6 @@ export default function Playground() {
269281
)
270282
}
271283

272-
273284
return (
274285
<>
275286
<NetworkIndicator />

0 commit comments

Comments
 (0)