V1 presentation styling + API data fetching
This commit is contained in:
13
presentation/src/app/api/wordlist/route.tsx
Normal file
13
presentation/src/app/api/wordlist/route.tsx
Normal 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)
|
||||||
|
}
|
||||||
@@ -37,10 +37,9 @@ export default function Presentation() {
|
|||||||
function Introduction() {
|
function Introduction() {
|
||||||
return (
|
return (
|
||||||
<div className="main-presentation-comparation">
|
<div className="main-presentation-comparation">
|
||||||
<h3>Next</h3>
|
<h1>
|
||||||
<div>
|
NEXT<span>.JS</span>
|
||||||
<p>Nos da la posibilidad de renderizar desde el servidor</p>
|
</h1>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -50,17 +49,21 @@ function Rendering() {
|
|||||||
<div className="main-presentation-comparation">
|
<div className="main-presentation-comparation">
|
||||||
<h3>Rendering</h3>
|
<h3>Rendering</h3>
|
||||||
<div>
|
<div>
|
||||||
The rendering works is split into chunks: - By individual route
|
The rendering works is split into chunks:
|
||||||
segments - By React [Suspense
|
<ul>
|
||||||
boundaries](https://react.dev/reference/react/Suspense) (react
|
<li>- By individual route segments </li>
|
||||||
way of having a fallback while a components has finished
|
<li>- By React with Suspense boundaries </li>
|
||||||
loading) Each chunk is rendered in the server, then, on the
|
</ul>
|
||||||
client: 1. The HTML is used to immediately show fast preview. 2.
|
Each chunk is rendered in the server, then, on the client:
|
||||||
The server components rendered are inserted to update the DOM
|
<ul>
|
||||||
(components rendered in server with placeholders for client
|
<li>
|
||||||
components and props). 3. `JS` instructions are used to
|
1. The HTML is used to immediately show fast preview
|
||||||
[hydrate?](https://react.dev/reference/react-dom/client/hydrateRoot)
|
</li>
|
||||||
Client Components and make the application interactive.
|
<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>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -72,25 +75,9 @@ function Strategies() {
|
|||||||
<h3>Strategies</h3>
|
<h3>Strategies</h3>
|
||||||
<div>
|
<div>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>- Static rendering (default)</li>
|
||||||
- Static rendering (default): Good for static pages:
|
<li>- Dynamic rendering: rendered per user request</li>
|
||||||
Rendered in build time or in the background after data
|
<li>- Streaming: work is split into chunks and streamed</li>
|
||||||
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>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -100,33 +87,15 @@ function Strategies() {
|
|||||||
function Benefits() {
|
function Benefits() {
|
||||||
return (
|
return (
|
||||||
<div className="main-presentation-benefits">
|
<div className="main-presentation-benefits">
|
||||||
<h3>Beneficios</h3>
|
<h3>Benefits</h3>
|
||||||
<div>
|
<div>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>- Fetch data directly on the server</li>
|
||||||
- Fetch data directly on the server, performance and
|
<li>- Security: sensitive data is kept int the server</li>
|
||||||
load benefits.
|
<li>- Caching</li>
|
||||||
</li>
|
<li>- Bundle size reduced</li>
|
||||||
<li>
|
<li>- SEO </li>
|
||||||
- Security: sensitive data is kept int the server (API
|
<li>- Streaming</li>
|
||||||
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>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ export default function ClientWordList() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button onClick={fetchData}>Revalidate!</button>
|
<button onClick={fetchData}>Refetch!</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
import { revalidateFetchByTag } from '../actions'
|
import { revalidateFetchByTag } from '../actions'
|
||||||
|
|
||||||
export default function RevalidateButton() {
|
export default function RevalidateButton(props: { tag?: string }) {
|
||||||
return (
|
return (
|
||||||
<button onClick={() => revalidateFetchByTag('wordlist')}>
|
<button onClick={() => revalidateFetchByTag(props.tag ?? 'wordlist')}>
|
||||||
Revalidate!
|
Revalidate!
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import NextAPIWordList from './components/apiside-word-list'
|
||||||
import ClientWordList from './components/clientside-word-list'
|
import ClientWordList from './components/clientside-word-list'
|
||||||
import GoBackButton from './components/go-back-button'
|
import GoBackButton from './components/go-back-button'
|
||||||
import ServerWordList from './components/serverside-word-list'
|
import ServerWordList from './components/serverside-word-list'
|
||||||
@@ -7,6 +8,7 @@ export default function WordList() {
|
|||||||
<div className="data-fetching">
|
<div className="data-fetching">
|
||||||
<ServerWordList />
|
<ServerWordList />
|
||||||
<ClientWordList />
|
<ClientWordList />
|
||||||
|
<NextAPIWordList />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const WORDLIST_API_URL =
|
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 = {
|
export type WordElement = {
|
||||||
word: string
|
word: string
|
||||||
@@ -11,8 +11,8 @@ export type WordListRepsonse = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fetch("https://...", { cache: "no-store" });
|
// fetch("https://...", { cache: "no-store" });
|
||||||
export const fetchWordlist = async () => {
|
export const fetchWordlist = async (url?: string) => {
|
||||||
const wordList: WordListRepsonse = await fetch(WORDLIST_API_URL, {
|
const wordList: WordListRepsonse = await fetch(url ?? WORDLIST_API_URL, {
|
||||||
next: { tags: ['wordlist'] },
|
next: { tags: ['wordlist'] },
|
||||||
}).then((response) => response.json())
|
}).then((response) => response.json())
|
||||||
|
|
||||||
|
|||||||
@@ -6,16 +6,18 @@
|
|||||||
|
|
||||||
.data-fetching {
|
.data-fetching {
|
||||||
display: grid;
|
display: grid;
|
||||||
padding: 20rem;
|
padding: 0 10rem;
|
||||||
gap: 8rem;
|
gap: 5rem;
|
||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
background-color: #e9ecef;
|
background-color: #e9ecef;
|
||||||
border-radius: 1.5rem;
|
border-radius: 1.5rem;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
min-height: 15rem;
|
height: 15rem;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -85,10 +87,40 @@
|
|||||||
|
|
||||||
> h3 {
|
> h3 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 2.8rem;
|
font-size: 3rem;
|
||||||
font-weight: 100;
|
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 {
|
&-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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user