V1 presentation styling + API data fetching

This commit is contained in:
Daniel Heras Quesada
2024-01-17 09:25:35 +01:00
parent 3869d67172
commit e2c2e70e19
8 changed files with 119 additions and 70 deletions

View File

@@ -0,0 +1,13 @@
import { NextResponse } from 'next/server'
import { fetchWordlist } from 'src/app/word-list/utils'
export async function GET(request: Request) {
// const { searchParams } = new URL(request.url)
// const url = searchParams.get('url')
// console.log(url)
const res = await fetchWordlist()
console.log('API Data fetch is done', res)
return NextResponse.json(res)
}

View File

@@ -37,10 +37,9 @@ export default function Presentation() {
function Introduction() {
return (
<div className="main-presentation-comparation">
<h3>Next</h3>
<div>
<p>Nos da la posibilidad de renderizar desde el servidor</p>
</div>
<h1>
NEXT<span>.JS</span>
</h1>
</div>
)
}
@@ -50,17 +49,21 @@ function Rendering() {
<div className="main-presentation-comparation">
<h3>Rendering</h3>
<div>
The rendering works is split into chunks: - By individual route
segments - By React [Suspense
boundaries](https://react.dev/reference/react/Suspense) (react
way of having a fallback while a components has finished
loading) Each chunk is rendered in the server, then, on the
client: 1. The HTML is used to immediately show fast preview. 2.
The server components rendered are inserted to update the DOM
(components rendered in server with placeholders for client
components and props). 3. `JS` instructions are used to
[hydrate?](https://react.dev/reference/react-dom/client/hydrateRoot)
Client Components and make the application interactive.
The rendering works is split into chunks:
<ul>
<li>- By individual route segments </li>
<li>- By React with Suspense boundaries </li>
</ul>
Each chunk is rendered in the server, then, on the client:
<ul>
<li>
1. The HTML is used to immediately show fast preview
</li>
<li>2. The components are inserted to update the DOM</li>
<li>
3. JS instructions are used to hydrate client components
</li>
</ul>
</div>
</div>
)
@@ -72,25 +75,9 @@ function Strategies() {
<h3>Strategies</h3>
<div>
<ul>
<li>
- Static rendering (default): Good for static pages:
Rendered in build time or in the background after data
revalidation
</li>
<li>
- Security: sensitive data is kept int the server (API
keys and tokens)
</li>
<li>
- Dynamic rendering: rendered per user request, Next
uses this type of rendering automatically when discovers
a dynamic function (`cookies()`, `headers()`,
`userSearchParams()`).
</li>
<li>
- Streaming: work is split into chunks and streamed as
they become ready so the load is progressive.
</li>
<li>- Static rendering (default)</li>
<li>- Dynamic rendering: rendered per user request</li>
<li>- Streaming: work is split into chunks and streamed</li>
</ul>
</div>
</div>
@@ -100,33 +87,15 @@ function Strategies() {
function Benefits() {
return (
<div className="main-presentation-benefits">
<h3>Beneficios</h3>
<h3>Benefits</h3>
<div>
<ul>
<li>
- Fetch data directly on the server, performance and
load benefits.
</li>
<li>
- Security: sensitive data is kept int the server (API
keys and tokens)
</li>
<li>
- Caching: results can be cached to improve performance
between users.
</li>
<li>
- Bundle size: will be reduced as part of the
application will reside in the server.
</li>
<li>
- SEO: because the pages will be rendered the search
engine bots will make good use of it.
</li>
<li>
- Streaming: to split the rendering into chunks and
stream them as they become ready.
</li>
<li>- Fetch data directly on the server</li>
<li>- Security: sensitive data is kept int the server</li>
<li>- Caching</li>
<li>- Bundle size reduced</li>
<li>- SEO </li>
<li>- Streaming</li>
</ul>
</div>
</div>

View File

@@ -0,0 +1,33 @@
'use client'
import { useEffect, useState } from 'react'
import { WordListRepsonse, fetchWordlist } from '../utils'
export default function NextAPIWordList() {
const [data, setData] = useState<WordListRepsonse>()
const fetchData = () =>
fetchWordlist('http://localhost:3001/api/wordlist').then((response) =>
setData(response)
)
useEffect(() => {
fetchData()
}, [])
return (
<div className="data-fetching-client">
<h3>
Wordlist fetched in the <span>API</span>
</h3>
<div>
{data?.wordList.map((word) => (
<span>{word.word}</span>
))}
</div>
<div>
<button onClick={fetchData}>Refetch!</button>
</div>
</div>
)
}

View File

@@ -24,7 +24,7 @@ export default function ClientWordList() {
))}
</div>
<div>
<button onClick={fetchData}>Revalidate!</button>
<button onClick={fetchData}>Refetch!</button>
</div>
</div>
)

View File

@@ -2,9 +2,9 @@
import { revalidateFetchByTag } from '../actions'
export default function RevalidateButton() {
export default function RevalidateButton(props: { tag?: string }) {
return (
<button onClick={() => revalidateFetchByTag('wordlist')}>
<button onClick={() => revalidateFetchByTag(props.tag ?? 'wordlist')}>
Revalidate!
</button>
)

View File

@@ -1,3 +1,4 @@
import NextAPIWordList from './components/apiside-word-list'
import ClientWordList from './components/clientside-word-list'
import GoBackButton from './components/go-back-button'
import ServerWordList from './components/serverside-word-list'
@@ -7,6 +8,7 @@ export default function WordList() {
<div className="data-fetching">
<ServerWordList />
<ClientWordList />
<NextAPIWordList />
</div>
)
}

View File

@@ -1,5 +1,5 @@
const WORDLIST_API_URL =
'http://localhost:3000/words/es?complexity=medium&howMany=30'
'http://localhost:3000/words/es?complexity=medium&howMany=20'
export type WordElement = {
word: string
@@ -11,8 +11,8 @@ export type WordListRepsonse = {
}
// fetch("https://...", { cache: "no-store" });
export const fetchWordlist = async () => {
const wordList: WordListRepsonse = await fetch(WORDLIST_API_URL, {
export const fetchWordlist = async (url?: string) => {
const wordList: WordListRepsonse = await fetch(url ?? WORDLIST_API_URL, {
next: { tags: ['wordlist'] },
}).then((response) => response.json())

View File

@@ -6,16 +6,18 @@
.data-fetching {
display: grid;
padding: 20rem;
gap: 8rem;
padding: 0 10rem;
gap: 5rem;
background-color: #fafafa;
grid-template-columns: 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr;
align-items: center;
height: 100vh;
> * {
background-color: #e9ecef;
border-radius: 1.5rem;
padding: 2rem;
min-height: 15rem;
height: 15rem;
display: flex;
flex-direction: column;
@@ -85,10 +87,40 @@
> h3 {
text-align: center;
font-size: 2.8rem;
font-size: 3rem;
font-weight: 100;
}
div > ul {
padding: 0 8rem;
> li {
font-weight: 200;
font-size: 1.4rem;
margin: 10px 0;
}
}
}
font-size: 1.1rem;
font-weight: 200;
&-comparation {
h1 {
padding-top: 5rem;
text-align: center;
font-size: 8rem;
font-weight: 100;
> span {
font-size: 3rem;
}
}
}
&-benefits {
div > ul {
padding: 0 15rem;
> li {
font-size: 1.2rem;
}
}
}
}