A tool for people curious about the React Server Components protocol rscexplorer.dev/
rsc react
35
fork

Configure Feed

Select the types of activity you want to include in your feed.

tweak examples

+77 -77
+1 -1
README.md
··· 16 16 - [Async Component](https://rscexplorer.dev/?s=async) 17 17 - [Counter](https://rscexplorer.dev/?s=counter) 18 18 - [Form Action](https://rscexplorer.dev/?s=form) 19 - - [Pagination](https://rscexplorer.dev/?s=pagination) 20 19 - [Router Refresh](https://rscexplorer.dev/?s=refresh) 20 + - [Pagination](https://rscexplorer.dev/?s=pagination) 21 21 - [Error Handling](https://rscexplorer.dev/?s=errors) 22 22 - [Client Reference](https://rscexplorer.dev/?s=clientref) 23 23 - [Bound Actions](https://rscexplorer.dev/?s=bound)
+74 -74
src/client/samples.ts
··· 111 111 ) 112 112 }`, 113 113 }, 114 + refresh: { 115 + name: "Router Refresh", 116 + server: `import { Suspense } from 'react' 117 + import { Timer, Router } from './client' 118 + 119 + export default function App() { 120 + return ( 121 + <div> 122 + <h1>Router Refresh</h1> 123 + <p style={{ marginBottom: 12, color: '#666' }}> 124 + Client state persists across server navigations 125 + </p> 126 + <Suspense fallback={<p>Loading...</p>}> 127 + <Router initial={renderPage()} refreshAction={renderPage} /> 128 + </Suspense> 129 + </div> 130 + ) 131 + } 132 + 133 + async function renderPage() { 134 + 'use server' 135 + return <ColorTimer /> 136 + } 137 + 138 + async function ColorTimer() { 139 + await new Promise(r => setTimeout(r, 300)) 140 + const hue = Math.floor(Math.random() * 360) 141 + return <Timer color={\`hsl(\${hue}, 70%, 85%)\`} /> 142 + }`, 143 + client: `'use client' 144 + 145 + import { useState, useEffect, useTransition, use } from 'react' 146 + 147 + export function Timer({ color }) { 148 + const [seconds, setSeconds] = useState(0) 149 + 150 + useEffect(() => { 151 + const id = setInterval(() => setSeconds(s => s + 1), 1000) 152 + return () => clearInterval(id) 153 + }, []) 154 + 155 + return ( 156 + <div style={{ 157 + background: color, 158 + padding: 24, 159 + borderRadius: 8, 160 + textAlign: 'center' 161 + }}> 162 + <p style={{ fontFamily: 'monospace', fontSize: 32, margin: 0 }}>Timer: {seconds}s</p> 163 + </div> 164 + ) 165 + } 166 + 167 + export function Router({ initial, refreshAction }) { 168 + const [contentPromise, setContentPromise] = useState(initial) 169 + const [isPending, startTransition] = useTransition() 170 + const content = use(contentPromise) 171 + 172 + const refresh = () => { 173 + startTransition(() => { 174 + setContentPromise(refreshAction()) 175 + }) 176 + } 177 + 178 + return ( 179 + <div style={{ opacity: isPending ? 0.7 : 1 }}> 180 + {content} 181 + <button onClick={refresh} disabled={isPending} style={{ marginTop: 12 }}> 182 + {isPending ? 'Refreshing...' : 'Refresh'} 183 + </button> 184 + </div> 185 + ) 186 + }`, 187 + }, 114 188 pagination: { 115 189 name: "Pagination", 116 190 server: `import { Suspense } from 'react' ··· 204 278 } 205 279 206 280 return { items, hasMore, formAction, isPending } 207 - }`, 208 - }, 209 - refresh: { 210 - name: "Router Refresh", 211 - server: `import { Suspense } from 'react' 212 - import { Timer, Router } from './client' 213 - 214 - export default function App() { 215 - return ( 216 - <div> 217 - <h1>Router Refresh</h1> 218 - <p style={{ marginBottom: 12, color: '#666' }}> 219 - Client state persists across server navigations 220 - </p> 221 - <Suspense fallback={<p>Loading...</p>}> 222 - <Router initial={renderPage()} refreshAction={renderPage} /> 223 - </Suspense> 224 - </div> 225 - ) 226 - } 227 - 228 - async function renderPage() { 229 - 'use server' 230 - return <ColorTimer /> 231 - } 232 - 233 - async function ColorTimer() { 234 - await new Promise(r => setTimeout(r, 300)) 235 - const hue = Math.floor(Math.random() * 360) 236 - return <Timer color={\`hsl(\${hue}, 70%, 85%)\`} /> 237 - }`, 238 - client: `'use client' 239 - 240 - import { useState, useEffect, useTransition, use } from 'react' 241 - 242 - export function Timer({ color }) { 243 - const [seconds, setSeconds] = useState(0) 244 - 245 - useEffect(() => { 246 - const id = setInterval(() => setSeconds(s => s + 1), 1000) 247 - return () => clearInterval(id) 248 - }, []) 249 - 250 - return ( 251 - <div style={{ 252 - background: color, 253 - padding: 24, 254 - borderRadius: 8, 255 - textAlign: 'center' 256 - }}> 257 - <p style={{ fontFamily: 'monospace', fontSize: 32, margin: 0 }}>Timer: {seconds}s</p> 258 - </div> 259 - ) 260 - } 261 - 262 - export function Router({ initial, refreshAction }) { 263 - const [contentPromise, setContentPromise] = useState(initial) 264 - const [isPending, startTransition] = useTransition() 265 - const content = use(contentPromise) 266 - 267 - const refresh = () => { 268 - startTransition(() => { 269 - setContentPromise(refreshAction()) 270 - }) 271 - } 272 - 273 - return ( 274 - <div style={{ opacity: isPending ? 0.6 : 1, transition: 'opacity 0.2s' }}> 275 - {content} 276 - <button onClick={refresh} disabled={isPending} style={{ marginTop: 12 }}> 277 - {isPending ? 'Refreshing...' : 'Refresh'} 278 - </button> 279 - </div> 280 - ) 281 281 }`, 282 282 }, 283 283 errors: {
+2 -2
src/client/ui/App.tsx
··· 84 84 }; 85 85 } 86 86 87 - const defaultSample = SAMPLES.pagination as Sample; 87 + const defaultSample = SAMPLES.refresh as Sample; 88 88 return { 89 89 server: defaultSample.server, 90 90 client: defaultSample.client, 91 - sampleKey: "pagination", 91 + sampleKey: "refresh", 92 92 }; 93 93 } 94 94