create app:
npx create-next-app app-name
run app:
yarn dev
yarn add typescript @types/react @types/node -D
yarn add date-fns
import format from 'date-fns/format';
import ptBR from 'date-fns/locale/pt-BR';
const currentDate = format(new Date(), 'EEEEEE d MMMM', {
locale: ptBR,
})
Caso exista um arquivo server.json onde tenham informações que simulam uma API é necessário instalar um pacote:
yarn add json-server -D
É necessário fazer uma alteração no package.json incluindo um novo script:
package.json
"scripts": {
"server": "json-server server.json -w -d 758 -p 3333"
},
Depois basta salvar o arquivo e rodar o servidor no terminal:
yarn server
- Roda no JavaScript do browser; index.js
import { useEffect } from 'react';
export default function Home(props) {
useEffect(() => {
fetch('http://localhost:3333/episodes')
.then((response) => response.json())
.then((data) => console.log(data))
}, [])
return (
<div>
<h1>Index</h1>
</div>
)
}
- Faz a requisição antes da página ser carregada; index.js
export default function Home(props) {
return (
<div>
<h1>Index</h1>
<p>{JSON.stringify(props.episodes)}</p>
</div>
)
}
export async function getServerSideProps() {
const response = await fetch('http://localhost:3333/episodes');
const data = await response.json()
return {
props: {
episodes: data,
}
}
}
- Faz a requisição para a API a cada 8 horas (revalidate: 60 * 60 * 8,);
- Funciona apenas em produção, para simular pode ser instalada uma build; index.js
export default function Home(props) {
return (
<div>
<h1>Index</h1>
<p>{JSON.stringify(props.episodes)}</p>
</div>
)
}
export async function getStaticProps() {
const response = await fetch('http://localhost:3333/episodes');
const data = await response.json()
return {
props: {
episodes: data,
},
revalidate: 60 * 60 * 8,
}
}
- Se o projeto estiver rodando, a execução deve ser parada;
- O servidor da API deve estar rodando;
yarn build
Agora podemos ver em qual modelo o nosso projeto estã sendo renderizado;
yarn start
É necessário fazer a instalação:
yarn add axios
- axios é uma biblioteca para fazer requisições HTTP assim como o fetch, mas o axios tem algumas funcionalidades a mais.
- entendimento por padrão do json;
- configurar uma url base, a url que se repete para todas as chamadas para a API;
Criar uma nova pasta dentro de src chamada 'services' e um arquivo api.ts ou js
Definição da url padrão: src/services/api.ts
import axios from 'axios';
export const api = axios.create({
baseURL: 'http://localhost:3333/',
})
Utilização da axios para a requisição src/pages/index.tsx
import { GetStaticProps } from 'next';
import { api } from '../services/api';
type Episodes = {
id: string;
title: string;
numbers: string;
}
type HomeProps = {
episodes: Episodes[];
}
export default function Home(props: HomeProps) {
return (
<div>
<h1>Index</h1>
<p>{JSON.stringify(props.episodes)}</p>
</div>
)
}
export const getStaticProps: GetStaticProps = async () => {
const { data } = await api.get('episodes', {
params: {
_limit: 12,
_sort: 'published_at',
_order: 'desc'
}
});
return {
props: {
episodes: data,
},
revalidate: 60 * 60 * 8,
}
}
A formatação de dados vindo de uma requisição de API devem ser formatados antes de serem renderizados,ou seja, antes de estarem no HTML para serem renderizados. O ideal é ser feito logo após obter o retorno dos dados.
Para criar um arquivo que forma a rota da aplicação é necessário criar uma pasta dentro da pasta 'pages', nesse caso será 'episodes', dentro dessa pasta deve ser criado um arquivo o qual o nome do arquivo fica dentro de [], ex: [slug].tsx (pode ser qualquer nome). Obs: Quando houver [] a rotá é gerada dinâmicamente. caso seja uma rota estática não é necessário os [].
Agora o caminho da url para essa pasta fica nesse caso (http://localhost:3000/episodes/qualquercoisa), então qual quer coisa que for depois de '/episode' vai direcionar para a página 'episode'. Para isso precisamos pegar o slug que irá depois de '/episode'; A implementação completa está em 'src/pages/episodes/[slug].tsx'
Quando a rota possui [] é necessário informar o método getStaticPaths:
- Quando o paths: [] - nenhuma página é gerada estáticamente no momento da build
- O que determina o comportamento de quando uma pessoa acessa a página de um episódio que não foi gerado estaticamente é o 'fallback'.
- Quando fallback: 'blocking' - a pessoa só vai ser direcionada para a tela quando os dados já estiverem carregado, (gera novas páginas conforme as pessoas vão acessando e faz a revalidação dos dados)
src/pages/episodes/[slug].tsx
export const getStaticPaths: GetStaticPaths = async () => {
return {
paths: [],
fallback: 'blocking'
}
}
- Quando o paths: path - Nesse caso o paths está recebendo dois ids que são responsáveis pelo caminho da página, o qual quando ocorrer o build ele já será gerado de forma estática, para quando a pessoa clicar ele abrir imediatamente, os outros paths vão sendo gerados conforme a pessoa vai abrindo novas páginas.
export const getStaticPaths: GetStaticPaths = async () => {
const { data } = await api.get('episodes', {
params: {
_limit: 2,
_sort: 'published_at',
_order: 'desc'
}
});
const path = data.map((episode) => {
return {
params: {
slug: episode.id
}
}
})
return {
paths: path,
fallback: 'blocking'
}
}
- Quando o fallback: false: - Se o usuário clicar em um episódio ele será redirecionado para o erro 404 pois nenhum episódio foi criado de forma estática no momento da build
export const getStaticPaths: GetStaticPaths = async () => {
return {
paths: [],
fallback: false,
}
}
- Quando o fallback: true - o método getStaticProps é executado pelo lado do client (browser), e ele demora um pouco para executar então os dados estarão vazios, pois a requisição do getStaticPaths demora um pouco para acontecer
export const getStaticPaths: GetStaticPaths = async () => {
return {
paths: [],
fallback: true,
}
}
Para resolver o erro pela demora:
import { useRouter } from 'next/router';
export default function Episode({episode}: EpisodeProps) {
//const router = useRouter();
//Para pegar a rota router.query.slug
const router = useRouter();
if(router.isFallback) {
return <p>Carregando</p>
}
}
yarn add rc-slider
src/components/Player/index.jsx
import Slider from 'rc-slider';
import 'rc-slider/assets/index.css';
<Slider
trackStyle={{backgroundColor: '#04d361'}}
railStyle={{ backgroundColor: '#9f75ff'}}
handleStyle={{ borderBlockColor: '#04d361', borderWidth: 4}}
/>
Aqui estamos recuperando a referência da tag 'audio':
- useRef: sempre inicia com null;
import { useContext, useEffect, useRef } from 'react';
export default function Player() {
const audioRef = useRef<HTMLAudioElement>(null);
const [progress, setProgress] = useState(0);
const {
episodeList,
currentEpisodeIndex,
isPlaying,
isLooping,
toggleLoop,
togglePlay,
toggleShuffle,
isShuffling,
setPlayingState,
playNext,
playPrevious,
hasPrevious,
hasNext,
clearPlayerState
} = usePlayer();
useEffect(() => {
if (!audioRef.current) return;
if (isPlaying) {
audioRef.current.play();
} else {
audioRef.current.pause();
}
}, [isPlaying]);
function setupProgressListener() {
audioRef.current.currentTime = 0;
audioRef.current.addEventListener('timeupdate', () => {
setProgress(Math.floor(audioRef.current.currentTime));
});
}
...
{ episode && (
<audio
src={episode.url}
ref={audioRef}
loop={isLooping}
autoPlay
onEnded={handleEpisodeEnded}
onPlay={() => setPlayingState(true)}
onPause={() => setPlayingState(false)}
onLoadedMetadata={setupProgressListener}
/>
)}
...
}
Missões: Deixar site responsivo; Transformar em pwa; tema Dark; Electron;