Muitas vezes, no desenvolvimento de aplicativos Web, precisamos buscar grandes conjuntos de registros de dados de um servidor remoto, API ou banco de dados. Se você estiver construindo um sistema de pagamento, por exemplo, ele poderia estar buscando milhares de transações. Se é um aplicativo de mídia social, poderia estar buscando muitos comentários de usuários, perfis ou atividades. Seja qual for o caso, existem diversas soluções para apresentar os dados de uma maneira que não sobrecarregue o usuário final que esteja interagindo com o aplicativo.
Um dos métodos para lidar com grandes conjuntos de dados é usar a paginação. A paginação funciona de maneira eficaz quando o tamanho do conjunto de dados (o número total de registros no conjunto de dados) é conhecido previamente. Além disso, apenas a quantidade de dados necessária é carregada do conjunto total de dados com base na interação de usuários finais com o controle de paginação. Essa é a técnica usada na exibição de resultados na pesquisa do Google.
Neste tutorial, veremos como criar um componente de paginação personalizada com o React para paginar grandes conjuntos de dados. Será construida uma visualização paginada dos países no mundo — um conjunto de dados com um tamanho conhecido.
Aqui está uma demonstração daquilo que você construirá neste tutorial:
Para completar este tutorial, você precisará de:
npm < 5.2
, pode ser necessário instalar o create-react-app
como uma dependência global.Este tutorial foi verificado com o Node v14.2.0, npm
v6.14.4, react
v16.13.1 e react-scripts
v3.4.1.
Incie um novo aplicativo React usando o comando create-react-app
. Sinta-se à vontade para dar o nome que preferir ao aplicativo, mas este tutorial irá chamá-lo de react-pagination
:
- npx create-react-app react-pagination
Em seguida, você irá instalar as dependências necessárias para o seu aplicativo. Primeiro, use a janela do terminal para navegar até o diretório do projeto:
- cd react-pagination
Execute o seguinte comando para instalar as dependências necessárias:
- npm install bootstrap@4.1.0 prop-types@15.6.1 react-flags@0.1.13 countries-api@2.0.1 node-sass@4.14.1
Isso irá instalar o bootstrap
, prop-types
, react-flags
, countries-api
e o node-sass
.
O pacote bootstrap
foi instalado como uma dependência para o seu aplicativo, pois você precisará de um estilo padrão. Além disso, você também irá utilizar estilos do componente pagination
do Bootstrap.
Para incluir o Bootstrap no aplicativo, edite o arquivo src/index.js
:
- nano src/index.js
E adicione a linha a seguir antes das outras declarações import
:
import "bootstrap/dist/css/bootstrap.min.css";
Agora, o estilo do Bootstrap estará disponível em todo o seu aplicativo.
Além disso, o react-flags
foi instalado como uma dependência para o seu aplicativo. Para obter acesso aos ícones de bandeira a partir do seu aplicativo, será necessário copiar as imagens dos ícones para o diretório public
do seu aplicativo.
Crie um diretório img
em seu diretório public
:
- mkdir public/img
Copie os arquivos de imagem em flags
para img
:
- cp -R node_modules/react-flags/vendor/flags public/img
Isso gera uma cópia de todas as imagens do react-flag
para o seu aplicativo.
Agora que você incluiu algumas dependências, inicie o aplicativo executando o comando a seguir com o npm
a partir do diretório de projeto react-pagination
:
- npm start
Agora que o aplicativo foi iniciado, o desenvolvimento pode começar. Observe que uma guia do navegador foi aberta para você com a funcionalidade de carregamento dinâmico para manter tudo sincronizado à medida em que você avança no desenvolvimento.
Neste ponto, a visualização do aplicativo deve se assemelhar à seguinte captura de tela:
Agora, tudo está pronto para começarmos a criar os componentes.
ContryCard
Neste passo, você irá criar o componente CountryCard
. O componente CountryCard
processa o nome, região e bandeira de um determinado país.
Primeiro, vamos criar um diretório components
dentro do diretório src
:
- mkdir src/components
Em seguida, crie um novo arquivo CountryCard.js
dentro do diretório src/components
:
- nano src/components/CountryCard.js
E adicione o seguinte código a ele:
import React from 'react';
import PropTypes from 'prop-types';
import Flag from 'react-flags';
const CountryCard = props => {
const {
cca2: code2 = '', region = null, name = {}
} = props.country || {};
return (
<div className="col-sm-6 col-md-4 country-card">
<div className="country-card-container border-gray rounded border mx-2 my-3 d-flex flex-row align-items-center p-0 bg-light">
<div className="h-100 position-relative border-gray border-right px-2 bg-white rounded-left">
<Flag country={code2} format="png" pngSize={64} basePath="./img/flags" className="d-block h-100" />
</div>
<div className="px-3">
<span className="country-name text-dark d-block font-weight-bold">{ name.common }</span>
<span className="country-region text-secondary text-uppercase">{ region }</span>
</div>
</div>
</div>
)
}
CountryCard.propTypes = {
country: PropTypes.shape({
cca2: PropTypes.string.isRequired,
region: PropTypes.string.isRequired,
name: PropTypes.shape({
common: PropTypes.string.isRequired
}).isRequired
}).isRequired
};
export default CountryCard;
O componente CountryCard
exige uma propriedade country
que contém os dados sobre o país a ser processado. Como visto nas propTypes
para o componente CountryCard
, a propriedade country
deve conter os seguintes dados:
cca2
- Código de 2 dígitos do paísregion
- a região do país (por exemplo, “África”)name.common
- o nome comum do país (por exemplo, “Nigéria”)Aqui está um objeto de país de amostra:
{
cca2: "NG",
region: "Africa",
name: {
common: "Nigeria"
}
}
Além disso, note como a bandeira do país é renderizada usando o pacote react-flags
. Verifique a documentação do react-flags
para aprender mais sobre as propriedades exigidas e como usar o pacote.
Agora, você finalizou a criação de um componente CountryCard
individual. No final das contas, você usará os CountryCard
s várias vezes para exibir diferentes bandeiras e informações dos países em seu aplicativo.
Pagination
Neste passo, você irá criar o componente Pagination
. O componente Pagination
contém a lógica para a construção, renderização e troca de páginas no controle de paginação.
Crie um novo arquivo Pagination.js
dentro do diretório src/components
:
- nano src/components/Pagination.js
E adicione o seguinte código a ele:
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
class Pagination extends Component {
constructor(props) {
super(props);
const { totalRecords = null, pageLimit = 30, pageNeighbours = 0 } = props;
this.pageLimit = typeof pageLimit === 'number' ? pageLimit : 30;
this.totalRecords = typeof totalRecords === 'number' ? totalRecords : 0;
// pageNeighbours can be: 0, 1 or 2
this.pageNeighbours = typeof pageNeighbours === 'number'
? Math.max(0, Math.min(pageNeighbours, 2))
: 0;
this.totalPages = Math.ceil(this.totalRecords / this.pageLimit);
this.state = { currentPage: 1 };
}
}
Pagination.propTypes = {
totalRecords: PropTypes.number.isRequired,
pageLimit: PropTypes.number,
pageNeighbours: PropTypes.number,
onPageChanged: PropTypes.func
};
export default Pagination;
O componente Pagination
pode receber quatro propriedades especiais assim como especificado no objeto propTypes
.
onPageChanged
é uma função chamada com dados do estado atual de paginação apenas quando a página atual muda.totalRecords
indica o número total de registros a serem paginados. Ele é exigido.pageLimit
indica o número de registros a serem exibidos por página. Caso não seja especificado, seu valor padrão é 30
conforme definido no constructor()
.pageNeighbours
indica a quantidade de números de página adicionais a se exibir em cada lado da página atual. O valor mínimo é 0
, e o valor máximo é 2
. Caso não seja especificado, o valor padrão é 0
conforme definido no constructor()
.A imagem a seguir ilustra o efeito de diferentes valores para a propriedade pageNeighbours
:
Na função constructor()
, você computa o total de páginas da seguinte forma:
this.totalPages = Math.ceil(this.totalRecords / this.pageLimit);
Observe que o Math.ceil()
é usado aqui para garantir que seja obtido um valor inteiro para o número total de páginas. Isso também garante que os registros em excesso sejam capturados na última página, principalmente nos casos em que o número de registros em excesso seja inferior ao número de registros a serem exibidos por página.
Por fim, você inicializou o estado com a propriedade currentPage
definida em 1
. Essa propriedade de estado é necessária para manter o controle interno da página ativa atual.
Em seguida, você irá criar o método para gerar os números de página.
Após os import
s mas antes da classe Pagination
, adicione as constantes a seguir e a função range
:
// ...
const LEFT_PAGE = 'LEFT';
const RIGHT_PAGE = 'RIGHT';
/**
* Helper method for creating a range of numbers
* range(1, 5) => [1, 2, 3, 4, 5]
*/
const range = (from, to, step = 1) => {
let i = from;
const range = [];
while (i <= to) {
range.push(i);
i += step;
}
return range;
}
Na classe Pagination
, após o constructor
, adicione o seguinte método fetchPageNumbers
:
class Pagination extends Component {
// ...
/**
* Let's say we have 10 pages and we set pageNeighbours to 2
* Given that the current page is 6
* The pagination control will look like the following:
*
* (1) < {4 5} [6] {7 8} > (10)
*
* (x) => terminal pages: first and last page(always visible)
* [x] => represents current page
* {...x} => represents page neighbours
*/
fetchPageNumbers = () => {
const totalPages = this.totalPages;
const currentPage = this.state.currentPage;
const pageNeighbours = this.pageNeighbours;
/**
* totalNumbers: the total page numbers to show on the control
* totalBlocks: totalNumbers + 2 to cover for the left(<) and right(>) controls
*/
const totalNumbers = (this.pageNeighbours * 2) + 3;
const totalBlocks = totalNumbers + 2;
if (totalPages > totalBlocks) {
const startPage = Math.max(2, currentPage - pageNeighbours);
const endPage = Math.min(totalPages - 1, currentPage + pageNeighbours);
let pages = range(startPage, endPage);
/**
* hasLeftSpill: has hidden pages to the left
* hasRightSpill: has hidden pages to the right
* spillOffset: number of hidden pages either to the left or to the right
*/
const hasLeftSpill = startPage > 2;
const hasRightSpill = (totalPages - endPage) > 1;
const spillOffset = totalNumbers - (pages.length + 1);
switch (true) {
// handle: (1) < {5 6} [7] {8 9} (10)
case (hasLeftSpill && !hasRightSpill): {
const extraPages = range(startPage - spillOffset, startPage - 1);
pages = [LEFT_PAGE, ...extraPages, ...pages];
break;
}
// handle: (1) {2 3} [4] {5 6} > (10)
case (!hasLeftSpill && hasRightSpill): {
const extraPages = range(endPage + 1, endPage + spillOffset);
pages = [...pages, ...extraPages, RIGHT_PAGE];
break;
}
// handle: (1) < {4 5} [6] {7 8} > (10)
case (hasLeftSpill && hasRightSpill):
default: {
pages = [LEFT_PAGE, ...pages, RIGHT_PAGE];
break;
}
}
return [1, ...pages, totalPages];
}
return range(1, totalPages);
}
}
Aqui, você define duas constantes pela primeira vez: LEFT_PAGE
e RIGHT_PAGE
. Essas constantes serão usadas para indicar os pontos onde haverá controles de página para mover-se para a esquerda e direita, respectivamente.
Você também definiu uma função auxiliar range()
que pode ajudá-lo a gerar intervalos de números.
Nota: se você usa uma biblioteca de utilidades como o Lodash em seu projeto, então pode usar a função _.range()
disponibilizada pelo Lodash como alternativa. O código a seguir mostra a diferença entre a função range()
que você acabou de definir e a do Lodash:
range(1, 5); // returns [1, 2, 3, 4, 5]
_.range(1, 5); // returns [1, 2, 3, 4]
Em seguida, você definiu o método fetchPageNumbers()
na classe Pagination
. Esse método processa a lógica principal para a geração dos números de página a serem exibidos no controle de paginação. É desejável que a primeira página e a última sejam estejam visíveis.
Primeiro, algumas variáveis foram definidas. totalNumbers
representa os números totais de páginas que serão exibidos no controle. totalBlocks
representa os números totais de página a serem exibidos com o acréscimo de dois blocos adicionais para os indicadores esquerdo e direito de página.
Se o totalPages
não for maior que totalBlocks
, você retorna um intervalo de números de 1
até totalPages
. Caso contrário, você retorna a matriz de números de página, com LEFT_PAGE
e RIGHT_PAGE
em pontos onde existem páginas sendo despejadas para a esquerda e direita, respectivamente.
No entanto, observe que o controle de paginação garante que a primeira página e a última estejam sempre visíveis. Os controles de página da esquerda e direita aparecem na parte de dentro.
Agora, você irá adicionar o método render()
para habilitar a renderização do controle de paginação.
Na classe Pagination
, após os métodosconstructor
e fetchPageNumbers
, adicione o seguinte método render
:
class Pagination extends Component {
// ...
render() {
if (!this.totalRecords || this.totalPages === 1) return null;
const { currentPage } = this.state;
const pages = this.fetchPageNumbers();
return (
<Fragment>
<nav aria-label="Countries Pagination">
<ul className="pagination">
{ pages.map((page, index) => {
if (page === LEFT_PAGE) return (
<li key={index} className="page-item">
<a className="page-link" href="#" aria-label="Previous" onClick={this.handleMoveLeft}>
<span aria-hidden="true">«</span>
<span className="sr-only">Previous</span>
</a>
</li>
);
if (page === RIGHT_PAGE) return (
<li key={index} className="page-item">
<a className="page-link" href="#" aria-label="Next" onClick={this.handleMoveRight}>
<span aria-hidden="true">»</span>
<span className="sr-only">Next</span>
</a>
</li>
);
return (
<li key={index} className={`page-item${ currentPage === page ? ' active' : ''}`}>
<a className="page-link" href="#" onClick={ this.handleClick(page) }>{ page }</a>
</li>
);
}) }
</ul>
</nav>
</Fragment>
);
}
}
Aqui, você gera o array
de números de página chamando o método fetchPageNumbers()
que você criou anteriormente. Em seguida, você renderiza cada número de página usando o Array.prototype.map()
. Observe que manipuladores de eventos de clique foram registrados em cada número de página renderizada para manipular os cliques.
Além disso, note que o controle de paginação não será renderizado caso a propriedade totalRecords
não tenha sido passada corretamente para o componente Pagination
, ou nos casos em que haja apenas 1
página.
Por fim, você irá definir os métodos do manipulador de eventos.
Na classe Pagination
, após o constructor
e métodos fetchPageNumbers
e render
, adicione o seguinte:
class Pagination extends Component {
// ...
componentDidMount() {
this.gotoPage(1);
}
gotoPage = page => {
const { onPageChanged = f => f } = this.props;
const currentPage = Math.max(0, Math.min(page, this.totalPages));
const paginationData = {
currentPage,
totalPages: this.totalPages,
pageLimit: this.pageLimit,
totalRecords: this.totalRecords
};
this.setState({ currentPage }, () => onPageChanged(paginationData));
}
handleClick = page => evt => {
evt.preventDefault();
this.gotoPage(page);
}
handleMoveLeft = evt => {
evt.preventDefault();
this.gotoPage(this.state.currentPage - (this.pageNeighbours * 2) - 1);
}
handleMoveRight = evt => {
evt.preventDefault();
this.gotoPage(this.state.currentPage + (this.pageNeighbours * 2) + 1);
}
}
Foi definido o método gotoPage()
que modifica o estado e define o currentPage
para a página especificada. Ele garante que o argumento page
tenha um valor mínimo de 1
e um valor máximo do número total de páginas. Por fim, a função onPageChanged()
que foi passada como uma propriedade é chamada, com dados indicando o novo estado de paginação.
Quando o componente é montado, você vai até a primeira página chamando this.gotoPage(1)
como mostrado no método de ciclo de vida componentDidMount()
.
Observe como o (this.pageNeighbours * 2)
foi usado em handleMoveLeft()
e handleMoveRight()
para deslizar os números de página para a esquerda e direita, respectivamente, com base no número atual de página.
Aqui está uma demonstração da interação dos movimentos para a esquerde e direita.
Agora, você concluiu o componente Pagination
. Os usuários serão capazes de interagir com os controles de navegação neste componente para exibir diferentes páginas de bandeiras.
App
Agora que você tem um componente CountryCard
e um Pagination
, use-os em seu componente App
.
Modifique o arquivo App.js
no diretório src
:
- nano src/App.js
Substitua o conteúdo de App.js
pelas linhas de código a seguir:
import React, { Component } from 'react';
import Countries from 'countries-api';
import './App.css';
import Pagination from './components/Pagination';
import CountryCard from './components/CountryCard';
class App extends Component {
state = { allCountries: [], currentCountries: [], currentPage: null, totalPages: null }
componentDidMount() {
const { data: allCountries = [] } = Countries.findAll();
this.setState({ allCountries });
}
onPageChanged = data => {
const { allCountries } = this.state;
const { currentPage, totalPages, pageLimit } = data;
const offset = (currentPage - 1) * pageLimit;
const currentCountries = allCountries.slice(offset, offset + pageLimit);
this.setState({ currentPage, currentCountries, totalPages });
}
}
export default App;
Aqui, inicializa-se o estado do componente App
com os seguintes atributos:
allCountries
- é uma matriz de todos os países em seu aplicativo. Inicializada como uma matriz vazia ([]
).currentCountries
- é uma matriz de todos os países a serem exibidos na página atualmente ativa. Inicializada como uma matriz vazia ([]
).currentPage
- é o número da página que está atualmente ativa. Inicializada como null
.totalPages
- é o número total de páginas para todos os registros de país. Inicializada como null
.Em seguida, no método de ciclo de vida componentDidMount()
, busca-se todos os países usando o pacote countries-api
invocando o Countries.findAll()
. Depois disso, atualiza-se o estado do aplicativo, definindo allCountries
para conter todos os países do mundo. Veja a [documentação do countries-api
] countries-apipara aprender mais sobre o pacote.
Por fim, definiu-se o método onPageChanged()
, que será chamado toda vez que o usuário navegar para uma nova página a partir do controle de paginação. Esse método será passado para a propriedade onPageChanged
do componente Pagination
.
Há duas linhas que merecem atenção nesse método. A primeira é esta:
const offset = (currentPage - 1) * pageLimit;
O valor offset
(deslocamento) indica o índice de partida para buscar os registros para a página atual. Usar (currentPage - 1)
garante que o deslocamento é baseado em zero. Vamos supor que, por exemplo, estejam sendo exibidos 25
registros por página, e o usuário esteja visualizado atualmente a página 5
. Sendo assim, o offset
será ((5-1) * 25 = 100)
.
Exemplificando, caso esteja buscando registros sob demanda de um banco de dados, esta é uma consulta SQL de amostra para mostrar como o offset pode ser usado:
SELECT * FROM `countries` LIMIT 100, 25
Como você não está coletando registros de um banco de dados ou qualquer fonte externa, uma maneira de extrair os registros a serem exibidos na página atual é necessária.
A segunda é esta linha:
const currentCountries = allCountries.slice(offset, offset + pageLimit);
Aqui usa-se o método Array.prototype.slice()
para extrair a parte dos registros requisitada de allCountries
passando o offset
como o índice de partida para a fatia e (offset + pageLimit)
como o índice antes do qual a fatia deve terminar.
Nota: neste tutorial, não foram coletados registros de nenhuma fonte externa. Em um aplicativo real, você provavelmente estaria buscando registros de um banco de dados ou uma API. A lógica para coletar os registros pode entrar no método onPageChanged()
do componente App
.
Vamos supor que você tenha um ponto de extremidade de API fictício /api/countries?page={current_page}&limit={page_limit}
. O código a seguir mostra como é possível buscar os países sob demanda a partir da API usando o pacote HTTP [axios][``axios]:
onPageChanged = data => {
const { currentPage, totalPages, pageLimit } = data;
axios.get(`/api/countries?page=${currentPage}&limit=${pageLimit}`)
.then(response => {
const currentCountries = response.data.countries;
this.setState({ currentPage, currentCountries, totalPages });
});
}
Agora, finalize o componente App
adicionando o método render()
.
Na classe App
, após o componentDidMount
e onPageChanged
, adicione o seguinde método render
:
class App extends Component {
// ... other methods here ...
render() {
const { allCountries, currentCountries, currentPage, totalPages } = this.state;
const totalCountries = allCountries.length;
if (totalCountries === 0) return null;
const headerClass = ['text-dark py-2 pr-4 m-0', currentPage ? 'border-gray border-right' : ''].join(' ').trim();
return (
<div className="container mb-5">
<div className="row d-flex flex-row py-5">
<div className="w-100 px-4 py-5 d-flex flex-row flex-wrap align-items-center justify-content-between">
<div className="d-flex flex-row align-items-center">
<h2 className={headerClass}>
<strong className="text-secondary">{totalCountries}</strong> Countries
</h2>
{ currentPage && (
<span className="current-page d-inline-block h-100 pl-4 text-secondary">
Page <span className="font-weight-bold">{ currentPage }</span> / <span className="font-weight-bold">{ totalPages }</span>
</span>
) }
</div>
<div className="d-flex flex-row py-4 align-items-center">
<Pagination totalRecords={totalCountries} pageLimit={18} pageNeighbours={1} onPageChanged={this.onPageChanged} />
</div>
</div>
{ currentCountries.map(country => <CountryCard key={country.cca3} country={country} />) }
</div>
</div>
);
}
}
No método render()
, renderiza-se o número total de países, a página atual, o número total de páginas, o controle <Pagination>
e o <CountryCard>
para cada país na página atual.
Observe que o método onPageChanged()
definido anteriormente na propriedade onPageChanged
do controle <Pagination>
foi passado. Isso é muito importante para capturar alterações de página a partir do componente Pagination
. Além disso, estão sendo exibidos 18
países por página.
Neste ponto, o aplicativo estará parecido com o mostrado na captura de tela a seguir:
Agora, você possui um componente App
que exibe vários componentes CountryCard
e um componente Pagination
que divide o conteúdo em páginas separadas. Em seguida, você irá explorar a estilização do seu aplicativo.
Você pode ter notado que algumas classes personalizadas foram sendo adicionadas aos componentes criados anteriormente. Vamos definir algumas regras de estilo para essas classes no arquivo src/App.scss
.
- nano src/App.scss
O arquivo App.scss
ficará parecido com o seguinte código:
/* Declare some variables */
$base-color: #ced4da;
$light-background: lighten(desaturate($base-color, 50%), 12.5%);
.current-page {
font-size: 1.5rem;
vertical-align: middle;
}
.country-card-container {
height: 60px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.country-name {
font-size: 0.9rem;
}
.country-region {
font-size: 0.7rem;
}
.current-page,
.country-name,
.country-region {
line-height: 1;
}
// Override some Bootstrap pagination styles
ul.pagination {
margin-top: 0;
margin-bottom: 0;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
li.page-item.active {
a.page-link {
color: saturate(darken($base-color, 50%), 5%) !important;
background-color: saturate(lighten($base-color, 7.5%), 2.5%) !important;
border-color: $base-color !important;
}
}
a.page-link {
padding: 0.75rem 1rem;
min-width: 3.5rem;
text-align: center;
box-shadow: none !important;
border-color: $base-color !important;
color: saturate(darken($base-color, 30%), 10%);
font-weight: 900;
font-size: 1rem;
&:hover {
background-color: $light-background;
}
}
}
Modifique seu arquivo App.js
para fazer referência ao App.scss
ao invés do App.css
.
Nota: para mais informações sobre isso, consulte a documentação do Create React App.
- nano src/App.js
import React, { Component } from 'react';
import Countries from 'countries-api';
import './App.scss';
import Pagination from './components/Pagination';
import CountryCard from './components/CountryCard';
Após adicionar os estilos, o aplicativo terá um visual semelhante ao da captura de tela a seguir:
Agora, você possui um aplicativo completo com uma estilização personalizada adicional. É possível utilizar estilos personalizados para modificar e aprimorar todos os estilos padrão disponibilizados por bibliotecas como o Bootstrap.
Neste tutorial, você criou um widget de paginação personalizada em seu aplicativo React. Embora não tenha feito chamadas para nenhuma API, nem interagido com qualquer backend de banco de dados neste tutorial, seu aplicativo pode exigir essas interações. Não sinta-se limitado à abordagem usada neste tutorial de maneira alguma — você pode estendê-la conforme desejar para que atenda aos requisitos do seu aplicativo.
Para o código fonte completo deste tutorial, confira o repositório build-react-pagination-demo no GitHub. Se quiser, obtenha também uma demonstração em tempo real deste tutorial no Code Sandbox.
Se quiser aprender mais sobre o React, dê uma olhada em nossa série Como programar no React.js, ou confira nossa página do tópico React para exercícios e projetos de programação.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
boa noite, sou novo neste mundo web, criei uma pequena aplicação contendo uma agenda de telefones para a empresa onde trabalho, o banco de dados deixei no firebase, gostaria de saber como adaptaria seu codigo para o meu, ja tentei e nao consegui, poderia me ajudar?