<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Ana Beatriz - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Aprenda a codificar - de graça. Tutoriais de programação em Python, JavaScript, Linux e muito mais. ]]>
        </description>
        <link>https://www.freecodecamp.org/portuguese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Ana Beatriz - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/portuguese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 23 May 2026 08:28:33 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/portuguese/news/author/anabeatriz/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Como criar um aplicação de lista de afazeres usando ReactJS e Firebase ]]>
                </title>
                <description>
                    <![CDATA[ Olá, pessoal. Boas-vindas a este tutorial. Antes de começarmos, você deve estar familiarizado com os conceitos básicos do ReactJS. Se não está, eu recomendo que você veja a documentação do ReactJS [https://reactjs.org/docs/getting-started.html] (em inglês). Usaremos os seguintes componentes na aplicação:  1. ReactJS [https://reactjs.org/]  2. Material UI [https://material-ui.com/]  ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/como-criar-uma-aplicacao-de-lista-de-afazeres-usando-reactjs-e-firebase/</link>
                <guid isPermaLink="false">63273562926c2f06e57d1260</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ana Beatriz ]]>
                </dc:creator>
                <pubDate>Wed, 02 Nov 2022 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/Screenshot-2020-04-11-at-5.10.03-PM.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/how-to-build-a-todo-application-using-reactjs-and-firebase/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Build a TodoApp using ReactJS and Firebase</a>
      </p><p>Olá, pessoal. Boas-vindas a este tutorial. Antes de começarmos, você deve estar familiarizado com os conceitos básicos do ReactJS. Se não está, eu recomendo que você veja a <a href="https://reactjs.org/docs/getting-started.html">documentação do ReactJS</a> (em inglês).</p><p>Usaremos os seguintes componentes na aplicação:</p><ol><li><a href="https://reactjs.org/"><strong>ReactJS</strong></a></li><li><a href="https://material-ui.com/"><strong>Material UI</strong></a></li><li><a href="https://firebase.google.com/"><strong>Firebase</strong></a></li><li><a href="https://expressjs.com/"><strong>ExpressJS</strong></a></li><li><a href="https://www.postman.com/"><strong>Postman</strong></a></li></ol><h2 id="como-nossa-aplica-o-ficar-">Como nossa aplicação ficará:</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/Account-1.gif" class="kg-image" alt="Account-1" width="1272" height="705" loading="lazy"><figcaption>Criação de conta</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/ezgif.com-optimize.gif" class="kg-image" alt="ezgif.com-optimize" width="1272" height="713" loading="lazy"><figcaption>Painel da aplicação de lista de afazeres</figcaption></figure><!--kg-card-begin: html--><hr><!--kg-card-end: html--><h2 id="arquitetura-da-aplica-o-">Arquitetura da aplicação:</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/TodoApp-1.png" class="kg-image" alt="TodoApp-1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/TodoApp-1.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/TodoApp-1.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/TodoApp-1.png 1566w" sizes="(min-width: 720px) 720px" width="1566" height="2170" loading="lazy"><figcaption>Arquitetura da aplicação</figcaption></figure><h2 id="entendendo-nossos-componentes-">Entendendo nossos componentes:</h2><p>Você talvez esteja se perguntando o motivo de estarmos usando o Firebase nessa aplicação. Bem, ele fornece <strong>autenticação</strong> segura, um <strong>banco de dados em tempo real</strong>, um <strong>componente serverless </strong>(em português, sem servidor) e um <strong>bucket de armazenamento</strong>.</p><p>Estamos usando o Express aqui. Assim, não precisaremos lidar com as exceções do HTTP. Usaremos todos os pacotes do Firebase em nossos componentes funcionais. Não queremos deixar o client da aplicação muito grande, o que tende a deixar o processo de carregamento da interface lento.</p><p><strong>Observação:</strong> dividirei este tutorial em quatro seções separadas. Você encontrará um <em>git commit</em> com o código desenvolvido para cada seção. Se você quiser ver o código completo, também, ele estará disponível neste <a href="https://github.com/Sharvin26/TodoApp">repositório</a>.</p><h2 id="se-o-1-desenvolvimento-das-apis-de-afazeres">Seção 1: desenvolvimento das APIs de afazeres</h2><p>Nesta seção, vamos desenvolver os seguintes elementos:</p><ol><li><strong>Configurar as funções do Firebase.</strong></li><li><strong>Instalar o framework do Express e criar as APIs.</strong></li><li><strong>Configurar o firestore como banco de dados.</strong></li></ol><p>O <strong>código da API</strong> implementada nesta seção pode ser encontrado neste <a href="https://github.com/Sharvin26/TodoApp/tree/256e69f5d53646b648347b6f1fbdb965ad184763">commit</a>.</p><h3 id="configurar-as-fun-es-do-firebase-">Configurar as funções do firebase:</h3><p>Vá até o <a href="https://firebase.google.com/">Firebase console</a>.‌‌</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/FirebaseFunctions.png" class="kg-image" alt="FirebaseFunctions" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/FirebaseFunctions.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/FirebaseFunctions.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/FirebaseFunctions.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="903" loading="lazy"><figcaption>Firebase Console</figcaption></figure><p>Selecione a opção <strong>Add Project</strong> (Adicionar projeto). Depois disso, siga o passo a passo do gif abaixo para configurar o projeto do Firebase.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/FirebaseConfigure.gif" class="kg-image" alt="FirebaseConfigure" width="1255" height="652" loading="lazy"><figcaption>Configuração do Firebase</figcaption></figure><p>Vá até a aba <strong>Functions </strong>(Funções) e clique no botão <strong>Get Started</strong> (Iniciar):</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/FirebaseFunctionConfig1.png" class="kg-image" alt="FirebaseFunctionConfig1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/FirebaseFunctionConfig1.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/FirebaseFunctionConfig1.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/FirebaseFunctionConfig1.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="948" loading="lazy"><figcaption>Painel de funções</figcaption></figure><p>Você verá uma caixa de diálogo com instruções sobre <strong>Como configurar as funções do firebase</strong>. Vá até seu ambiente local. Abra a ferramenta de linha de comando. Para instalar as ferramentas do Firebase em sua máquina, use o comando abaixo:</p><!--kg-card-begin: markdown--><pre><code class="language-bash">npm install -g firebase-tools
</code></pre>
<!--kg-card-end: markdown--><p>Quando estiver concluído, use o comando <code>firebase init</code> para configurar as funções do Firebase em seu ambiente local. Selecione as seguintes opções quando estiver inicializando uma função do Firebase no ambiente local:</p><ol><li>Quais funcionalidades do Firebase CLI você quer configurar para esta pasta? Pressione a <em>barra de</em> <em>espaço</em> para selecionar as funcionalidades. Depois, aperte Enter para confirmar suas escolhas =&gt; <em>Functions: Configure and deploy Cloud Functions</em></li><li>Primeiro, vamos associar o diretório deste projeto com o projeto do Firebase… <em>=&gt; Use an existing project</em></li><li>Selecione um projeto padrão do Firebase para este diretório =&gt; <em>application_name</em></li><li>Com qual linguagem você gostaria de escrever as <em>Cloud Functions</em>? =&gt; <em>JavaScript</em></li><li>Você quer usar ESLint para saber sobre prováveis bugs e estilizar? =&gt; <em>N</em></li><li>Você quer instalar dependências com o npm agora? (Y/n) =&gt; <em>Y</em></li></ol><p>Depois que a configuração estiver concluída, você verá a seguinte mensagem:</p><pre><code class="language-bash">✔ Firebase initialization complete!</code></pre><p>Quando a inicialização estiver completa, a estrutura do nosso diretório ficará assim:</p><pre><code class="language-bash">+-- firebase.json 
+-- functions
|   +-- index.js
|   +-- node_modules
|   +-- package-lock.json
|   +-- package.json</code></pre><p>Agora, abra o <code>index.js</code> no diretório <code>functions</code> e copie e cole o seguinte código:</p><pre><code class="language-javascript">const functions = require('firebase-functions');

exports.helloWorld = functions.https.onRequest((request, response) =&gt; {
     response.send("Hello from Firebase!");
});</code></pre><p>Envie o código para as funções do Firebase usando o seguinte comando:</p><pre><code class="language-bash">firebase deploy</code></pre><p>Assim que o envio concluir, você verá a seguinte linha de registro no fim da sua linha de comando:</p><pre><code class="language-bash">&gt; ✔  Deploy complete!
&gt; Project Console: https://console.firebase.google.com/project/todoapp-&lt;id&gt;/overview</code></pre><p>Vá até <strong>Project Console &gt; Functions</strong>. Lá, você encontrará o URL da API. O URL será assim:</p><pre><code class="language-bash">https://&lt;região-de-hosting&gt;-todoapp-&lt;id&gt;.cloudfunctions.net/helloWorld</code></pre><p>Copie esse URL e cole-o no navegador. Você receberá a seguinte resposta:</p><pre><code class="language-bash">Hello from Firebase!</code></pre><p>Isso confirma que nossa função do Firebase foi configurada corretamente.</p><h3 id="instalar-o-framework-do-express-">Instalar o framework do Express:</h3><p>Agora vamos instalar o framework do <code>Express</code> em nosso projeto usando o seguinte comando:</p><pre><code class="language-bash">npm i express</code></pre><p>Vamos criar um diretório <strong>APIs</strong> dentro do diretório <strong>functions</strong>. Dentro desse diretório, vamos criar um arquivo chamado <code>todos.js</code>. Remova tudo do arquivo <code>index.js</code> e copie e cole o seguinte código:</p><pre><code class="language-javascript">//index.js

const functions = require('firebase-functions');
const app = require('express')();

const {
    getAllTodos
} = require('./APIs/todos')

app.get('/todos', getAllTodos);
exports.api = functions.https.onRequest(app);</code></pre><p>Atribuímos a função getAllTodos à rota <strong>/todos</strong>. Então, todas as chamadas à API nesta rota executarão a partir da função getAllTodos. Em seguida, vá até o arquivo <code>todos.js</code> no diretório <strong>APIs</strong> e vamos escrever a função getAllTodos.</p><pre><code class="language-javascript">//todos.js

exports.getAllTodos = (request, response) =&gt; {
    todos = [
        {
            'id': '1',
            'title': 'greeting',
            'body': 'Hello world from sharvin shah' 
        },
        {
            'id': '2',
            'title': 'greeting2',
            'body': 'Hello2 world2 from sharvin shah' 
        }
    ]
    return response.json(todos);
}</code></pre><p>Aqui, declaramos um objeto JSON de exemplo. Depois, vamos extrai-lo a partir do Firestore. Por agora, no entanto, vamos retornar isso. Envie isso para sua função do Firebase usando o comando <code>firebase deploy</code>. Ele pedirá permissão para apagar o módulo <strong>helloworld</strong> – apenas aperte <code>Enter</code> e &nbsp;<strong><code>y</code></strong>.</p><pre><code class="language-bash">The following functions are found in your project but do not exist in your local source code: helloWorld

Would you like to proceed with deletion? Selecting no will continue the rest of the deployments. (y/N) y</code></pre><p>Depois de concluído, vá até <strong>Project Console &gt; Functions</strong>. Lá, você encontrará o URL da API, que será algo como:</p><pre><code class="language-bash">https://&lt;região-de-hosting&gt;-todoapp-&lt;id&gt;.cloudfunctions.net/api</code></pre><p>Agora, vá ao navegador e copie e cole o URL, adicionando <strong>/todos</strong> no fim do URL. Você receberá o seguinte resultado:</p><pre><code class="language-javascript">[
        {
            'id': '1',
            'title': 'greeting',
            'body': 'Hello world from sharvin shah' 
        },
        {
            'id': '2',
            'title': 'greeting2',
            'body': 'Hello2 world2 from sharvin shah' 
        }
]</code></pre><h3 id="firebase-firestore-">Firebase Firestore:</h3><p>Usaremos o firestore do Firebase como banco de dados em tempo real para nossa aplicação. Vá até <strong>Console &gt; Database</strong> no Firebase Console. Para configurar o firestore, siga o gif abaixo:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/Firestore.gif" class="kg-image" alt="Firestore" width="1264" height="574" loading="lazy"><figcaption>Configurando o Firestore</figcaption></figure><p>Quando a configuração terminar, clique no botão <strong>Start Collection</strong> e indique <strong>Collection ID</strong> como sendo <strong>todos</strong>. Clique em <strong>Next</strong> e receberá o seguinte pop-up:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/FireStore-collection.png" class="kg-image" alt="FireStore-collection" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/FireStore-collection.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/FireStore-collection.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/FireStore-collection.png 1364w" sizes="(min-width: 720px) 720px" width="1364" height="1026" loading="lazy"><figcaption>Criando o banco de dados manualmente</figcaption></figure><p>Ignore a chave DocumentID. Para preencher <strong>field, type e value</strong>, veja o JSON abaixo. Atualize o valor de acordo:</p><pre><code class="language-json">{
    Field: title,
    Type: String,
    Value: Hello World
},
{
    Field: body,
    Type: String,
    Value: Hello folks I hope you are staying home...
},
{
    Field: createtAt,
    type: timestamp,
    value: Add the current date and time here
}</code></pre><p>Pressione o botão de salvar. Você verá que a coleção e o documento foram criados. Volte para o ambiente local. Precisamos instalar <code>firebase-admin</code>, que possui o pacote firestore de que precisamos. Use este comando para instalá-lo:</p><pre><code class="language-bash">npm i firebase-admin</code></pre><p>Crie um diretório chamado <strong>util</strong> no diretório <strong>functions</strong>. Vá até esse diretório e crie um arquivo chamado <code>admin.js</code>. Neste arquivo, vamos importar o pacote firebase admin e inicializar o objeto do banco de dados do firestore. Vamos exportá-lo para que outros <strong>módulos</strong> possam usá-lo.</p><pre><code class="language-javascript">//admin.js

const admin = require('firebase-admin');

admin.initializeApp();

const db = admin.firestore();

module.exports = { admin, db };</code></pre><p>Agora, vamos escrever uma API para buscar esses dados. Vá até <code>todos.js</code>, no diretório <strong>functions &gt; APIs</strong>. Remova o código antigo e copie e cole o código abaixo:</p><pre><code class="language-javascript">//todos.js

const { db } = require('../util/admin');

exports.getAllTodos = (request, response) =&gt; {
	db
		.collection('todos')
		.orderBy('createdAt', 'desc')
		.get()
		.then((data) =&gt; {
			let todos = [];
			data.forEach((doc) =&gt; {
				todos.push({
                    todoId: doc.id,
                    title: doc.data().title,
					body: doc.data().body,
					createdAt: doc.data().createdAt,
				});
			});
			return response.json(todos);
		})
		.catch((err) =&gt; {
			console.error(err);
			return response.status(500).json({ error: err.code});
		});
};</code></pre><p>Aqui estamos buscando todos os afazeres do banco de dados e enviando-os para o <em>client</em> como uma lista.</p><p>Você também pode executar a aplicação localmente usando o comando <code>firebase serve</code> &nbsp;ao invés de enviar toda vez. Quando você executar esse comando, talvez apareça um erro relacionado às credenciais. Para consertar, siga os passos mencionados abaixo:</p><ol><li>Vá até <strong>Project Settings</strong> (ícone de engrenagem ao lado esquerdo no topo)</li><li>Vá até a aba <strong>Service Accounts</strong></li><li>Lá embaixo, você verá a opção de <strong>Generating a new key</strong>. Clique nessa opção e você baixará um arquivo JSON.</li><li>Precisamos exportar essas credenciais para nossa seção de linha de comando. Use o comando abaixo para fazer isso:</li></ol><pre><code class="language-javascript">export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/[NOME_DO_ARQUIVO].json"</code></pre><p>Depois disso, execute o comando firebase serve. Se você ainda tiver um erro, use o seguinte comando: <code>firebase login --reauth</code>. Ele abrirá a página para fazer registro com o Google no navegador. Depois de completar o registro, ele funcionará sem erros.</p><p>Você encontrará um URL nos registros da sua ferramenta de linha de comando quando executar o comando <code>firebase serve</code>. Abra esse URL no navegador e digite <code>/todos</code> no fim.</p><pre><code class="language-bash">✔ functions[api]: http function initialized (http://localhost:5000/todoapp-&lt;project-id&gt;/&lt;nome-da-região&gt;/api).</code></pre><p>Você terá o seguinte resultado em JSON no seu navegador:</p><pre><code class="language-json">[
    {
        "todoId":"W67t1kSMO0lqvjCIGiuI",
        "title":"Hello World",
        "body":"Hello folks I hope you are staying home...",
        "createdAt":{"_seconds":1585420200,"_nanoseconds":0 }
    }
]</code></pre><h3 id="escrevendo-outras-apis-">Escrevendo outras APIs:</h3><p>Está na hora de escrever outras APIs das quais precisaremos na nossa aplicação.</p><ol><li><strong>Crie um afazer:</strong> vá até <code>index.js</code> no diretório <strong>functions</strong>. Importe o método postOneTodo abaixo de getAllTodos. Atribua, também, a rota POST para esse método.</li></ol><pre><code class="language-javascript">//index.js

const {
    ..,
    postOneTodo
} = require('./APIs/todos')

app.post('/todo', postOneTodo);</code></pre><p>Vá até <code>todos.js</code> dentro do diretório de funções e adicione um novo método <code>postOneTodo</code> abaixo do método <code>getAllTodos</code>.</p><pre><code class="language-javascript">//todos.js

exports.postOneTodo = (request, response) =&gt; {
	if (request.body.body.trim() === '') {
		return response.status(400).json({ body: 'Must not be empty' });
    }
    
    if(request.body.title.trim() === '') {
        return response.status(400).json({ title: 'Must not be empty' });
    }
    
    const newTodoItem = {
        title: request.body.title,
        body: request.body.body,
        createdAt: new Date().toISOString()
    }
    db
        .collection('todos')
        .add(newTodoItem)
        .then((doc)=&gt;{
            const responseTodoItem = newTodoItem;
            responseTodoItem.id = doc.id;
            return response.json(responseTodoItem);
        })
        .catch((err) =&gt; {
			response.status(500).json({ error: 'Something went wrong' });
			console.error(err);
		});
};</code></pre><p>Nesse método, estamos adicionando um novo afazer em nosso banco de dados. Se os elementos do nosso conteúdo estiverem vazios, retornaremos a resposta 400. Caso contrário, vamos adicionar os dados.</p><p>Execute o comando <code>firebase serve</code> e abra o Postman. Crie uma requisição e selecione o método de tipo <strong>POST</strong>. Adicione o URL e um <code>body</code> do tipo JSON.</p><pre><code class="language-bash">URL: http://localhost:5000/todoapp-&lt;app-id&gt;/&lt;nome-da-região&gt;/api/todo

METHOD: POST

Body: {
   "title":"Hello World",
   "body": "We are writing this awesome API"
}</code></pre><p>Pressione o botão de enviar e você terá a seguinte resposta:</p><pre><code class="language-json">{
     "title": "Hello World",
     "body": "We are writing this awesome API",
     "createdAt": "2020-03-29T12:30:48.809Z",
     "id": "nh41IgARCj8LPWBYzjU0"
}</code></pre><p><strong>2. Apague um afazer:</strong> vá até <code>index.js</code> no diretório <strong>functions</strong>. Importe o método deleteTodo abaixo do postOneTodo. Atribua a rota DELETE para esse método também.</p><pre><code class="language-javascript">//index.js

const {
    ..,
    deleteTodo
} = require('./APIs/todos')

app.delete('/todo/:todoId', deleteTodo);</code></pre><p>Vá até <code>todos.js</code> e adicione um novo método <code>deleteTodo</code> abaixo do método <code>postOneTodo</code>.</p><pre><code class="language-javascript">//todos.js

exports.deleteTodo = (request, response) =&gt; {
    const document = db.doc(`/todos/${request.params.todoId}`);
    document
        .get()
        .then((doc) =&gt; {
            if (!doc.exists) {
                return response.status(404).json({ error: 'Todo not found' })
            }
            return document.delete();
        })
        .then(() =&gt; {
            response.json({ message: 'Delete successfull' });
        })
        .catch((err) =&gt; {
            console.error(err);
            return response.status(500).json({ error: err.code });
        });
};</code></pre><p>Nesse método, estamos excluindo um afazer do nosso banco de dados. Execute o comando <code>firebase serve</code> e vá até o Postman. Crie uma requisição, selecione o tipo <strong>DELETE</strong> para o método e adicione o URL.</p><pre><code class="language-bash">URL: http://localhost:5000/todoapp-&lt;app-id&gt;/&lt;nome-da-região&gt;/api/todo/&lt;todo-id&gt;

METHOD: DELETE</code></pre><p>Pressione o botão de enviar e você terá a seguinte resposta:</p><pre><code class="language-json">{
   "message": "Delete successfull"
}</code></pre><p><strong>3. Edite um afazer:</strong> vá até <code>index.js</code> no diretório <strong>functions</strong>. Importe o método editTodo abaixo de deleteTodo. Atribua a rota PUT a esse método.</p><pre><code class="language-javascript">//index.js

const {
    ..,
    editTodo
} = require('./APIs/todos')

app.put('/todo/:todoId', editTodo);</code></pre><p>Vá até &nbsp;<code>todos.js</code> e adicione um novo método <code>editTodo</code> abaixo do método <code>deleteTodo</code> já existente.</p><pre><code class="language-javascript">//todos.js

exports.editTodo = ( request, response ) =&gt; { 
    if(request.body.todoId || request.body.createdAt){
        response.status(403).json({message: 'Not allowed to edit'});
    }
    let document = db.collection('todos').doc(`${request.params.todoId}`);
    document.update(request.body)
    .then(()=&gt; {
        response.json({message: 'Updated successfully'});
    })
    .catch((err) =&gt; {
        console.error(err);
        return response.status(500).json({ 
                error: err.code 
        });
    });
};</code></pre><p>Nesse método, estamos editando um afazer da nossa base de dados. Lembre-se de que não estamos permitindo que o usuário edite os campos todoId ou createdAt. Execute o comando <code>firebase serve</code> e vá até o Postman. Crie uma requisição, selecione o tipo <strong>PUT</strong> para o método e adicione o URL.</p><pre><code class="language-bash">URL: http://localhost:5000/todoapp-&lt;app-id&gt;/&lt;nome-da-região&gt;/api/todo/&lt;todo-id&gt;

METHOD: PUT</code></pre><p>Pressione o botão de enviar e você terá a seguinte resposta:</p><pre><code class="language-json">{  
   "message": "Updated successfully"
}</code></pre><p><strong>Estrutura do diretório até agora:</strong></p><pre><code class="language-bash">+-- firebase.json 
+-- functions
|   +-- API
|   +-- +-- todos.js
|   +-- util
|   +-- +-- admin.js
|   +-- index.js
|   +-- node_modules
|   +-- package-lock.json
|   +-- package.json
|   +-- .gitignore</code></pre><p>Com isso, completamos a primeira seção da aplicação. Você pode ir tomar um café, descansar e, depois, vamos trabalhar no desenvolvimento das APIs dos usuários.</p><h2 id="se-o-2-desenvolvimento-das-apis-dos-usu-rios">Seção 2: desenvolvimento das APIs dos usuários</h2><p>Nesta seção, vamos desenvolver estes componentes:</p><ol><li><strong>API de autenticação do usuário (login e registro).</strong></li><li><strong>API para obter e atualizar os detalhes do usuário.</strong></li><li><strong>API para atualizar a foto de perfil do usuário.</strong></li><li><strong>API de afazeres mais segura.</strong></li></ol><p>O código da API de usuários implementado nesta seção pode ser encontrado neste <a href="https://github.com/Sharvin26/TodoApp/tree/951a8605d988b8e17bd1623eac5c46e449786d1b">commit</a>.</p><p>Então, vamos começar construindo a API de autenticação do usuário. Vá até <strong>Firebase console &gt; Authentication</strong>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/FirebaseAuthentication.png" class="kg-image" alt="FirebaseAuthentication" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/FirebaseAuthentication.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/FirebaseAuthentication.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/FirebaseAuthentication.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="944" loading="lazy"><figcaption>Página de autenticação do Firebase</figcaption></figure><p>Clique no botão <strong>Set up sign-in method</strong>. Vamos usar e-mail e senha para a validação do usuário. Habilite a opção <strong>Email/Password</strong>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/FirebaseAuth1.png" class="kg-image" alt="FirebaseAuth1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/FirebaseAuth1.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/FirebaseAuth1.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/FirebaseAuth1.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="1060" loading="lazy"><figcaption>Página de configuração do registro do Firebase</figcaption></figure><p>Agora, vamos criar nosso usuário manualmente. Primeiro, vamos criar a API de login. Depois, vamos criar a API de registro.</p><p>Vá até a aba <strong>Users</strong>, em <strong>Authentication</strong>, preencha os detalhes do usuário e clique no botão <strong>Add User</strong>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/Login.png" class="kg-image" alt="Login" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/Login.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/Login.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/Login.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="1055" loading="lazy"><figcaption>Adicionando o usuário manualmente</figcaption></figure><h3 id="1-api-de-login-do-usu-rio-">1. API de login do usuário:</h3><p>Primeiro, precisamos instalar o pacote <code>firebase</code>, que consiste na <strong>biblioteca de autenticação do Firebase</strong>, usando o seguinte comando:</p><pre><code class="language-shell">npm i firebase</code></pre><p>Quando a instalação estiver concluída, vá até o diretório <strong>functions &gt; APIs</strong>. Aqui, vamos criar um arquivo <code>users.js</code>. Agora, dentro de <code>index.js</code>, importamos um método loginUser e atribuímos a ele a rota POST.</p><pre><code class="language-javascript">//index.js

const {
    loginUser
} = require('./APIs/users')

// Users
app.post('/login', loginUser);</code></pre><p>Vá até <strong>Project Settings &gt; General</strong>. Lá, você encontrará o seguinte card:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/app.png" class="kg-image" alt="app" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/app.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/app.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/app.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="512" loading="lazy"><figcaption>Obtendo a configuração do Firebase</figcaption></figure><p>Selecione o ícone da Web e então siga o gif abaixo:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/project.gif" class="kg-image" alt="project" width="1113" height="646" loading="lazy"></figure><p>Selecione a opção <strong>Continue to console</strong>. Quando estiver concluído, você verá um JSON com as configurações do Firebase. Vá até o diretório <strong>functions &gt; util</strong> e crie um arquivo <code>config.js</code>. Copie e cole o seguinte código nesse arquivo:</p><pre><code class="language-javascript">// config.js

module.exports = {
    apiKey: "............",
    authDomain: "........",
    databaseURL: "........",
    projectId: ".......",
    storageBucket: ".......",
    messagingSenderId: "........",
    appId: "..........",
    measurementId: "......."
};</code></pre><p>Substitua <code>............</code> pelos valores que estão em <strong>Firebase console &gt; Project settings &gt;</strong> &nbsp;<strong>General &gt; Your apps &gt; Firebase SD snippet &gt; Config</strong>.</p><p>Copie e cole o seguinte código no arquivo <code>users.js</code>:</p><pre><code class="language-javascript">// users.js

const { admin, db } = require('../util/admin');
const config = require('../util/config');

const firebase = require('firebase');

firebase.initializeApp(config);

const { validateLoginData, validateSignUpData } = require('../util/validators');

// Login
exports.loginUser = (request, response) =&gt; {
    const user = {
        email: request.body.email,
        password: request.body.password
    }

    const { valid, errors } = validateLoginData(user);
	if (!valid) return response.status(400).json(errors);

    firebase
        .auth()
        .signInWithEmailAndPassword(user.email, user.password)
        .then((data) =&gt; {
            return data.user.getIdToken();
        })
        .then((token) =&gt; {
            return response.json({ token });
        })
        .catch((error) =&gt; {
            console.error(error);
            return response.status(403).json({ general: 'wrong credentials, please try again'});
        })
};</code></pre><p>Estamos usando o módulo <strong>signInWithEmailAndPassword</strong> do Firebase para verificar se as credenciais enviadas pelo usuário estão certas. Se estiverem, então enviamos o token desse usuário. Caso contrário, enviamos um status 403 com mensagem "wrong credentials" (credenciais incorretas).</p><p>Agora vamos criar o arquivo <code>validators.js</code> no diretório <strong>functions &gt; util</strong>. Copie e cole o seguinte código nesse arquivo:</p><pre><code class="language-javascript">// validators.js

const isEmpty = (string) =&gt; {
	if (string.trim() === '') return true;
	else return false;
};

exports.validateLoginData = (data) =&gt; {
   let errors = {};
   if (isEmpty(data.email)) errors.email = 'Must not be empty';
   if (isEmpty(data.password)) errors.password = 'Must not be  empty';
   return {
       errors,
       valid: Object.keys(errors).length === 0 ? true : false
    };
};</code></pre><p>Com isso, a <strong>API de login </strong>está completa. Execute o comando <code>firebase serve</code> e vá até o Postman. Crie uma requisição, selecione o método <strong>POST</strong>, adicione o URL e o conteúdo.</p><pre><code class="language-javascript">URL: http://localhost:5000/todoapp-&lt;app-id&gt;/&lt;nome-da-região&gt;/api/login

METHOD: POST

Body: {   
    "email":"Add email that is assigned for user in console", 
    "password": "Add password that is assigned for user in console"
}</code></pre><p>Aperte o botão para enviar a requisição no Postman e você receberá o seguinte resultado:</p><pre><code class="language-javascript">{   
    "token": ".........."
}</code></pre><p>Usaremos esse token em uma parte futura para <strong>obter os detalhes do usuário</strong>. Lembre-se de que esse token expira em <strong>60 minutos</strong>. Para gerar um novo token, use esta API novamente.</p><h3 id="2-api-de-registro-de-usu-rio-">2. API de registro de usuário:</h3><p>O mecanismo padrão de autenticação do Firebase permite apenas que você armazene informações como e-mail, senha, etc. Precisamos, porém, de mais informações para identificar se esse usuário possui esse afazer para que possa haver a leitura, a atualização e a exclusão.</p><p>Para isso, vamos criar uma coleção chamada <strong>users</strong>. Nessa coleção, vamos armazenar os dados do usuário que serão mapeados para o afazer baseado no nome de usuário. Cada nome de usuário será único para todos os usuários na plataforma.</p><p>Vá até <code>index.js</code>. Importaremos o método signUpUser e atribuiremos a ele a rota POST.</p><pre><code class="language-javascript">//index.js

const {
    ..,
    signUpUser
} = require('./APIs/users')

app.post('/signup', signUpUser);</code></pre><p>Agora, vá até <code>validators.js</code> e adicione o seguinte código abaixo do método <code>validateLoginData</code>.</p><pre><code class="language-javascript">// validators.js

const isEmail = (email) =&gt; {
	const emailRegEx = /^(([^&lt;&gt;()\[\]\\.,;:\s@"]+(\.[^&lt;&gt;()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
	if (email.match(emailRegEx)) return true;
	else return false;
};

exports.validateSignUpData = (data) =&gt; {
	let errors = {};

	if (isEmpty(data.email)) {
		errors.email = 'Must not be empty';
	} else if (!isEmail(data.email)) {
		errors.email = 'Must be valid email address';
	}

	if (isEmpty(data.firstName)) errors.firstName = 'Must not be empty';
	if (isEmpty(data.lastName)) errors.lastName = 'Must not be empty';
	if (isEmpty(data.phoneNumber)) errors.phoneNumber = 'Must not be empty';
	if (isEmpty(data.country)) errors.country = 'Must not be empty';

	if (isEmpty(data.password)) errors.password = 'Must not be empty';
	if (data.password !== data.confirmPassword) errors.confirmPassword = 'Passowrds must be the same';
	if (isEmpty(data.username)) errors.username = 'Must not be empty';

	return {
		errors,
		valid: Object.keys(errors).length === 0 ? true : false
	};
};</code></pre><p>Vá até <code>users.js</code> e adicione o seguinte código abaixo do módulo <code>loginUser</code>.</p><pre><code class="language-javascript">// users.js

exports.signUpUser = (request, response) =&gt; {
    const newUser = {
        firstName: request.body.firstName,
        lastName: request.body.lastName,
        email: request.body.email,
        phoneNumber: request.body.phoneNumber,
        country: request.body.country,
		password: request.body.password,
		confirmPassword: request.body.confirmPassword,
		username: request.body.username
    };

    const { valid, errors } = validateSignUpData(newUser);

	if (!valid) return response.status(400).json(errors);

    let token, userId;
    db
        .doc(`/users/${newUser.username}`)
        .get()
        .then((doc) =&gt; {
            if (doc.exists) {
                return response.status(400).json({ username: 'this username is already taken' });
            } else {
                return firebase
                        .auth()
                        .createUserWithEmailAndPassword(
                            newUser.email, 
                            newUser.password
                    );
            }
        })
        .then((data) =&gt; {
            userId = data.user.uid;
            return data.user.getIdToken();
        })
        .then((idtoken) =&gt; {
            token = idtoken;
            const userCredentials = {
                firstName: newUser.firstName,
                lastName: newUser.lastName,
                username: newUser.username,
                phoneNumber: newUser.phoneNumber,
                country: newUser.country,
                email: newUser.email,
                createdAt: new Date().toISOString(),
                userId
            };
            return db
                    .doc(`/users/${newUser.username}`)
                    .set(userCredentials);
        })
        .then(()=&gt;{
            return response.status(201).json({ token });
        })
        .catch((err) =&gt; {
			console.error(err);
			if (err.code === 'auth/email-already-in-use') {
				return response.status(400).json({ email: 'Email already in use' });
			} else {
				return response.status(500).json({ general: 'Something went wrong, please try again' });
			}
		});
}</code></pre><p>Validamos os dados do nosso usuário. Então, enviamos um e-mail e senha para o módulo <strong>createUserWithEmailAndPassword</strong> do Firebase para criar o usuário. Depois que o usuário foi criado com sucesso, salvamos as credenciais do usuário no banco de dados.</p><p>Com isso, a<strong> API de registro </strong>está completa. Execute o comando <code>firebase serve</code> &nbsp;e vá até o Postman. Crie uma requisição, selecione o método <strong>POST</strong>. Adicione o URL e o conteúdo.</p><pre><code class="language-javascript">URL: http://localhost:5000/todoapp-&lt;app-id&gt;/&lt;region-name&gt;/api/signup

METHOD: POST

Body: {
   "firstName": "Add a firstName here",
   "lastName": "Add a lastName here",
   "email":"Add a email here",
   "phoneNumber": "Add a phone number here",
   "country": "Add a country here",
   "password": "Add a password here",
   "confirmPassword": "Add same password here",
   "username": "Add unique username here"
}</code></pre><p>Aperte o botão para enviar a requisição no Postman e você receberá o seguinte resultado:</p><pre><code class="language-javascript">{   
    "token": ".........."
}</code></pre><p>Agora, vá até <strong>Firebase console &gt; Database</strong> e, lá, você verá o seguinte resultado:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/database.png" class="kg-image" alt="database" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/database.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/database.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/database.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="947" loading="lazy"></figure><p>Como pode ver, nossa coleção de usuário foi criada com sucesso com um documento nela.</p><h3 id="3-carregar-a-foto-de-perfil-do-usu-rio-">3. Carregar a foto de perfil do usuário:</h3><p>Nossos usuários serão capazes de carregar suas fotos de perfil. Para isso, estaremos usando o bucket <strong>Storage</strong>. Vá até <strong>Firebase console &gt; Storage</strong> e clique no botão <strong>Get started</strong>. Siga o GIF abaixo para saber como configurar:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/storage.gif" class="kg-image" alt="storage" width="1090" height="555" loading="lazy"></figure><p>Agora, vá na aba <strong>Rules</strong>, em <strong>Storage</strong>, e atualize a permissão para o acesso ao bucket, como na imagem abaixo:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/storageRule.png" class="kg-image" alt="storageRule" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/storageRule.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/storageRule.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/storageRule.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="1070" loading="lazy"></figure><p>Para carregar a foto de perfil, usaremos o pacote chamado <code>busboy</code>. Para instalar o pacote, use o seguinte comando:</p><pre><code class="language-bash">npm i busboy</code></pre><p>Vá até <code>index.js</code>. Importe o método uploadProfilePhoto abaixo do método signUpUser. Também atribua a rota POST para ele.</p><pre><code class="language-javascript">//index.js

const auth = require('./util/auth');

const {
    ..,
    uploadProfilePhoto
} = require('./APIs/users')

app.post('/user/image', auth, uploadProfilePhoto);</code></pre><p>Aqui, adicionamos uma camada de autenticação para que apenas um usuário associado àquela conta possa carregar a imagem. Agora, crie um arquivo chamado <code>auth.js</code> no diretório <strong>functions &gt; utils</strong>. Copie e cole o seguinte código nesse arquivo:</p><pre><code class="language-javascript">// auth.js

const { admin, db } = require('./admin');

module.exports = (request, response, next) =&gt; {
	let idToken;
	if (request.headers.authorization &amp;&amp; request.headers.authorization.startsWith('Bearer ')) {
		idToken = request.headers.authorization.split('Bearer ')[1];
	} else {
		console.error('No token found');
		return response.status(403).json({ error: 'Unauthorized' });
	}
	admin
		.auth()
		.verifyIdToken(idToken)
		.then((decodedToken) =&gt; {
			request.user = decodedToken;
			return db.collection('users').where('userId', '==', request.user.uid).limit(1).get();
		})
		.then((data) =&gt; {
			request.user.username = data.docs[0].data().username;
			request.user.imageUrl = data.docs[0].data().imageUrl;
			return next();
		})
		.catch((err) =&gt; {
			console.error('Error while verifying token', err);
			return response.status(403).json(err);
		});
};</code></pre><p>Aqui, estamos usando o módulo <strong>verifyIdToken</strong> do Firebase para verificar o token. Depois disso, estaremos decodificando os detalhes do usuário e enviando-os na requisição existente.</p><p>Vá até <code>users.js</code> e adicione o seguinte código abaixo do método <code>signup</code>:</p><pre><code class="language-javascript">// users.js

deleteImage = (imageName) =&gt; {
    const bucket = admin.storage().bucket();
    const path = `${imageName}`
    return bucket.file(path).delete()
    .then(() =&gt; {
        return
    })
    .catch((error) =&gt; {
        return
    })
}

// Upload profile picture
exports.uploadProfilePhoto = (request, response) =&gt; {
    const BusBoy = require('busboy');
	const path = require('path');
	const os = require('os');
	const fs = require('fs');
	const busboy = new BusBoy({ headers: request.headers });

	let imageFileName;
	let imageToBeUploaded = {};

	busboy.on('file', (fieldname, file, filename, encoding, mimetype) =&gt; {
		if (mimetype !== 'image/png' &amp;&amp; mimetype !== 'image/jpeg') {
			return response.status(400).json({ error: 'Wrong file type submited' });
		}
		const imageExtension = filename.split('.')[filename.split('.').length - 1];
        imageFileName = `${request.user.username}.${imageExtension}`;
		const filePath = path.join(os.tmpdir(), imageFileName);
		imageToBeUploaded = { filePath, mimetype };
		file.pipe(fs.createWriteStream(filePath));
    });
    deleteImage(imageFileName);
	busboy.on('finish', () =&gt; {
		admin
			.storage()
			.bucket()
			.upload(imageToBeUploaded.filePath, {
				resumable: false,
				metadata: {
					metadata: {
						contentType: imageToBeUploaded.mimetype
					}
				}
			})
			.then(() =&gt; {
				const imageUrl = `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${imageFileName}?alt=media`;
				return db.doc(`/users/${request.user.username}`).update({
					imageUrl
				});
			})
			.then(() =&gt; {
				return response.json({ message: 'Image uploaded successfully' });
			})
			.catch((error) =&gt; {
				console.error(error);
				return response.status(500).json({ error: error.code });
			});
	});
	busboy.end(request.rawBody);
};</code></pre><p>Com isso, nossa <strong>API de carregar foto de perfil</strong> está completa. Execute o comando <code>firebase serve</code> e vá até o Postman. Crie uma requisição, selecione o método <strong>POST</strong>, adicione o URL e, na seção de conteúdo, selecione o tipo form-data.</p><p>A requisição está protegida. Você precisará enviar o <strong>token bearer</strong> também. Para enviar o <em>token bearer</em>, faça login novamente, caso ele tenha expirado. Depois disso, em <strong>Postman App &gt; Authorization tab &gt; Type &gt; Bearer Token</strong>, cole o token na seção do token.</p><pre><code class="language-javascript">URL: http://localhost:5000/todoapp-&lt;app-id&gt;/&lt;region-name&gt;/api/user/image

METHOD: GET

Body: { REFER THE IMAGE down below }</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/cover-1.png" class="kg-image" alt="cover-1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/cover-1.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/cover-1.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/cover-1.png 1100w" sizes="(min-width: 720px) 720px" width="1100" height="307" loading="lazy"></figure><p>Aperte o botão para enviar a requisição no Postman e você receberá o seguinte resultado:</p><pre><code class="language-javascript">{        
    "message": "Image uploaded successfully"
}</code></pre><h3 id="4-obter-os-detalhes-do-usu-rio-">4. Obter os detalhes do usuário:</h3><p>Aqui, estamos buscando os dados dos nossos usuários a partir do banco de dados. Vá até <code>index.js</code>, importe o método getUserDetail e atribua a rota GET.</p><pre><code class="language-javascript">// index.js

const {
    ..,
    getUserDetail
} = require('./APIs/users')

app.get('/user', auth, getUserDetail);</code></pre><p>Agora, vá até <code>users.js</code> e adicione o seguinte código depois do módulo <code>uploadProfilePhoto</code>:</p><pre><code class="language-javascript">// users.js

exports.getUserDetail = (request, response) =&gt; {
    let userData = {};
	db
		.doc(`/users/${request.user.username}`)
		.get()
		.then((doc) =&gt; {
			if (doc.exists) {
                userData.userCredentials = doc.data();
                return response.json(userData);
			}	
		})
		.catch((error) =&gt; {
			console.error(error);
			return response.status(500).json({ error: error.code });
		});
}</code></pre><p>Estamos usando o módulo <strong>doc().get()</strong> do Firebase para retornar os detalhes do usuário. Com isso, nossa <strong>API de obter detalhes do usuário</strong> está completa. Execute o comando <code>firebase serve</code> e vá até o Postman. Crie uma requisição, selecione o método <strong>GET</strong>, adicione o URL e o conteúdo.</p><p>A requisição está protegida. Então, você precisará enviar o <strong>token bearer</strong> também. Para enviar o token bearer, faça login novamente caso o token já tenha expirado.</p><pre><code class="language-javascript">URL: http://localhost:5000/todoapp-&lt;app-id&gt;/&lt;region-name&gt;/api/user
METHOD: GET</code></pre><p>Aperte o botão de enviar requisição no Postman e você receberá o seguinte resultado:</p><pre><code class="language-javascript">{
   "userCredentials": {
       "phoneNumber": "........",
       "email": "........",
       "country": "........",
       "userId": "........",
       "username": "........",
       "createdAt": "........",
       "lastName": "........",
       "firstName": "........"
    }
}</code></pre><h3 id="5-atualizar-os-detalhes-do-usu-rio-">5. Atualizar os detalhes do usuário:</h3><p>Agora, vamos adicionar a funcionalidade para atualizar os detalhes do usuário. Vá até <code>index.js</code>, copie e cole o seguinte código:</p><pre><code class="language-javascript">// index.js

const {
    ..,
    updateUserDetails
} = require('./APIs/users')

app.post('/user', auth, updateUserDetails);</code></pre><p>Vá até <code>users.js</code> e adicione o módulo <code>updateUserDetails</code> abaixo do <code>getUserDetails</code> existente:</p><pre><code class="language-javascript">// users.js

exports.updateUserDetails = (request, response) =&gt; {
    let document = db.collection('users').doc(`${request.user.username}`);
    document.update(request.body)
    .then(()=&gt; {
        response.json({message: 'Updated successfully'});
    })
    .catch((error) =&gt; {
        console.error(error);
        return response.status(500).json({ 
            message: "Cannot Update the value"
        });
    });
}</code></pre><p>Aqui, estamos usando o método <strong>update</strong> do Firebase. Com isso, nossa <strong>API de atualizar os detalhes do usuário</strong> está completa. Siga o mesmo processo feito na API de obter detalhes do usuário para realizar uma requisição, com apenas uma mudança. Adicione o conteúdo na requisição e um método POST.</p><pre><code class="language-javascript">URL: http://localhost:5000/todoapp-&lt;app-id&gt;/&lt;region-name&gt;/api/user

METHOD: POST

Body : {
    // You can edit First Name, last Name and country
    // We will disable other Form Tags from our UI
}</code></pre><p>Aperte o botão para enviar a requisição no Postman e você receberá o seguinte resultado:</p><pre><code class="language-javascript">{
    "message": "Updated successfully"
}</code></pre><h3 id="6-tornar-a-api-de-afazeres-segura-">6. Tornar a API de afazeres segura:</h3><p>Para tornar a API de afazeres segura, de modo que apenas o usuário escolhido possa acessá-la, faremos algumas pequenas mudanças em nosso código atual. Primeiramente, vamos atualizar <code>index.js</code> para:</p><pre><code class="language-javascript">// index.js

// Todos
app.get('/todos', auth, getAllTodos);
app.get('/todo/:todoId', auth, getOneTodo);
app.post('/todo',auth, postOneTodo);
app.delete('/todo/:todoId',auth, deleteTodo);
app.put('/todo/:todoId',auth, editTodo);</code></pre><p>Atualizamos todas as <strong>rotas de afazeres</strong> adicionando <code>auth</code>. Assim, todas as chamadas para a API precisarão de um token e poderão ser acessadas apenas por um usuário em particular.</p><p>Depois disso, vá até <code>todos.js</code> no diretório <strong>functions &gt; APIs</strong>.</p><ol><li><strong>Crie a API de afazeres:</strong> abra <code>todos.js</code> e, no método <strong>postOneTodo</strong>, adicione a chave <em>username</em>, assim:</li></ol><pre><code class="language-javascript">const newTodoItem = {
     ..,
     username: request.user.username,
     ..
}</code></pre><p><strong>2. API para obter todos os afazeres:</strong> abra <code>todos.js</code> e, no método <strong>getAllTodos</strong>, adicione a instrução <em>where</em>,<em> </em>assim:</p><pre><code class="language-javascript">db
.collection('todos')
.where('username', '==', request.user.username)
.orderBy('createdAt', 'desc')</code></pre><p>Execute <code>firebase serve</code> e teste nossa API GET. <strong>Não se esqueça de mandar o token bearer.</strong> Aqui, você terá uma resposta de erro assim:</p><pre><code class="language-javascript">{   
    "error": 9
}</code></pre><p>Vá até a linha de comando e você verá as seguintes linhas:</p><pre><code class="language-bash">i  functions: Beginning execution of "api"&gt;  Error: 9 FAILED_PRECONDITION: The query requires an index. You can create it here: &lt;URL&gt;&gt;      at callErrorFromStatus</code></pre><p>Abra o <strong>&lt;URL&gt;</strong> acima e clique em <strong>Create index</strong>.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/index.png" class="kg-image" alt="index" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/index.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/index.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/index.png 1294w" sizes="(min-width: 720px) 720px" width="1294" height="840" loading="lazy"></figure><p>Quando o índice estiver pronto, envie a requisição novamente e você terá o seguinte resultado:</p><pre><code class="language-javascript">[
   {
      "todoId": "......",
      "title": "......",
      "username": "......",
      "body": "......",
      "createdAt": "2020-03-30T13:01:58.478Z"
   }
]</code></pre><p><strong>3. API para excluir um afazer:</strong> abra <code>todos.js</code> e, abaixo de <strong>deleteTodo</strong>, adicione a condição abaixo. Adicione esta condição dentro da pesquisa <strong>document.get().then()</strong>, abaixo da condição <strong>!doc.exists</strong>.</p><pre><code class="language-javascript">..
if(doc.data().username !== request.user.username){
     return response.status(403).json({error:"UnAuthorized"})
}</code></pre><h3 id="estrutura-do-diret-rio-at-agora-">Estrutura do diretório até agora:</h3><pre><code class="language-bash">+-- firebase.json 
+-- functions
|   +-- API
|   +-- +-- todos.js 
|   +-- +-- users.js
|   +-- util
|   +-- +-- admin.js
|   +-- +-- auth.js
|   +-- +-- validators.js
|   +-- index.js
|   +-- node_modules
|   +-- package-lock.json
|   +-- package.json
|   +-- .gitignore</code></pre><p>Com isso, completamos o back-end da nossa API. Faça uma pausa, tome um café e, depois disso, vamos começar a construir o front-end da nossa aplicação.</p><h2 id="se-o-3-painel-do-usu-rio">Seção 3: painel do usuário</h2><p>Nesta seção, vamos desenvolver estes componentes:</p><ol><li><strong>Configurar ReactJS e Material UI.</strong></li><li><strong>Criar o formulário de login e registro.</strong></li><li><strong>Criar a seção das contas.</strong></li></ol><p>O código do painel do usuário implementado nesta seção pode ser encontrado neste <a href="https://github.com/Sharvin26/TodoApp/tree/2b207786651167c1ed5327c2c8583e97080abb54/view">commit</a>.</p><h3 id="1-configurar-o-reactjs-e-o-material-ui-">1. Configurar o ReactJS e o Material UI:</h3><p>Vamos usar o <em>template</em> <strong>create-react-app</strong>. Ele nos dará uma estrutura fundamental para desenvolver a aplicação. Para instalá-lo, use o seguinte comando:</p><pre><code class="language-bash">npm install -g create-react-app</code></pre><p>Vá até a pasta da raiz do projeto onde se encontra o diretório <strong>functions</strong>. Inicialize o front-end da nossa aplicação usando o seguinte comando:</p><pre><code class="language-bash">create-react-app view</code></pre><p>Lembre-se de usar a versão <strong>v16.13.1</strong> da biblioteca ReactJS.</p><p>Quando a instalação completar, você verá o seguinte nos registros da sua linha de comando:</p><pre><code class="language-bash">cd view
  npm start
Happy hacking!</code></pre><p>Com isso, configuramos nossa aplicação do React. Você terá a seguinte estrutura de projeto:</p><pre><code class="language-bash">+-- firebase.json 
+-- functions { This Directory consists our API logic }
+-- view { This Directory consists our FrontEnd Compoenents }
+-- .firebaserc
+-- .gitignore</code></pre><p>Agora, execute a aplicação usando o comando <code>npm start</code>. Vá até <code>http://localhost:3000/</code> no navegador e você verá o seguinte resultado:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/React1.png" class="kg-image" alt="React1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/React1.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/React1.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/React1.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="946" loading="lazy"></figure><p>Agora, vamos remover todos os componentes que não são necessários. Vá até o diretório <em>view </em>e remova todos os arquivos que tem a palavra <strong>[ Remove ] </strong>ao lado. <strong>Para isso, veja a árvore de estrutura do diretório abaixo.</strong></p><pre><code class="language-bash">+-- README.md [ Remove ]
+-- package-lock.json
+-- package.json
+-- node_modules
+-- .gitignore
+-- public
|   +-- favicon.ico [ Remove ]
|   +-- index.html
|   +-- logo192.png [ Remove ]
|   +-- logo512.png [ Remove ]
|   +-- manifest.json
|   +-- robots.txt
+-- src
|   +-- App.css
|   +-- App.test.js
|   +-- index.js
|   +-- serviceWorker.js
|   +-- App.js
|   +-- index.css [ Remove ]
|   +-- logo.svg [ Remove ]
|   +-- setupTests.js</code></pre><p>Vá até <code>index.html</code>, no diretório <strong>public</strong>, e remova as seguintes linhas:</p><pre><code class="language-html">&lt;link rel="icon" href="%PUBLIC_URL%/favicon.ico" /&gt;
&lt;link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /&gt;
&lt;link rel="manifest" href="%PUBLIC_URL%/manifest.json" /&gt;</code></pre><p>Vá até <code>App.js</code>, no diretório <strong>src</strong> e substitua o código antigo pelo seguinte código:</p><pre><code class="language-javascript">import React from 'react';
function App() {
  return (
    &lt;div&gt;
    &lt;/div&gt;
  );
}
export default App;</code></pre><p>Vá até <code>index.js</code> e remova a seguinte importação:</p><pre><code class="language-javascript">import './index.css'</code></pre><p>Não apaguei o <code>App.css</code> nem o estou usando nesta aplicação. Porém, se você quiser apagá-lo ou usá-lo, fique à vontade.</p><p>Vá até <a href="http://localhost:3000/">http://localhost:3000/</a> no navegador e você verá uma tela branca como resultado.</p><p>Para instalar o Material UI, vá até o diretório view, copie e cole este comando no terminal:</p><pre><code class="language-bash">npm install @material-ui/core</code></pre><p>Lembre-se de usar a versão <strong>v4.9.8</strong> da biblioteca Material UI.</p><h3 id="2-formul-rio-de-login-">2. Formulário de login:</h3><p>Para desenvolver o formulário de login, vá até <code>App.js</code>. No topo do arquivo <code>App.js</code>, adicione as seguintes importações:</p><pre><code class="language-javascript">import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import login from './pages/login';</code></pre><p>Estamos usando <strong>Switch</strong> e <strong>Route</strong> para atribuir rotas para nossa aplicação de afazeres. Agora, vamos adicionar apenas a rota de <strong>/login</strong> e atribuir um componente de login a ela.</p><pre><code class="language-javascript">// App.js

&lt;Router&gt;
    &lt;div&gt;
       &lt;Switch&gt;
           &lt;Route exact path="/login" component={login}/&gt;
       &lt;/Switch&gt;
    &lt;/div&gt;
&lt;/Router&gt;</code></pre><p>Crie um diretório <strong>pages</strong> abaixo do diretório <strong>view</strong> existente e um arquivo chamado <code>login.js</code> no diretório <strong>pages</strong>.</p><p>Vamos importar os componentes do Material UI e o pacote Axios em <code>login.js</code>:</p><pre><code class="language-javascript">// login.js

// Material UI components
import React, { Component } from 'react';
import Avatar from '@material-ui/core/Avatar';
import Button from '@material-ui/core/Button';
import CssBaseline from '@material-ui/core/CssBaseline';
import TextField from '@material-ui/core/TextField';
import Link from '@material-ui/core/Link';
import Grid from '@material-ui/core/Grid';
import LockOutlinedIcon from '@material-ui/icons/LockOutlined';
import Typography from '@material-ui/core/Typography';
import withStyles from '@material-ui/core/styles/withStyles';
import Container from '@material-ui/core/Container';
import CircularProgress from '@material-ui/core/CircularProgress';

import axios from 'axios';</code></pre><p>Vamos adicionar a seguinte estilização para nossa página de login:</p><pre><code class="language-javascript">// login.js

const styles = (theme) =&gt; ({
	paper: {
		marginTop: theme.spacing(8),
		display: 'flex',
		flexDirection: 'column',
		alignItems: 'center'
	},
	avatar: {
		margin: theme.spacing(1),
		backgroundColor: theme.palette.secondary.main
	},
	form: {
		width: '100%',
		marginTop: theme.spacing(1)
	},
	submit: {
		margin: theme.spacing(3, 0, 2)
	},
	customError: {
		color: 'red',
		fontSize: '0.8rem',
		marginTop: 10
	},
	progess: {
		position: 'absolute'
	}
});</code></pre><p>Vamos criar uma classe chamada login, que possui um formulário e uma função que lida com o envio desse formulário.</p><pre><code class="language-javascript">// login.js

class login extends Component {
	constructor(props) {
		super(props);

		this.state = {
			email: '',
			password: '',
			errors: [],
			loading: false
		};
	}

	componentWillReceiveProps(nextProps) {
		if (nextProps.UI.errors) {
			this.setState({
				errors: nextProps.UI.errors
			});
		}
	}

	handleChange = (event) =&gt; {
		this.setState({
			[event.target.name]: event.target.value
		});
	};

	handleSubmit = (event) =&gt; {
		event.preventDefault();
		this.setState({ loading: true });
		const userData = {
			email: this.state.email,
			password: this.state.password
		};
		axios
			.post('/login', userData)
			.then((response) =&gt; {
				localStorage.setItem('AuthToken', `Bearer ${response.data.token}`);
				this.setState({ 
					loading: false,
				});		
				this.props.history.push('/');
			})
			.catch((error) =&gt; {				
				this.setState({
					errors: error.response.data,
					loading: false
				});
			});
	};

	render() {
		const { classes } = this.props;
		const { errors, loading } = this.state;
		return (
			&lt;Container component="main" maxWidth="xs"&gt;
				&lt;CssBaseline /&gt;
				&lt;div className={classes.paper}&gt;
					&lt;Avatar className={classes.avatar}&gt;
						&lt;LockOutlinedIcon /&gt;
					&lt;/Avatar&gt;
					&lt;Typography component="h1" variant="h5"&gt;
						Login
					&lt;/Typography&gt;
					&lt;form className={classes.form} noValidate&gt;
						&lt;TextField
							variant="outlined"
							margin="normal"
							required
							fullWidth
							id="email"
							label="Email Address"
							name="email"
							autoComplete="email"
							autoFocus
							helperText={errors.email}
							error={errors.email ? true : false}
							onChange={this.handleChange}
						/&gt;
						&lt;TextField
							variant="outlined"
							margin="normal"
							required
							fullWidth
							name="password"
							label="Password"
							type="password"
							id="password"
							autoComplete="current-password"
							helperText={errors.password}
							error={errors.password ? true : false}
							onChange={this.handleChange}
						/&gt;
						&lt;Button
							type="submit"
							fullWidth
							variant="contained"
							color="primary"
							className={classes.submit}
							onClick={this.handleSubmit}
							disabled={loading || !this.state.email || !this.state.password}
						&gt;
							Sign In
							{loading &amp;&amp; &lt;CircularProgress size={30} className={classes.progess} /&gt;}
						&lt;/Button&gt;
						&lt;Grid container&gt;
							&lt;Grid item&gt;
								&lt;Link href="signup" variant="body2"&gt;
									{"Don't have an account? Sign Up"}
								&lt;/Link&gt;
							&lt;/Grid&gt;
						&lt;/Grid&gt;
						{errors.general &amp;&amp; (
							&lt;Typography variant="body2" className={classes.customError}&gt;
								{errors.general}
							&lt;/Typography&gt;
						)}
					&lt;/form&gt;
				&lt;/div&gt;
			&lt;/Container&gt;
		);
	}
}</code></pre><p>No fim desse arquivo, adicione a seguinte exportação:</p><pre><code class="language-javascript">export default withStyles(styles)(login);</code></pre><p>Adicione o URL das nossas funções do Firebase em <strong>view &gt; package.json</strong> assim:</p><blockquote>Lembre-se de adicionar uma chave chamada <strong>proxy</strong> abaixo do objeto de JSON <strong>browserslist</strong></blockquote><pre><code class="language-json">"proxy": "https://&lt;nome-da-região&gt;-todoapp-&lt;id&gt;.cloudfunctions.net/api"</code></pre><p>Instale os pacotes <strong>Axios</strong> e <strong>Material Icons</strong> usando os seguintes comandos:</p><pre><code class="language-bash">// Axios command:
npm i axios
// Material Icons:
npm install @material-ui/icons</code></pre><p>Adicionamos a rota login em <code>App.js</code>. No arquivo <code>login.js</code>, criamos um componente de classe que lida com o estado e envia a requisição POST para a API de login usando o pacote Axios. Se a requisição for bem-sucedida, armazenamos o token. Se tivermos erros na resposta, simplesmente mostramos os erros na interface.</p><p>Vá até <a href="http://localhost:3000/login">http://localhost:3000/login</a> no navegador e você verá a seguinte interface de login.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/LoginPage.png" class="kg-image" alt="LoginPage" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/LoginPage.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/LoginPage.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/LoginPage.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="900" loading="lazy"><figcaption>Página de login</figcaption></figure><p>Tente preencher credenciais erradas ou enviar uma requisição vazia e você terá erros. Envie uma requisição válida. Vá até <strong>Developer console &gt; Application</strong>. Você verá que o token dos usuários está armazenado no armazenamento local. Quando o login for feito com sucesso, vamos ser redirecionados para a página de início.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/loginDev.png" class="kg-image" alt="loginDev" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/loginDev.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/loginDev.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/loginDev.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="385" loading="lazy"><figcaption>Console de Desenvolvedor do Google Chrome</figcaption></figure><h3 id="3-formul-rio-de-registro-">3. Formulário de registro:</h3><p>Para desenvolver o formulário de registro, vá até <code>App.js</code> e atualize o componente <code>Route</code> existente com a linha abaixo:</p><pre><code class="language-javascript">// App.js

&lt;Route exact path="/signup" component={signup}/&gt;</code></pre><p>Não se esqueça de importar:</p><pre><code class="language-javascript">// App.js

import signup from './pages/signup';</code></pre><p>Crie um arquivo chamado <code>signup.js</code> no diretório <strong>pages</strong>.</p><p>Dentro de <code>signup.js</code>, vamos importar os pacotes Material UI e Axios:</p><pre><code class="language-javascript">// signup.js

import React, { Component } from 'react';
import Avatar from '@material-ui/core/Avatar';
import Button from '@material-ui/core/Button';
import CssBaseline from '@material-ui/core/CssBaseline';
import TextField from '@material-ui/core/TextField';
import Link from '@material-ui/core/Link';
import Grid from '@material-ui/core/Grid';
import LockOutlinedIcon from '@material-ui/icons/LockOutlined';
import Typography from '@material-ui/core/Typography';
import Container from '@material-ui/core/Container';
import withStyles from '@material-ui/core/styles/withStyles';
import CircularProgress from '@material-ui/core/CircularProgress';

import axios from 'axios';</code></pre><p>Vamos adicionar a seguinte estilização em nossa página de registro:</p><pre><code class="language-javascript">// signup.js


const styles = (theme) =&gt; ({
	paper: {
		marginTop: theme.spacing(8),
		display: 'flex',
		flexDirection: 'column',
		alignItems: 'center'
	},
	avatar: {
		margin: theme.spacing(1),
		backgroundColor: theme.palette.secondary.main
	},
	form: {
		width: '100%', // Fix IE 11 issue.
		marginTop: theme.spacing(3)
	},
	submit: {
		margin: theme.spacing(3, 0, 2)
	},
	progess: {
		position: 'absolute'
	}
});</code></pre><p>Vamos criar uma classe chamada <strong>signup</strong>,<strong> </strong>que possui um formulário e uma função que lida com o envio dele.</p><pre><code class="language-javascript">// signup.js

class signup extends Component {
	constructor(props) {
		super(props);

		this.state = {
			firstName: '',
			lastName: '',
			phoneNumber: '',
			country: '',
			username: '',
			email: '',
			password: '',
			confirmPassword: '',
			errors: [],
			loading: false
		};
	}

	componentWillReceiveProps(nextProps) {
		if (nextProps.UI.errors) {
			this.setState({
				errors: nextProps.UI.errors
			});
		}
	}

	handleChange = (event) =&gt; {
		this.setState({
			[event.target.name]: event.target.value
		});
	};

	handleSubmit = (event) =&gt; {
		event.preventDefault();
		this.setState({ loading: true });
		const newUserData = {
			firstName: this.state.firstName,
			lastName: this.state.lastName,
			phoneNumber: this.state.phoneNumber,
			country: this.state.country,
			username: this.state.username,
			email: this.state.email,
			password: this.state.password,
			confirmPassword: this.state.confirmPassword
		};
		axios
			.post('/signup', newUserData)
			.then((response) =&gt; {
				localStorage.setItem('AuthToken', `${response.data.token}`);
				this.setState({ 
					loading: false,
				});	
				this.props.history.push('/');
			})
			.catch((error) =&gt; {
				this.setState({
					errors: error.response.data,
					loading: false
				});
			});
	};

	render() {
		const { classes } = this.props;
		const { errors, loading } = this.state;
		return (
			&lt;Container component="main" maxWidth="xs"&gt;
				&lt;CssBaseline /&gt;
				&lt;div className={classes.paper}&gt;
					&lt;Avatar className={classes.avatar}&gt;
						&lt;LockOutlinedIcon /&gt;
					&lt;/Avatar&gt;
					&lt;Typography component="h1" variant="h5"&gt;
						Sign up
					&lt;/Typography&gt;
					&lt;form className={classes.form} noValidate&gt;
						&lt;Grid container spacing={2}&gt;
							&lt;Grid item xs={12} sm={6}&gt;
								&lt;TextField
									variant="outlined"
									required
									fullWidth
									id="firstName"
									label="First Name"
									name="firstName"
									autoComplete="firstName"
									helperText={errors.firstName}
									error={errors.firstName ? true : false}
									onChange={this.handleChange}
								/&gt;
							&lt;/Grid&gt;
							&lt;Grid item xs={12} sm={6}&gt;
								&lt;TextField
									variant="outlined"
									required
									fullWidth
									id="lastName"
									label="Last Name"
									name="lastName"
									autoComplete="lastName"
									helperText={errors.lastName}
									error={errors.lastName ? true : false}
									onChange={this.handleChange}
								/&gt;
							&lt;/Grid&gt;

							&lt;Grid item xs={12} sm={6}&gt;
								&lt;TextField
									variant="outlined"
									required
									fullWidth
									id="username"
									label="User Name"
									name="username"
									autoComplete="username"
									helperText={errors.username}
									error={errors.username ? true : false}
									onChange={this.handleChange}
								/&gt;
							&lt;/Grid&gt;

							&lt;Grid item xs={12} sm={6}&gt;
								&lt;TextField
									variant="outlined"
									required
									fullWidth
									id="phoneNumber"
									label="Phone Number"
									name="phoneNumber"
									autoComplete="phoneNumber"
									pattern="[7-9]{1}[0-9]{9}"
									helperText={errors.phoneNumber}
									error={errors.phoneNumber ? true : false}
									onChange={this.handleChange}
								/&gt;
							&lt;/Grid&gt;

							&lt;Grid item xs={12}&gt;
								&lt;TextField
									variant="outlined"
									required
									fullWidth
									id="email"
									label="Email Address"
									name="email"
									autoComplete="email"
									helperText={errors.email}
									error={errors.email ? true : false}
									onChange={this.handleChange}
								/&gt;
							&lt;/Grid&gt;

							&lt;Grid item xs={12}&gt;
								&lt;TextField
									variant="outlined"
									required
									fullWidth
									id="country"
									label="Country"
									name="country"
									autoComplete="country"
									helperText={errors.country}
									error={errors.country ? true : false}
									onChange={this.handleChange}
								/&gt;
							&lt;/Grid&gt;

							&lt;Grid item xs={12}&gt;
								&lt;TextField
									variant="outlined"
									required
									fullWidth
									name="password"
									label="Password"
									type="password"
									id="password"
									autoComplete="current-password"
									helperText={errors.password}
									error={errors.password ? true : false}
									onChange={this.handleChange}
								/&gt;
							&lt;/Grid&gt;
							&lt;Grid item xs={12}&gt;
								&lt;TextField
									variant="outlined"
									required
									fullWidth
									name="confirmPassword"
									label="Confirm Password"
									type="password"
									id="confirmPassword"
									autoComplete="current-password"
									onChange={this.handleChange}
								/&gt;
							&lt;/Grid&gt;
						&lt;/Grid&gt;
						&lt;Button
							type="submit"
							fullWidth
							variant="contained"
							color="primary"
							className={classes.submit}
							onClick={this.handleSubmit}
                            disabled={loading || 
                                !this.state.email || 
                                !this.state.password ||
                                !this.state.firstName || 
                                !this.state.lastName ||
                                !this.state.country || 
                                !this.state.username || 
                                !this.state.phoneNumber}
						&gt;
							Sign Up
							{loading &amp;&amp; &lt;CircularProgress size={30} className={classes.progess} /&gt;}
						&lt;/Button&gt;
						&lt;Grid container justify="flex-end"&gt;
							&lt;Grid item&gt;
								&lt;Link href="login" variant="body2"&gt;
									Already have an account? Sign in
								&lt;/Link&gt;
							&lt;/Grid&gt;
						&lt;/Grid&gt;
					&lt;/form&gt;
				&lt;/div&gt;
			&lt;/Container&gt;
		);
	}
}</code></pre><p>No fim desse arquivo, adicione a seguinte exportação:</p><pre><code class="language-javascript">export default withStyles(styles)(signup);</code></pre><p>A lógica para o componente <strong>Signup</strong> é a mesma do componente de login. Vá até <a href="http://localhost:3000/signup">http://localhost:3000/signup</a> no navegador e você verá a interface de registro abaixo. Quando o registro for feito com sucesso, seremos redirecionados para a página inicial.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/SignupPage.png" class="kg-image" alt="SignupPage" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/SignupPage.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/SignupPage.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/SignupPage.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="894" loading="lazy"><figcaption>Formulário de registro</figcaption></figure><p>Tente preencher credenciais erradas ou enviar uma requisição vazia e você terá erros. Envie uma requisição válida. Vá até <strong>Developer console &gt; Application</strong>. Você verá que o token dos usuários está armazenado no armazenamento local.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/DevConsoleSignup.png" class="kg-image" alt="DevConsoleSignup" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/DevConsoleSignup.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/DevConsoleSignup.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/DevConsoleSignup.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="385" loading="lazy"><figcaption>Console de Desenvolvedor do Chrome</figcaption></figure><h3 id="4-se-o-das-contas-">4. Seção das contas:</h3><p>Para construir a página de contas, vamos precisar primeiro criar nossa <strong>página inicial</strong>, que será onde carregaremos a <strong>seção da conta</strong>. Vá até <code>App.js</code> e atualize a seguinte rota:</p><pre><code class="language-javascript">// App.js

&lt;Route exact path="/" component={home}/&gt;</code></pre><p>Não se esqueça de importar:</p><pre><code class="language-javascript">// App.js

import home from './pages/home';</code></pre><p>Crie um arquivo chamado <code>home.js</code>. Esse arquivo será o índice da nossa aplicação. As seções sobre Contas e Afazeres serão carregadas com base nessa página no clique do botão.</p><p>Importe os pacotes do Material UI, o pacote Axios, nossa Conta personalizada, componentes de afazeres e middleware de autenticação.</p><pre><code class="language-javascript">// home.js

import React, { Component } from 'react';
import axios from 'axios';

import Account from '../components/account';
import Todo from '../components/todo';

import Drawer from '@material-ui/core/Drawer';
import AppBar from '@material-ui/core/AppBar';
import CssBaseline from '@material-ui/core/CssBaseline';
import Toolbar from '@material-ui/core/Toolbar';
import List from '@material-ui/core/List';
import Typography from '@material-ui/core/Typography';
import Divider from '@material-ui/core/Divider';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import withStyles from '@material-ui/core/styles/withStyles';
import AccountBoxIcon from '@material-ui/icons/AccountBox';
import NotesIcon from '@material-ui/icons/Notes';
import Avatar from '@material-ui/core/avatar';
import ExitToAppIcon from '@material-ui/icons/ExitToApp';
import CircularProgress from '@material-ui/core/CircularProgress';

import { authMiddleWare } from '../util/auth'</code></pre><p>Vamos definir nosso <em>drawerWidth</em> assim:</p><pre><code class="language-javascript">const drawerWidth = 240;</code></pre><p>Vamos adicionar a seguinte estilização na nossa página inicial:</p><pre><code class="language-javascript">const styles = (theme) =&gt; ({
	root: {
		display: 'flex'
	},
	appBar: {
		zIndex: theme.zIndex.drawer + 1
	},
	drawer: {
		width: drawerWidth,
		flexShrink: 0
	},
	drawerPaper: {
		width: drawerWidth
	},
	content: {
		flexGrow: 1,
		padding: theme.spacing(3)
	},
	avatar: {
		height: 110,
		width: 100,
		flexShrink: 0,
		flexGrow: 0,
		marginTop: 20
	},
	uiProgess: {
		position: 'fixed',
		zIndex: '1000',
		height: '31px',
		width: '31px',
		left: '50%',
		top: '35%'
	},
	toolbar: theme.mixins.toolbar
});</code></pre><p>Vamos criar uma classe chamada <em>home</em>. Essa classe terá uma chamada de API para obter a foto de perfil do usuário, primeiro e último nome. Também terá a lógica para escolher qual componente mostrar, se o dos Afazeres ou o das Contas:</p><pre><code class="language-javascript">class home extends Component {
	state = {
		render: false
	};

	loadAccountPage = (event) =&gt; {
		this.setState({ render: true });
	};

	loadTodoPage = (event) =&gt; {
		this.setState({ render: false });
	};

	logoutHandler = (event) =&gt; {
		localStorage.removeItem('AuthToken');
		this.props.history.push('/login');
	};

	constructor(props) {
		super(props);

		this.state = {
			firstName: '',
			lastName: '',
			profilePicture: '',
			uiLoading: true,
			imageLoading: false
		};
	}

	componentWillMount = () =&gt; {
		authMiddleWare(this.props.history);
		const authToken = localStorage.getItem('AuthToken');
		axios.defaults.headers.common = { Authorization: `${authToken}` };
		axios
			.get('/user')
			.then((response) =&gt; {
				console.log(response.data);
				this.setState({
					firstName: response.data.userCredentials.firstName,
					lastName: response.data.userCredentials.lastName,
					email: response.data.userCredentials.email,
					phoneNumber: response.data.userCredentials.phoneNumber,
					country: response.data.userCredentials.country,
					username: response.data.userCredentials.username,
					uiLoading: false,
					profilePicture: response.data.userCredentials.imageUrl
				});
			})
			.catch((error) =&gt; {
				if(error.response.status === 403) {
					this.props.history.push('/login')
				}
				console.log(error);
				this.setState({ errorMsg: 'Error in retrieving the data' });
			});
	};

	render() {
		const { classes } = this.props;		
		if (this.state.uiLoading === true) {
			return (
				&lt;div className={classes.root}&gt;
					{this.state.uiLoading &amp;&amp; &lt;CircularProgress size={150} className={classes.uiProgess} /&gt;}
				&lt;/div&gt;
			);
		} else {
			return (
				&lt;div className={classes.root}&gt;
					&lt;CssBaseline /&gt;
					&lt;AppBar position="fixed" className={classes.appBar}&gt;
						&lt;Toolbar&gt;
							&lt;Typography variant="h6" noWrap&gt;
								TodoApp
							&lt;/Typography&gt;
						&lt;/Toolbar&gt;
					&lt;/AppBar&gt;
					&lt;Drawer
						className={classes.drawer}
						variant="permanent"
						classes={{
							paper: classes.drawerPaper
						}}
					&gt;
						&lt;div className={classes.toolbar} /&gt;
						&lt;Divider /&gt;
						&lt;center&gt;
							&lt;Avatar src={this.state.profilePicture} className={classes.avatar} /&gt;
							&lt;p&gt;
								{' '}
								{this.state.firstName} {this.state.lastName}
							&lt;/p&gt;
						&lt;/center&gt;
						&lt;Divider /&gt;
						&lt;List&gt;
							&lt;ListItem button key="Todo" onClick={this.loadTodoPage}&gt;
								&lt;ListItemIcon&gt;
									{' '}
									&lt;NotesIcon /&gt;{' '}
								&lt;/ListItemIcon&gt;
								&lt;ListItemText primary="Todo" /&gt;
							&lt;/ListItem&gt;

							&lt;ListItem button key="Account" onClick={this.loadAccountPage}&gt;
								&lt;ListItemIcon&gt;
									{' '}
									&lt;AccountBoxIcon /&gt;{' '}
								&lt;/ListItemIcon&gt;
								&lt;ListItemText primary="Account" /&gt;
							&lt;/ListItem&gt;

							&lt;ListItem button key="Logout" onClick={this.logoutHandler}&gt;
								&lt;ListItemIcon&gt;
									{' '}
									&lt;ExitToAppIcon /&gt;{' '}
								&lt;/ListItemIcon&gt;
								&lt;ListItemText primary="Logout" /&gt;
							&lt;/ListItem&gt;
						&lt;/List&gt;
					&lt;/Drawer&gt;

					&lt;div&gt;{this.state.render ? &lt;Account /&gt; : &lt;Todo /&gt;}&lt;/div&gt;
				&lt;/div&gt;
			);
		}
	}
}</code></pre><p>Aqui no código, você verá que <code>authMiddleWare(this.props.history);</code> é usado. Esse middleware verifica se <code>authToken</code> é nulo. Se for, ele direcionará o usuário de volta para <code>login.js</code>. Isso foi adicionado para que nosso usuário não possa acessar a rota <code>/</code> sem se registrar ou realizar login. No fim desse arquivo, adicione a seguinte exportação:</p><pre><code class="language-javascript">export default withStyles(styles)(home);</code></pre><p>Agora, você deve estar se perguntando o que este código em <code>home.js</code> faz?</p><pre><code class="language-javascript">&lt;div&gt;{this.state.render ? &lt;Account /&gt; : &lt;Todo /&gt;}&lt;/div&gt;</code></pre><p>Ele está verificando o estado de renderização que estamos definindo no clique do botão. Vamos criar o diretório <strong>component</strong> e, nesse diretório, criaremos dois arquivos: <code>account.js</code> &nbsp;e &nbsp;<code>todo.js</code>.</p><p>Vamos criar um diretório chamado <strong>util</strong> e um arquivo chamado <code>auth.js</code> nesse diretório. Copie e cole o seguinte código em <code>auth.js</code>:</p><pre><code class="language-javascript">export const authMiddleWare = (history) =&gt; {
    const authToken = localStorage.getItem('AuthToken');
    if(authToken === null){
        history.push('/login')
    }
}</code></pre><p>Por agora, dentro do arquivo <code>todo.js</code>, vamos apenas escrever uma classe que renderiza o texto <strong>Hello I am todo</strong>. Trabalharemos em nossos afazeres na próxima seção:</p><pre><code class="language-javascript">import React, { Component } from 'react'

import withStyles from '@material-ui/core/styles/withStyles';
import Typography from '@material-ui/core/Typography';

const styles = ((theme) =&gt; ({
    content: {
        flexGrow: 1,
        padding: theme.spacing(3),
    },
    toolbar: theme.mixins.toolbar,
    })
);

class todo extends Component {
    render() {
        const { classes } = this.props;
        return (
            &lt;main className={classes.content}&gt;
            &lt;div className={classes.toolbar} /&gt;
            &lt;Typography paragraph&gt;
                Hello I am todo
            &lt;/Typography&gt;
            &lt;/main&gt;
        )
    }
}

export default (withStyles(styles)(todo));</code></pre><p>Agora, é hora de fazer a seção das contas. Importe o Material UI, o clsx, o axios e o authmiddleWare em <code>account.js</code>.</p><pre><code class="language-javascript">// account.js

import React, { Component } from 'react';

import withStyles from '@material-ui/core/styles/withStyles';
import Typography from '@material-ui/core/Typography';
import CircularProgress from '@material-ui/core/CircularProgress';
import CloudUploadIcon from '@material-ui/icons/CloudUpload';
import { Card, CardActions, CardContent, Divider, Button, Grid, TextField } from '@material-ui/core';

import clsx from 'clsx';

import axios from 'axios';
import { authMiddleWare } from '../util/auth';</code></pre><p>Vamos adicionar a seguinte estilização à nossa página de contas:</p><pre><code class="language-javascript">// account.js

const styles = (theme) =&gt; ({
	content: {
		flexGrow: 1,
		padding: theme.spacing(3)
	},
	toolbar: theme.mixins.toolbar,
	root: {},
	details: {
		display: 'flex'
	},
	avatar: {
		height: 110,
		width: 100,
		flexShrink: 0,
		flexGrow: 0
	},
	locationText: {
		paddingLeft: '15px'
	},
	buttonProperty: {
		position: 'absolute',
		top: '50%'
	},
	uiProgess: {
		position: 'fixed',
		zIndex: '1000',
		height: '31px',
		width: '31px',
		left: '50%',
		top: '35%'
	},
	progess: {
		position: 'absolute'
	},
	uploadButton: {
		marginLeft: '8px',
		margin: theme.spacing(1)
	},
	customError: {
		color: 'red',
		fontSize: '0.8rem',
		marginTop: 10
	},
	submitButton: {
		marginTop: '10px'
	}
});</code></pre><p>Vamos criar um componente de classe chamado <strong>account</strong>. Por agora, apenas copie e cole o seguinte código:</p><pre><code class="language-javascript">// account.js

class account extends Component {
	constructor(props) {
		super(props);

		this.state = {
			firstName: '',
			lastName: '',
			email: '',
			phoneNumber: '',
			username: '',
			country: '',
			profilePicture: '',
			uiLoading: true,
			buttonLoading: false,
			imageError: ''
		};
	}

	componentWillMount = () =&gt; {
		authMiddleWare(this.props.history);
		const authToken = localStorage.getItem('AuthToken');
		axios.defaults.headers.common = { Authorization: `${authToken}` };
		axios
			.get('/user')
			.then((response) =&gt; {
				console.log(response.data);
				this.setState({
					firstName: response.data.userCredentials.firstName,
					lastName: response.data.userCredentials.lastName,
					email: response.data.userCredentials.email,
					phoneNumber: response.data.userCredentials.phoneNumber,
					country: response.data.userCredentials.country,
					username: response.data.userCredentials.username,
					uiLoading: false
				});
			})
			.catch((error) =&gt; {
				if (error.response.status === 403) {
					this.props.history.push('/login');
				}
				console.log(error);
				this.setState({ errorMsg: 'Error in retrieving the data' });
			});
	};

	handleChange = (event) =&gt; {
		this.setState({
			[event.target.name]: event.target.value
		});
	};

	handleImageChange = (event) =&gt; {
		this.setState({
			image: event.target.files[0]
		});
	};

	profilePictureHandler = (event) =&gt; {
		event.preventDefault();
		this.setState({
			uiLoading: true
		});
		authMiddleWare(this.props.history);
		const authToken = localStorage.getItem('AuthToken');
		let form_data = new FormData();
		form_data.append('image', this.state.image);
		form_data.append('content', this.state.content);
		axios.defaults.headers.common = { Authorization: `${authToken}` };
		axios
			.post('/user/image', form_data, {
				headers: {
					'content-type': 'multipart/form-data'
				}
			})
			.then(() =&gt; {
				window.location.reload();
			})
			.catch((error) =&gt; {
				if (error.response.status === 403) {
					this.props.history.push('/login');
				}
				console.log(error);
				this.setState({
					uiLoading: false,
					imageError: 'Error in posting the data'
				});
			});
	};

	updateFormValues = (event) =&gt; {
		event.preventDefault();
		this.setState({ buttonLoading: true });
		authMiddleWare(this.props.history);
		const authToken = localStorage.getItem('AuthToken');
		axios.defaults.headers.common = { Authorization: `${authToken}` };
		const formRequest = {
			firstName: this.state.firstName,
			lastName: this.state.lastName,
			country: this.state.country
		};
		axios
			.post('/user', formRequest)
			.then(() =&gt; {
				this.setState({ buttonLoading: false });
			})
			.catch((error) =&gt; {
				if (error.response.status === 403) {
					this.props.history.push('/login');
				}
				console.log(error);
				this.setState({
					buttonLoading: false
				});
			});
	};

	render() {
		const { classes, ...rest } = this.props;
		if (this.state.uiLoading === true) {
			return (
				&lt;main className={classes.content}&gt;
					&lt;div className={classes.toolbar} /&gt;
					{this.state.uiLoading &amp;&amp; &lt;CircularProgress size={150} className={classes.uiProgess} /&gt;}
				&lt;/main&gt;
			);
		} else {
			return (
				&lt;main className={classes.content}&gt;
					&lt;div className={classes.toolbar} /&gt;
					&lt;Card {...rest} className={clsx(classes.root, classes)}&gt;
						&lt;CardContent&gt;
							&lt;div className={classes.details}&gt;
								&lt;div&gt;
									&lt;Typography className={classes.locationText} gutterBottom variant="h4"&gt;
										{this.state.firstName} {this.state.lastName}
									&lt;/Typography&gt;
									&lt;Button
										variant="outlined"
										color="primary"
										type="submit"
										size="small"
										startIcon={&lt;CloudUploadIcon /&gt;}
										className={classes.uploadButton}
										onClick={this.profilePictureHandler}
									&gt;
										Upload Photo
									&lt;/Button&gt;
									&lt;input type="file" onChange={this.handleImageChange} /&gt;

									{this.state.imageError ? (
										&lt;div className={classes.customError}&gt;
											{' '}
											Wrong Image Format || Supported Format are PNG and JPG
										&lt;/div&gt;
									) : (
										false
									)}
								&lt;/div&gt;
							&lt;/div&gt;
							&lt;div className={classes.progress} /&gt;
						&lt;/CardContent&gt;
						&lt;Divider /&gt;
					&lt;/Card&gt;

					&lt;br /&gt;
					&lt;Card {...rest} className={clsx(classes.root, classes)}&gt;
						&lt;form autoComplete="off" noValidate&gt;
							&lt;Divider /&gt;
							&lt;CardContent&gt;
								&lt;Grid container spacing={3}&gt;
									&lt;Grid item md={6} xs={12}&gt;
										&lt;TextField
											fullWidth
											label="First name"
											margin="dense"
											name="firstName"
											variant="outlined"
											value={this.state.firstName}
											onChange={this.handleChange}
										/&gt;
									&lt;/Grid&gt;
									&lt;Grid item md={6} xs={12}&gt;
										&lt;TextField
											fullWidth
											label="Last name"
											margin="dense"
											name="lastName"
											variant="outlined"
											value={this.state.lastName}
											onChange={this.handleChange}
										/&gt;
									&lt;/Grid&gt;
									&lt;Grid item md={6} xs={12}&gt;
										&lt;TextField
											fullWidth
											label="Email"
											margin="dense"
											name="email"
											variant="outlined"
											disabled={true}
											value={this.state.email}
											onChange={this.handleChange}
										/&gt;
									&lt;/Grid&gt;
									&lt;Grid item md={6} xs={12}&gt;
										&lt;TextField
											fullWidth
											label="Phone Number"
											margin="dense"
											name="phone"
											type="number"
											variant="outlined"
											disabled={true}
											value={this.state.phoneNumber}
											onChange={this.handleChange}
										/&gt;
									&lt;/Grid&gt;
									&lt;Grid item md={6} xs={12}&gt;
										&lt;TextField
											fullWidth
											label="User Name"
											margin="dense"
											name="userHandle"
											disabled={true}
											variant="outlined"
											value={this.state.username}
											onChange={this.handleChange}
										/&gt;
									&lt;/Grid&gt;
									&lt;Grid item md={6} xs={12}&gt;
										&lt;TextField
											fullWidth
											label="Country"
											margin="dense"
											name="country"
											variant="outlined"
											value={this.state.country}
											onChange={this.handleChange}
										/&gt;
									&lt;/Grid&gt;
								&lt;/Grid&gt;
							&lt;/CardContent&gt;
							&lt;Divider /&gt;
							&lt;CardActions /&gt;
						&lt;/form&gt;
					&lt;/Card&gt;
					&lt;Button
						color="primary"
						variant="contained"
						type="submit"
						className={classes.submitButton}
						onClick={this.updateFormValues}
						disabled={
							this.state.buttonLoading ||
							!this.state.firstName ||
							!this.state.lastName ||
							!this.state.country
						}
					&gt;
						Save details
						{this.state.buttonLoading &amp;&amp; &lt;CircularProgress size={30} className={classes.progess} /&gt;}
					&lt;/Button&gt;
				&lt;/main&gt;
			);
		}
	}
}</code></pre><p>No fim desse arquivo, adicione a seguinte exportação:</p><pre><code class="language-javascript">export default withStyles(styles)(account);</code></pre><p>Em <code>account.js</code>, há vários componentes sendo usados. Vamos ver como está nossa aplicação. Depois disso, vamos explicar todos os componentes que estão sendo usados e o motivo de estarem sendo usados.</p><p>Vá até o navegador. Se o token expirou, ele redirecionará você para a página de <code>login</code>. Adicione seus detalhes e faça login novamente. Quando fizer isso, vá até a aba <code>Account</code> e você verá a seguinte interface:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/image-88.png" class="kg-image" alt="image-88" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/image-88.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/image-88.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/image-88.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="900" loading="lazy"><figcaption>Seção da conta</figcaption></figure><p>Há 3 funções que lidam com questões diferentes na seção das contas:</p><ol><li><strong>componentWillMount</strong>: este é o método incorporado ao ciclo de vida do React. Estamos usando esse método para carregar os dados antes do ciclo de vida de renderização e para atualizar nossos valores do estado.</li><li><strong>ProfilePictureUpdate:</strong> esta é nossa função personalizada que estamos usando para lidar com o clique do usuário no botão de carregar foto. Ela enviará os dados para um servidor e recarregará a página para mostrar a nova foto de perfil do usuário.</li><li><strong>updateFormValues:</strong> esta também é nossa função personalizada que lida com a atualização dos detalhes do usuário. Aqui, o usuário pode atualizar seu primeiro nome, sobrenome e país. Não estamos permitindo a atualização de e-mail e nome de usuário, pois nossa lógica do back-end depende dessas chaves.</li></ol><p>Além dessas três funções, há uma página de formulário estilizada. Aqui está a estrutura do diretório até aqui dentro da pasta <strong>view</strong>:</p><pre><code class="language-bash">+-- public 
+-- src
|   +-- components
|   +-- +-- todo.js
|   +-- +-- account.js
|   +-- pages
|   +-- +-- home.js
|   +-- +-- login.js
|   +-- +-- signup.js
|   +-- util
|   +-- +-- auth.js 
|   +-- README.md
|   +-- package-lock.json
|   +-- package.json
|   +-- .gitignore</code></pre><p>Com isso, completamos nosso painel de contas. Agora tome um café, faça uma pausa e, na próxima seção, vamos criar o painel de afazeres.</p><h2 id="se-o-4-painel-de-afazeres">Seção 4: painel de afazeres</h2><p>Nesta seção, vamos desenvolver a interface para essas funcionalidades do painel de afazeres:</p><ol><li><strong>Adicionar um afazer</strong></li><li><strong>Obter todos os afazeres</strong></li><li><strong>Excluir um afazer</strong></li><li><strong>Editar um afazer</strong></li><li><strong>Obter um afazer</strong></li><li><strong>Aplicar o tema</strong></li></ol><p>O código do painel de afazeres implementado nesta seção pode ser encontrado neste <a href="https://github.com/Sharvin26/TodoApp/tree/3799980aa13eeb8d313e17d83aa3032748aedb00/view">commit</a>.</p><p>Vá até <code>todos.js</code> no diretório <strong>components</strong>. Adicione as seguintes importações às importações existentes:</p><pre><code class="language-javascript">import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import AddCircleIcon from '@material-ui/icons/AddCircle';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import IconButton from '@material-ui/core/IconButton';
import CloseIcon from '@material-ui/icons/Close';
import Slide from '@material-ui/core/Slide';
import TextField from '@material-ui/core/TextField';
import Grid from '@material-ui/core/Grid';
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CircularProgress from '@material-ui/core/CircularProgress';
import CardContent from '@material-ui/core/CardContent';
import MuiDialogTitle from '@material-ui/core/DialogTitle';
import MuiDialogContent from '@material-ui/core/DialogContent';

import axios from 'axios';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import { authMiddleWare } from '../util/auth';</code></pre><p>Precisamos, também, adicionar os seguintes elementos do CSS nos componentes de estilo abaixo:</p><pre><code class="language-javascript">const styles = (theme) =&gt; ({
	.., // Existing CSS elements
	title: {
		marginLeft: theme.spacing(2),
		flex: 1
	},
	submitButton: {
		display: 'block',
		color: 'white',
		textAlign: 'center',
		position: 'absolute',
		top: 14,
		right: 10
	},
	floatingButton: {
		position: 'fixed',
		bottom: 0,
		right: 0
	},
	form: {
		width: '98%',
		marginLeft: 13,
		marginTop: theme.spacing(3)
	},
	toolbar: theme.mixins.toolbar,
	root: {
		minWidth: 470
	},
	bullet: {
		display: 'inline-block',
		margin: '0 2px',
		transform: 'scale(0.8)'
	},
	pos: {
		marginBottom: 12
	},
	uiProgess: {
		position: 'fixed',
		zIndex: '1000',
		height: '31px',
		width: '31px',
		left: '50%',
		top: '35%'
	},
	dialogeStyle: {
		maxWidth: '50%'
	},
	viewRoot: {
		margin: 0,
		padding: theme.spacing(2)
	},
	closeButton: {
		position: 'absolute',
		right: theme.spacing(1),
		top: theme.spacing(1),
		color: theme.palette.grey[500]
	}
});</code></pre><p>Vamos adicionar a transição para o pop-up da caixa de diálogo:</p><pre><code class="language-javascript">const Transition = React.forwardRef(function Transition(props, ref) {
	return &lt;Slide direction="up" ref={ref} {...props} /&gt;;
});</code></pre><p>Remova a classe todo abaixo e copie e cole a seguinte classe:</p><pre><code class="language-javascript">class todo extends Component {
	constructor(props) {
		super(props);

		this.state = {
			todos: '',
			title: '',
			body: '',
			todoId: '',
			errors: [],
			open: false,
			uiLoading: true,
			buttonType: '',
			viewOpen: false
		};

		this.deleteTodoHandler = this.deleteTodoHandler.bind(this);
		this.handleEditClickOpen = this.handleEditClickOpen.bind(this);
		this.handleViewOpen = this.handleViewOpen.bind(this);
	}

	handleChange = (event) =&gt; {
		this.setState({
			[event.target.name]: event.target.value
		});
	};

	componentWillMount = () =&gt; {
		authMiddleWare(this.props.history);
		const authToken = localStorage.getItem('AuthToken');
		axios.defaults.headers.common = { Authorization: `${authToken}` };
		axios
			.get('/todos')
			.then((response) =&gt; {
				this.setState({
					todos: response.data,
					uiLoading: false
				});
			})
			.catch((err) =&gt; {
				console.log(err);
			});
	};

	deleteTodoHandler(data) {
		authMiddleWare(this.props.history);
		const authToken = localStorage.getItem('AuthToken');
		axios.defaults.headers.common = { Authorization: `${authToken}` };
		let todoId = data.todo.todoId;
		axios
			.delete(`todo/${todoId}`)
			.then(() =&gt; {
				window.location.reload();
			})
			.catch((err) =&gt; {
				console.log(err);
			});
	}

	handleEditClickOpen(data) {
		this.setState({
			title: data.todo.title,
			body: data.todo.body,
			todoId: data.todo.todoId,
			buttonType: 'Edit',
			open: true
		});
	}

	handleViewOpen(data) {
		this.setState({
			title: data.todo.title,
			body: data.todo.body,
			viewOpen: true
		});
	}

	render() {
		const DialogTitle = withStyles(styles)((props) =&gt; {
			const { children, classes, onClose, ...other } = props;
			return (
				&lt;MuiDialogTitle disableTypography className={classes.root} {...other}&gt;
					&lt;Typography variant="h6"&gt;{children}&lt;/Typography&gt;
					{onClose ? (
						&lt;IconButton aria-label="close" className={classes.closeButton} onClick={onClose}&gt;
							&lt;CloseIcon /&gt;
						&lt;/IconButton&gt;
					) : null}
				&lt;/MuiDialogTitle&gt;
			);
		});

		const DialogContent = withStyles((theme) =&gt; ({
			viewRoot: {
				padding: theme.spacing(2)
			}
		}))(MuiDialogContent);

		dayjs.extend(relativeTime);
		const { classes } = this.props;
		const { open, errors, viewOpen } = this.state;

		const handleClickOpen = () =&gt; {
			this.setState({
				todoId: '',
				title: '',
				body: '',
				buttonType: '',
				open: true
			});
		};

		const handleSubmit = (event) =&gt; {
			authMiddleWare(this.props.history);
			event.preventDefault();
			const userTodo = {
				title: this.state.title,
				body: this.state.body
			};
			let options = {};
			if (this.state.buttonType === 'Edit') {
				options = {
					url: `/todo/${this.state.todoId}`,
					method: 'put',
					data: userTodo
				};
			} else {
				options = {
					url: '/todo',
					method: 'post',
					data: userTodo
				};
			}
			const authToken = localStorage.getItem('AuthToken');
			axios.defaults.headers.common = { Authorization: `${authToken}` };
			axios(options)
				.then(() =&gt; {
					this.setState({ open: false });
					window.location.reload();
				})
				.catch((error) =&gt; {
					this.setState({ open: true, errors: error.response.data });
					console.log(error);
				});
		};

		const handleViewClose = () =&gt; {
			this.setState({ viewOpen: false });
		};

		const handleClose = (event) =&gt; {
			this.setState({ open: false });
		};

		if (this.state.uiLoading === true) {
			return (
				&lt;main className={classes.content}&gt;
					&lt;div className={classes.toolbar} /&gt;
					{this.state.uiLoading &amp;&amp; &lt;CircularProgress size={150} className={classes.uiProgess} /&gt;}
				&lt;/main&gt;
			);
		} else {
			return (
				&lt;main className={classes.content}&gt;
					&lt;div className={classes.toolbar} /&gt;

					&lt;IconButton
						className={classes.floatingButton}
						color="primary"
						aria-label="Add Todo"
						onClick={handleClickOpen}
					&gt;
						&lt;AddCircleIcon style={{ fontSize: 60 }} /&gt;
					&lt;/IconButton&gt;
					&lt;Dialog fullScreen open={open} onClose={handleClose} TransitionComponent={Transition}&gt;
						&lt;AppBar className={classes.appBar}&gt;
							&lt;Toolbar&gt;
								&lt;IconButton edge="start" color="inherit" onClick={handleClose} aria-label="close"&gt;
									&lt;CloseIcon /&gt;
								&lt;/IconButton&gt;
								&lt;Typography variant="h6" className={classes.title}&gt;
									{this.state.buttonType === 'Edit' ? 'Edit Todo' : 'Create a new Todo'}
								&lt;/Typography&gt;
								&lt;Button
									autoFocus
									color="inherit"
									onClick={handleSubmit}
									className={classes.submitButton}
								&gt;
									{this.state.buttonType === 'Edit' ? 'Save' : 'Submit'}
								&lt;/Button&gt;
							&lt;/Toolbar&gt;
						&lt;/AppBar&gt;

						&lt;form className={classes.form} noValidate&gt;
							&lt;Grid container spacing={2}&gt;
								&lt;Grid item xs={12}&gt;
									&lt;TextField
										variant="outlined"
										required
										fullWidth
										id="todoTitle"
										label="Todo Title"
										name="title"
										autoComplete="todoTitle"
										helperText={errors.title}
										value={this.state.title}
										error={errors.title ? true : false}
										onChange={this.handleChange}
									/&gt;
								&lt;/Grid&gt;
								&lt;Grid item xs={12}&gt;
									&lt;TextField
										variant="outlined"
										required
										fullWidth
										id="todoDetails"
										label="Todo Details"
										name="body"
										autoComplete="todoDetails"
										multiline
										rows={25}
										rowsMax={25}
										helperText={errors.body}
										error={errors.body ? true : false}
										onChange={this.handleChange}
										value={this.state.body}
									/&gt;
								&lt;/Grid&gt;
							&lt;/Grid&gt;
						&lt;/form&gt;
					&lt;/Dialog&gt;

					&lt;Grid container spacing={2}&gt;
						{this.state.todos.map((todo) =&gt; (
							&lt;Grid item xs={12} sm={6}&gt;
								&lt;Card className={classes.root} variant="outlined"&gt;
									&lt;CardContent&gt;
										&lt;Typography variant="h5" component="h2"&gt;
											{todo.title}
										&lt;/Typography&gt;
										&lt;Typography className={classes.pos} color="textSecondary"&gt;
											{dayjs(todo.createdAt).fromNow()}
										&lt;/Typography&gt;
										&lt;Typography variant="body2" component="p"&gt;
											{`${todo.body.substring(0, 65)}`}
										&lt;/Typography&gt;
									&lt;/CardContent&gt;
									&lt;CardActions&gt;
										&lt;Button size="small" color="primary" onClick={() =&gt; this.handleViewOpen({ todo })}&gt;
											{' '}
											View{' '}
										&lt;/Button&gt;
										&lt;Button size="small" color="primary" onClick={() =&gt; this.handleEditClickOpen({ todo })}&gt;
											Edit
										&lt;/Button&gt;
										&lt;Button size="small" color="primary" onClick={() =&gt; this.deleteTodoHandler({ todo })}&gt;
											Delete
										&lt;/Button&gt;
									&lt;/CardActions&gt;
								&lt;/Card&gt;
							&lt;/Grid&gt;
						))}
					&lt;/Grid&gt;

					&lt;Dialog
						onClose={handleViewClose}
						aria-labelledby="customized-dialog-title"
						open={viewOpen}
						fullWidth
						classes={{ paperFullWidth: classes.dialogeStyle }}
					&gt;
						&lt;DialogTitle id="customized-dialog-title" onClose={handleViewClose}&gt;
							{this.state.title}
						&lt;/DialogTitle&gt;
						&lt;DialogContent dividers&gt;
							&lt;TextField
								fullWidth
								id="todoDetails"
								name="body"
								multiline
								readonly
								rows={1}
								rowsMax={25}
								value={this.state.body}
								InputProps={{
									disableUnderline: true
								}}
							/&gt;
						&lt;/DialogContent&gt;
					&lt;/Dialog&gt;
				&lt;/main&gt;
			);
		}
	}
}</code></pre><p>No fim desse arquivo, adicione a seguinte exportação:</p><pre><code class="language-javascript">export default withStyles(styles)(todo);</code></pre><p>Primeiro, vamos entender como nossa interface funciona e, depois disso, vamos entender o código. Vá até o navegador e você verá a seguinte interface:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/TodoDashboard.png" class="kg-image" alt="TodoDashboard" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/TodoDashboard.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/TodoDashboard.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/TodoDashboard.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="898" loading="lazy"><figcaption>Painel de afazeres</figcaption></figure><p>Clique no botão <strong>Add</strong>,<strong> </strong>no canto inferior direito, e verá a seguinte tela:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/AddTodo.png" class="kg-image" alt="AddTodo" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/AddTodo.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/AddTodo.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/AddTodo.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="857" loading="lazy"><figcaption>Adicionar afazer</figcaption></figure><p>Adicione o título do afazer e os detalhes. Depois, pressione o botão de envio. Você verá a seguinte tela:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/Added-Todo.png" class="kg-image" alt="Added-Todo" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/Added-Todo.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/Added-Todo.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/Added-Todo.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="907" loading="lazy"><figcaption>Painel de afazeres</figcaption></figure><p>Depois desse clique no botão de visualização, você poderá ver os detalhes completos do afazer:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/View-Todo.png" class="kg-image" alt="View-Todo" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/View-Todo.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/View-Todo.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/View-Todo.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="901" loading="lazy"><figcaption>Ver afazer único</figcaption></figure><p>Clique no botão Edit e você poderá editar o afazer:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/EditTodo.png" class="kg-image" alt="EditTodo" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/EditTodo.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/EditTodo.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/EditTodo.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="864" loading="lazy"><figcaption>Editar afazer</figcaption></figure><p>Clique no botão de exclusão e você poderá excluir o afazer. Agora que sabemos como o painel funciona, entenderemos os componentes usados.</p><p><strong>1. Adicionar afazer:</strong> para adicionar um afazer, usaremos o <a href="https://material-ui.com/components/dialogs/#full-screen-dialogs">componente Dialogue</a> do Material UI. Esse componente implementa a funcionalidade <em>hook</em>. Usamos classes. Por isso, removeremos essa funcionalidade.</p><pre><code class="language-javascript">// This sets the state to open and buttonType flag to add:
const handleClickOpen = () =&gt; {
      this.setState({
           todoId: '',
           title: '',
           body: '',
           buttonType: '',
           open: true
     });
};

// This sets the state to close:
const handleClose = (event) =&gt; {
      this.setState({ open: false });
};</code></pre><p>Além disso, também mudaremos a posição do botão de adicionar afazeres.</p><pre><code class="language-javascript">// Position our button
floatingButton: {
    position: 'fixed',
    bottom: 0,
    right: 0
},

&lt;IconButton className={classes.floatingButton} ... &gt;</code></pre><p>Agora, substituiremos a <em>tag list</em> por <em>form </em>dentro deste <strong>Dialogue</strong>. Isso nos ajudará a adicionar o novo afazer.</p><pre><code class="language-javascript">// Show Edit or Save depending on buttonType state
{this.state.buttonType === 'Edit' ? 'Save' : 'Submit'}

// Our Form to add a todo
&lt;form className={classes.form} noValidate&gt;
	&lt;Grid container spacing={2}&gt;
		&lt;Grid item xs={12}&gt;
        // TextField here
        &lt;/Grid&gt;
        &lt;Grid item xs={12}&gt;
        // TextField here
        &lt;/Grid&gt;
    &lt;/Grid&gt;
&lt;/form&gt;</code></pre><p>A função <strong>handleSubmit</strong> consiste de lógica para realizar a leitura do estado <code>buttonType</code>. Se o estado for uma string vazia <code>("")</code>, realizará um post na API de adicionar afazer. Se o estado for um <code>Edit</code>, realizará um update na API editar afazer.</p><p><strong>2. Obter afazeres:</strong> para mostrar os afazeres vamos usar o <code>Grid container</code> e, dentro dele, colocaremos o <code>Grid item</code>. Dentro desse item, vamos usar um componente <code>Card</code> para mostrar os dados.</p><pre><code class="language-javascript">&lt;Grid container spacing={2}&gt;
    {this.state.todos.map((todo) =&gt; (
	&lt;Grid item xs={12} sm={6}&gt;
	&lt;Card className={classes.root} variant="outlined"&gt;
	    &lt;CardContent&gt;
        // Here will show Todo with view, edit and delete button
        &lt;/CardContent&gt;
    &lt;/Card&gt;
    &lt;/Grid&gt;))}
&lt;/Grid&gt;</code></pre><p>Usamos a função <em>map</em> para mostrar um afazer de cada vez, à medida que a API envia os afazeres como uma lista. Vamos usar a função de ciclo de vida <em>componentWillMount</em> para obter e enviar o estado antes da renderização ser executada. Há três botões (<strong>view, edit e delete</strong>). Vamos precisar de três funções para lidar com a operação quando o botão for clicado. Vamos aprender sobre esses botões nas respectivas subseções.</p><p><strong>3. Editar afazer:</strong> para editar o afazer, estamos reutilizando o código de pop-up de diálogo que está sendo usado para adicionar afazeres. Para diferenciar entre os cliques dos botões, usaremos um estado <code>buttonType</code>. Para adicionar um afazer, o estado <code>buttonType</code> é <code>("")</code>. Para editar um afazer, ele é <code>Edit</code>.</p><pre><code class="language-javascript">handleEditClickOpen(data) {
	this.setState({
		..,
		buttonType: 'Edit',
		..
	});
}</code></pre><p>No método <code>handleSubmit</code>, lemos o estado <code>buttonType</code> e enviamos a requisição de acordo.</p><p><strong>4. Excluir afazer:</strong> quando esse botão é clicado, enviamos o afazer como objeto para nosso <em>deleteTodoHandler</em>. Então, ele envia a requisição para o <em>back-end</em>.</p><pre><code class="language-javascript">&lt;Button size="small" onClick={() =&gt; this.deleteTodoHandler({ todo })}&gt;Delete&lt;/Button&gt;</code></pre><p><strong>5. Visualizar afazer:</strong> quando mostramos os dados, mostramos apenas uma parte deles. Assim, o usuário terá uma ideia sobre o assunto do afazer. Se, no entanto, o usuário quiser saber mais sobre ele, é preciso clicar no botão de visualizar.</p><p>Para isso, vamos usar o <a href="https://material-ui.com/components/dialogs/#customized-dialogs">diálogo customizado</a>. Dentro dele, usamos DialogTitle e DialogContent. Isso mostra nosso título e conteúdo. No DialougeContent, vamos usar o formulário para mostrar o conteúdo que o usuário postou (essa é uma solução que encontrei – há muitas e você pode tentar a que quiser).</p><pre><code class="language-javascript">// This is used to remove the underline of the Form
InputProps={{
       disableUnderline: true
}}

// This is used so that user cannot edit the data
readonly</code></pre><p><strong>6. Aplicar o tema:</strong> este é o último passo da nossa aplicação. Vamos aplicar um tema a ela. Para isso, usaremos <code>createMuiTheme</code> e <code>ThemeProvider</code>, do Material UI. Copie e cole o seguinte código em <code>App.js</code>:</p><pre><code class="language-javascript">import { ThemeProvider as MuiThemeProvider } from '@material-ui/core/styles';
import createMuiTheme from '@material-ui/core/styles/createMuiTheme';

const theme = createMuiTheme({
	palette: {
		primary: {
			light: '#33c9dc',
			main: '#FF5722',
			dark: '#d50000',
			contrastText: '#fff'
		}
	}
});

function App() {
	return (
        &lt;MuiThemeProvider theme={theme}&gt;
        // Router and switch will be here.
        &lt;/MuiThemeProvider&gt;
    );
}</code></pre><p>Esquecemos de aplicar um tema para nosso botão de <code>todo.js</code>, em <code>CardActions</code>. Adicione a <em>tag</em> <code>color</code> aos botões de visualização, edição e exclusão.</p><pre><code class="language-javascript">&lt;Button size="small" color="primary" ...&gt;</code></pre><p>Vá até o navegador e você verá que tudo está igual. A única diferença é a cor da aplicação.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/FinalTodo.png" class="kg-image" alt="FinalTodo" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/FinalTodo.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/FinalTodo.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/FinalTodo.png 1600w" sizes="(min-width: 720px) 720px" width="1600" height="900" loading="lazy"><figcaption>TodoApp depois de aplicar o tema</figcaption></figure><p>Assim, chegamos ao final do projeto! Criamos uma aplicação de afazeres (TodoApp) usando o ReactJS e o Firebase. Se chegou até aqui, meus parabéns por ter conseguido.</p><blockquote>Fique à vontade para se conectar com o autor pelo <a href="https://twitter.com/sharvinshah26">Twitter</a> e pelo <a href="https://github.com/Sharvin26">Github</a>.</blockquote> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Control-Alt-Delete no Mac - como abrir o Gerenciador de tarefas em seu Macbook ]]>
                </title>
                <description>
                    <![CDATA[ Acontece com todos nós: estamos trabalhando em um projeto importante e nosso computador trava. Também ocorre em um programa que estamos usando e que apenas para de responder. O que fazer nessa situação? Se você possui uma máquina Windows, você pode simplesmente usar a sequência CTRL+ALT+DEL para sair de qualquer ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/control-alt-delete-no-mac-como-abrir-o-gerenciador-de-tarefas-em-seu-macbook/</link>
                <guid isPermaLink="false">6342e3ed287f9a05e0d4c8bc</guid>
                
                    <category>
                        <![CDATA[ Mac ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ana Beatriz ]]>
                </dc:creator>
                <pubDate>Sun, 30 Oct 2022 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/5f9c9fae740569d1a4ca43f5.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/control-alt-delete-on-mac-how-to-open-task-manager-on-your-macbook/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Control Alt Delete on a Mac - How to Open Task Manager on your Macbook</a>
      </p><p>Acontece com todos nós: estamos trabalhando em um projeto importante e nosso computador trava. Também ocorre em um programa que estamos usando e que apenas para de responder. O que fazer nessa situação?</p><p>Se você possui uma máquina Windows, você pode simplesmente usar a sequência CTRL+ALT+DEL para sair de qualquer programa que está com comportamento ruim. Isso, no entanto, não funciona no Mac.</p><p>Não se preocupe - há uma maneira muito simples de forçar a saída de um programa no Mac (e vários outros métodos que pode guardar na manga também). Vamos aprender quais são.</p><h2 id="como-for-ar-a-sa-da-de-um-programa-em-um-mac">Como forçar a saída de um programa em um Mac</h2><p>A maneira mais fácil de forçar a saída de um programa em seu Mac é usar uma sequência simples de teclas, similar ao CTRL+ALT+DELETE. Simplesmente pressione COMMAND+OPTION+ESC, nesta ordem. Aqui está a localização destas teclas em um teclado típico do Mac:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/force-quit-keystrokes.png" class="kg-image" alt="force-quit-keystrokes" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/force-quit-keystrokes.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/force-quit-keystrokes.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/force-quit-keystrokes.png 1052w" sizes="(min-width: 720px) 720px" width="1052" height="610" loading="lazy"></figure><p>Isso mostrará uma janela semelhante à do Gerenciador de tarefas do Windows, assim:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/force-quit.png" class="kg-image" alt="force-quit" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/force-quit.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/force-quit.png 961w" sizes="(min-width: 720px) 720px" width="961" height="569" loading="lazy"><figcaption>Command+Option+ESC traz você para essa tela</figcaption></figure><p>Então, basta selecionar o programa que não está respondendo e pressionar "Force Quit" (Forçar Parada). Isso vai interromper a execução daquele programa.</p><p><strong>Observação</strong>: como você estará forçando aquele programa a parar no meio de qualquer coisa que esteja fazendo, os dados não salvos talvez sejam perdidos. Confira se você habilitou o salvamento automático, faça backup dos seus projetos frequentemente e deixe seu computador limpo e atualizado.</p><h2 id="um-m-todo-alternativo">Um método alternativo</h2><p>Por que aprender apenas uma maneira de forçar a saída quando você pode aprender duas? Clique no logotipo da Apple na parte superior esquerda da sua tela na barra do menu. Role para baixo até "Force Quit" (Forçar Parada) e você verá o mesmo "gerenciador de tarefas".</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/force-quit-way-2-1.png" class="kg-image" alt="force-quit-way-2-1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/force-quit-way-2-1.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/10/force-quit-way-2-1.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/force-quit-way-2-1.png 1095w" sizes="(min-width: 720px) 720px" width="1095" height="776" loading="lazy"><figcaption>Ao lado de Force Quit você verá o nome do programa que estiver usando atualmente</figcaption></figure><p>É simples assim!</p><p>Agora que lidou com o programa que travou, você pode voltar ao trabalho. :)</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Como usar o padrão Provider no Flutter ]]>
                </title>
                <description>
                    <![CDATA[ Neste artigo, vamos conferir o padrão provider no Flutter. Alguns outros padrões, como a Arquitetura BLoC, usam o padrão provider internamente. Ele, no entanto, é muito fácil de aprender e possui muito menos código repetitivo. Neste artigo, vamos usar o aplicativo padrão de Contador fornecido pelo Flutter e refatorá-lo para ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/como-usar-o-padrao-provider-no-flutter/</link>
                <guid isPermaLink="false">63388082ffecac05daeb62bc</guid>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ana Beatriz ]]>
                </dc:creator>
                <pubDate>Sun, 23 Oct 2022 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/flutter-provider-pattern.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/provider-pattern-in-flutter/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Use the Provider Pattern in Flutter</a>
      </p><p>Neste artigo, vamos conferir o padrão <em>provider</em> no Flutter. Alguns outros padrões, como a Arquitetura BLoC, usam o padrão <em>provider </em>internamente. Ele, no entanto, é muito fácil de aprender e possui muito menos código repetitivo.</p><p>Neste artigo, vamos usar o aplicativo padrão de Contador fornecido pelo Flutter e refatorá-lo para que use o padrão <em>provider</em>.</p><p>Se quiser saber o que a equipe do Flutter tem a dizer sobre o padrão <em>provider</em>, confira <a href="https://www.youtube.com/watch?v=d_m5csmrf7I">essa palestra de 2019</a> na sede do Google (vídeo em inglês).</p><p>Se quiser aprender mais sobre a arquitetura BLoC, <a href="https://ayusch.com/understanding-bloc-architecture-in-flutter/">confira aqui</a> (texto em inglês).</p><h3 id="come-ando">Começando</h3><p>Crie um projeto do Flutter e coloque nele o nome que quiser.</p><p>Primeiro, precisamos remover todos os comentários. Assim, teremos um código mais limpo para trabalhar:</p><pre><code class="language-dart">import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() =&gt; _MyHomePageState();
}

class _MyHomePageState extends State&lt;MyHomePage&gt; {
  int _counter = 0;
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &lt;Widget&gt;[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}</code></pre><p>Agora, adicionamos a dependência para o padrão <em>provider</em> no arquivo <code>pubspec.yaml</code>. Atualmente, a versão mais recente é a 4.1.2.</p><p>O arquivo <code>pubspec.yaml</code> ficará assim:</p><pre><code class="language-yaml">name: provider_pattern_explained
description: A new Flutter project.

publish_to: 'none' 

version: 1.0.0+1

environment:
  sdk: "&gt;=2.7.0 &lt;3.0.0"

dependencies:
  flutter:
    sdk: flutter
  provider: ^4.1.2

  cupertino_icons: ^0.1.3

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true</code></pre><p>O aplicativo padrão é basicamente um widget com monitoramento de estado, que é criado novamente toda vez que você clica em <code>FloatingActionButton</code> (que, por sua vez, chama <code>setState()</code>).</p><p>Agora, vamos convertê-lo em um widget sem monitoramento de estado.</p><h3 id="criando-o-provider">Criando o Provider</h3><p>Vamos criar nosso <em>provider</em>. Ele será a fonte da verdade do nosso aplicativo. É onde armazenaremos nosso <em>state </em>(estado) que, neste caso, é a contagem atual.</p><p>Crie uma classe chamada <code>Counter</code> e adicione a variável <code>count</code>:</p><pre><code class="language-dart">import 'package:flutter/material.dart';

class Counter {
  var _count = 0;
}</code></pre><p>Para convertê-la em uma classe <em>provider</em>, estenda <code>ChangeNotifier</code>, do pacote <code>material.dart</code>. Isso nos fornece o método <code>notifyListeners()</code> e notificará todas as funções ouvintes quando mudarmos um valor.</p><p>Depois, adicione um método para incrementar o contador:</p><pre><code class="language-dart">import 'package:flutter/material.dart';

class Counter extends ChangeNotifier {
  var _count = 0;
  void incrementCounter() {
    _count += 1;
  }
}</code></pre><p>No fim desse método, chamaremos <code>notifyListeners()</code>. Isso acionará uma mudança em todo o aplicativo para qualquer widget que esteja ouvindo.</p><p>Esta é a beleza do padrão <em>provider </em>no Flutter – você não precisa se importar com despachar manualmente para <em>streams</em>.</p><p>Por fim, crie uma função <em>getter</em> para retornar o valor do contador. Vamos usá-la para mostrar o valor mais atual:</p><pre><code class="language-dart">import 'package:flutter/material.dart';

class Counter extends ChangeNotifier {
  var _count = 0;
  int get getCounter {
    return _count;
  }

  void incrementCounter() {
    _count += 1;
    notifyListeners();
  }
}</code></pre><h2 id="ouvindo-cliques-no-bot-o"><strong>Ouvindo cliques no botão</strong></h2><p>Agora que temos o <em>provider </em>configurado, podemos continuar e usá-lo em nosso widget principal.</p><p>Primeiramente, vamos converter <code>MyHomePage</code> para um widget sem monitoramento de estado. Vamos ter que remover a chamada para a função <code>setState()</code>, pois ela está disponível apenas em um <code>StatefulWidget</code> (widget com monitoramento de estado):</p><pre><code class="language-dart">import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatelessWidget {
  int _counter = 0;
  final String title;
  MyHomePage({this.title});
  void _incrementCounter() {}
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &lt;Widget&gt;[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}</code></pre><p>Com isso feito, podemos usar o padrão <em>provider </em>no Flutter para indicar e obter o valor do contador. A cada clique no botão, precisamos incrementar o valor do contador em 1.</p><p>Então, no método <code>_incrementCounter</code> (que é chamado quando o botão é pressionado), adicione esta linha:</p><pre><code class="language-dart">Provider.of&lt;Counter&gt;(context, listen: false).incrementCounter();</code></pre><p>O que acontece aqui? Você pediu para o Flutter subir na <em>árvore de widget</em> e encontrar o primeiro lugar onde <code>Counter</code> é fornecido (mostrarei como fazer isso na próxima seção). É isso que <code>Provider.of()</code> faz.</p><p>Os genéricos (valores dentro dos <strong>&lt;&gt;</strong>) dizem ao Flutter qual tipo de <em>provider</em> procurar. Então, o Flutter sobe na árvore do widget até encontrar o valor fornecido. Se o valor não estiver fornecido em nenhum lugar, então uma exceção é criada.</p><p>Finalmente, quando você já tem o <em>provider</em>, pode chamar qualquer método a partir dele. Aqui, chamamos nosso método de <code>incrementCounter</code>.</p><p>Também precisamos de um contexto (em inglês, <em>context</em>). Então, aceitamos <code>context</code> como argumento e modificamos o método <code>onPressed</code> para que também passe <em>context</em>:</p><pre><code class="language-dart">void _incrementCounter(BuildContext context) {
  Provider.of&lt;Counter&gt;(context, listen: false).incrementCounter();
}</code></pre><p>Observação: indicamos <em>listen </em>como falso, pois não precisamos escutar nenhum valor aqui. Estamos apenas despachando uma ação que será executada.</p><h3 id="fornecendo-o-provider">Fornecendo o Provider</h3><p>O padrão provider no Flutter procurará o último valor fornecido. O diagrama abaixo ajudará você a entender melhor.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/Provider-Pattern-Flow.png" class="kg-image" alt="Provider-Pattern-Flow" width="511" height="421" loading="lazy"></figure><p>Neste diagrama, o objeto <strong>A VERDE</strong> estará disponível para o resto dos elementos abaixo dele, que são <strong>B, C, D, E,</strong> e <strong>F.</strong></p><p>Agora, suponha que queremos adicionar funcionalidade ao aplicativo e criamos outro <em>provider</em>, <strong>Z.</strong> Z precisa ser usado por E e F.</p><p>Onde é o melhor lugar para adicioná-lo?</p><p>Podemos adicioná-lo na raiz abaixo de <strong>A</strong>. Ficaria assim:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/Provider-Pattern-Flow-2.png" class="kg-image" alt="Provider-Pattern-Flow-2" width="511" height="561" loading="lazy"><figcaption>Tradução da frase: "Funciona, mas não é muito eficiente :("</figcaption></figure><p>Esse método, porém, não é muito eficiente.</p><p>O Flutter passará por todos os widgets abaixo e, então, finalmente, subirá até a raiz. Se você possui árvores de widget muito longas – algo que você provavelmente terá em um aplicativo em produção – não é uma boa ideia colocar tudo na raiz.</p><p>Ao invés disso, podemos olhar para o denominador comum de E e F, que é C. Se colocamos Z abaixo de E e F, dará certo.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/Provider-Pattern-Flow-3.png" class="kg-image" alt="Provider-Pattern-Flow-3" width="511" height="581" loading="lazy"><figcaption>Tradução da frase: "Funciona, mas será que podemos melhorar ainda mais?"</figcaption></figure><p>Se quisermos adicionar outro objeto <strong>X</strong>, requerido por E e F? Faremos a mesma coisa. Note, porém, como a árvore continua crescendo.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/Provider-Pattern-Flow-4.png" class="kg-image" alt="Provider-Pattern-Flow-4" width="511" height="816" loading="lazy"><figcaption>Tradução das frases: "E assim por diante. Uma árvore muito longa"</figcaption></figure><p>Há uma maneira melhor de gerenciar isso. Que tal se providenciarmos todos os objetos em um nível?</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/10/Provider-Pattern-Flow-5.png" class="kg-image" alt="Provider-Pattern-Flow-5" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/10/Provider-Pattern-Flow-5.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/10/Provider-Pattern-Flow-5.png 651w" width="651" height="541" loading="lazy"></figure><p>Isso é perfeito. Será a maneira de implementarmos nosso padrão <em>provider </em>no Flutter. Usaremos algo chamado <code>MultiProvider</code>, que nos deixa declarar diversos <em>providers</em> em um nível.</p><p>Deixaremos <code>MultiProvider</code> cercar o widget <code>MaterialApp</code>:</p><pre><code class="language-dart">class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider.value(
          value: Counter(),
        ),
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: MyHomePage(title: "AndroidVille Provider Pattern"),
      ),
    );
  }
}</code></pre><p>Com isso, fornecemos o <em>provider </em>para nossa árvore de widget e podemos usá-lo em qualquer lugar abaixo deste nível da árvore.</p><p>Há apenas mais uma coisa a fazer: precisamos atualizar o valor que é mostrado.</p><h2 id="atualizando-o-texto"><strong>Atualizando o texto</strong></h2><p>Para atualizar o texto, obtenha o <em>provider </em>na função do widget <code>MyHomePage</code>. Vamos usar o getter que criamos para obter o valor mais recente.</p><p>Então, simplesmente adicione esse valor ao widget de texto abaixo.</p><p>Terminamos! Nosso arquivo <code>main.dart</code> ficará assim no final:</p><pre><code class="language-dart">import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_pattern_explained/counter.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider.value(
          value: Counter(),
        ),
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: MyHomePage(title: "AndroidVille Provider Pattern"),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String title;
  MyHomePage({this.title});
  void _incrementCounter(BuildContext context) {
    Provider.of&lt;Counter&gt;(context, listen: false).incrementCounter();
  }

  @override
  Widget build(BuildContext context) {
    var counter = Provider.of&lt;Counter&gt;(context).getCounter;
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &lt;Widget&gt;[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () =&gt; _incrementCounter(context),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}</code></pre><p>Observação: não definimos <code>listen:false</code> nesse caso, pois queremos ficar sabendo de qualquer atualização no valor do contador.</p><p>Aqui está o código fonte no GitHub se você quiser ver: &nbsp;<a href="https://github.com/Ayusch/Flutter-Provider-Pattern">https://github.com/Ayusch/Flutter-Provider-Pattern</a>.</p><p>Peço que informe se tiver qualquer problema.</p><h2 id="boas-vindas-androidville-">Boas-vindas à AndroidVille :)</h2><p>A AndroidVille é uma comunidade de desenvolvedores para dispositivos móveis onde compartilhamos conhecimento relacionado com o desenvolvimento para Android, desenvolvimento em Flutter, tutoriais sobre React Native, Java, Kotlin e muito mais.</p><p><a href="https://rebrand.ly/73lbl3">Clique neste link para juntar-se ao SLACK do AndroidVille. É totalmente gratuito!</a></p><p>Se você gostou deste artigo, fique à vontade para compartilhá-lo no Facebook ou LinkedIn. Você pode seguir o autor no <a href="https://www.linkedin.com/in/ayuschjain">LinkedIn</a>, no <a href="https://twitter.com/ayuschjain">Twitter</a>, no <a href="https://www.quora.com/profile/Ayusch-Jain">Quora</a> e no <a href="https://medium.com/@jain.ayusch10">Medium</a>, onde ele responde perguntas relacionadas ao desenvolvimento para dispositivos móveis, Android e Flutter.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Como criar uma aplicação em React Native e integrá-la com Firebase ]]>
                </title>
                <description>
                    <![CDATA[ Neste tutorial, vamos construir uma aplicação em React Native integrada ao Firebase no back-end. A aplicação também terá suporte da React Native CLI e da Expo CLI. Este tutorial de React Native com Firebase abrangerá as funcionalidades principais, como autenticação, registro e operações CRUD no banco de dados (Firestore). Você ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/como-criar-uma-aplicacao-em-react-native-app-and-integrate-e-integra-la-com-firebase/</link>
                <guid isPermaLink="false">631fbc87926c2f06e57cea94</guid>
                
                    <category>
                        <![CDATA[ React Native ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ana Beatriz ]]>
                </dc:creator>
                <pubDate>Wed, 05 Oct 2022 09:34:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/09/react-native-firebase.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/react-native-firebase-tutorial/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Build a React Native App and Integrate It with Firebase</a>
      </p><p>Neste tutorial, vamos construir uma aplicação em React Native integrada ao Firebase no back-end. A aplicação também terá suporte da React Native CLI e da Expo CLI.</p><p>Este tutorial de <strong>React Native com Firebase</strong> abrangerá as funcionalidades principais, como autenticação, registro e operações CRUD no banco de dados (Firestore).</p><p>Você também pode <a href="https://github.com/instamobile/react-native-firebase">baixar o código completo</a> no GitHub se quiser conferi-lo.</p><p>Este tutorial percorrerá os detalhes por meio das seguintes seções:</p><ol><li><strong>Criação de um projeto com o Firebase</strong></li><li><strong>Criação e configuração de uma aplicação em React Native</strong></li><li><strong>Configuração da estrutura de pastas, rotas e navegação</strong></li><li><strong>Implementação da interface para telas de login, registro e início</strong></li><li><strong>Registro com Firebase Auth</strong></li><li><strong>Login com Firebase Auth</strong></li><li><strong>Persistência de credenciais de login</strong></li><li><strong>Escrita e leitura de dados a partir do Firebase Firestore</strong></li></ol><p>Sem mais delongas, vamos começar a construir o projeto em React Native com Firebase. A aplicação final ficará assim:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/09/react-native-firebase-1.png" class="kg-image" alt="react-native-firebase-1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/09/react-native-firebase-1.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/09/react-native-firebase-1.png 800w" sizes="(min-width: 720px) 720px" width="800" height="600" loading="lazy"></figure><h2 id="1-cria-o-de-um-projeto-com-o-firebase">1. Criação de um projeto com o Firebase</h2><p>Visite <a href="https://firebase.google.com/">Firebase.com</a> e crie uma conta. Quando fizer login, você poderá criar um projeto no <a href="https://console.firebase.google.com/u/0/">Firebase Console</a>.</p><ul><li>Crie uma conta no <a href="https://firebase.google.com/">Firebase.com</a></li><li>Crie um projeto no <a href="https://console.firebase.google.com/">Firebase Console</a></li><li>Habilite o método de autenticação com e-mail e senha em <em>Firebase Console</em> &nbsp;-&gt; &nbsp;<em>Authentication</em> &nbsp;-&gt; &nbsp;<em>Sign-in method</em></li></ul><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/09/1__J2bqHTUxhs_sTxwRdbvAg.png" class="kg-image" alt="1__J2bqHTUxhs_sTxwRdbvAg" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/09/1__J2bqHTUxhs_sTxwRdbvAg.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/09/1__J2bqHTUxhs_sTxwRdbvAg.png 800w" sizes="(min-width: 720px) 720px" width="800" height="298" loading="lazy"></figure><ul><li>Crie uma aplicação para o iOS, com o ID de aplicação <em>com.reactnativefirebase</em></li></ul><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/09/1_RFyy5eHgUlZIEQtaCj5ddA.png" class="kg-image" alt="1_RFyy5eHgUlZIEQtaCj5ddA" width="551" height="520" loading="lazy"></figure><ul><li>(Opcional) Crie uma aplicação do Android com o nome de pacote <em>com.reactnativefirebase</em></li><li>Baixe no seu computador o arquivo de configuração gerado no próximo passo (<em>GoogleService-Info.plist </em>para iOS e <em>google-services.json </em>para Android)</li></ul><p>O Firebase permite construir aplicações <em>sem o back-end</em>. É um produto que executa por dentro da Google Cloud e permite que os desenvolvedores construam aplicações para a web e dispositivos móveis sem precisarem de seus próprios servidores.</p><p>Isso faz você economizar muito tempo, por não haver a necessidade de escrever código para o back-end. Também é muito escalável, sendo sustentado pela infraestrutura do Google.</p><p>No Firebase, você poderá armazenar tudo que é necessário para sua aplicação – usuários, dados, arquivos, tokens para notificações por push, etc. Toda essa informação fica disponível nos clients para dispositivos móveis a partir dos SDKs do Firebase, que são compatíveis com o React Native. Isso significa que todas as interações com o back-end ficam abstraídas e encapsuladas no SDK. Então, os desenvolvedores para dispositivos móveis não precisam se preocupar com chamadas para API, análise de dados, gerenciamento de sockets e outros aspectos.</p><h2 id="2-cria-o-e-configura-o-de-uma-aplica-o-em-react-native">2. Criação e configuração de uma aplicação em React Native</h2><p>Vamos deixar nossa aplicação do React Native com Firebase compatível com a Expo CLI e a React Native CLI.‌‌ Vamos usar o Expo por agora, pois facilita a visualização da aplicação. Não usaremos, no entanto, bibliotecas específicas do Expo. Assim, o código fonte será usado de maneira simples em qualquer aplicação do React Native, não importando no que ele está baseado. ‌‌Vamos usar o <a href="https://firebase.google.com/docs/reference/js">SDK Web do Firebase</a>, que é compatível com o Expo e com a React Native CLI, além de ter suporte direto do Google.</p><p>Se você quiser usar <a href="https://rnfirebase.io/">react-native-firebase</a> como alternativa, fique à vontade para instalar e configurar (o código permanecerá o mesmo). Saiba que não recomendamos isso por alguns motivos:</p><ul><li>ele não possui suporte direto do Google, o que torna a manutenção mais difícil, dado que é uma camada extra que pode causar bugs e</li><li>ele não funciona com o Expo, o que pode ser um problema para muitos desenvolvedores.</li></ul><p>Os passos a seguir também estão na documentação oficial do React Native, em <a href="https://reactnative.dev/docs/environment-setup">Como configurar seu ambiente de desenvolvimento</a> (em inglês).</p><ul><li>Instale a Expo CLI</li></ul><p>No seu terminal, execute</p><p><code>npm install -g expo-cli</code></p><ul><li>Crie uma aplicação do React Native executando</li></ul><p><code>expo init react-native-firebase</code></p><p>Para o template, escolha <em>Managed Workflow</em> — <em>Blank</em></p><ul><li>Inicie a aplicação executando</li></ul><pre><code class="language-bash">yarn ios
// ou
yarn android</code></pre><p>O comando também fornecerá a você um QR Code que pode ser escaneado usando a aplicação de Camera, no iOS, ou o Expo, no Android.</p><p>Ótimo. Agora temos uma nova aplicação do React Native, executando no iOS e no Android. Vamos começar conectando-a ao nosso back-end usando o Firebase.</p><ul><li>Adicione o SDK do Firebase no projeto React Native</li></ul><p><code>yarn add firebase</code></p><ul><li>Adicione a biblioteca React Native Navigation executando</li></ul><pre><code>yarn add @react-navigation/native &amp;&amp; yarn add @react-navigation/stack &amp;&amp; expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view</code></pre><ul><li>Adicione vários componentes à interface e pacotes que serão usados no projeto</li></ul><p><code>yarn add react-native-keyboard-aware-scroll-view base-64</code></p><p>Crie um arquivo de configuração para o Firebase</p><p><code>mkdir src src/firebase &amp;&amp; touch src/firebase/config.js</code></p><p>Adicione a configuração do seu firebase em <em>src/firebase/config.js:</em></p><pre><code class="language-javascript">import * as firebase from 'firebase';
import '@firebase/auth';
import '@firebase/firestore';

const firebaseConfig = {
  apiKey: 'YOUR_KEY_HERE_AIzaSyAOWH',
  authDomain: 'your-auth-domain-b1234.firebaseapp.com',
  databaseURL: 'https://your-database-name.firebaseio.com',
  projectId: 'your-project-id-1234',
  storageBucket: 'your-project-id-1234.appspot.com',
  messagingSenderId: '12345-insert-yourse',
  appId: 'insert yours: 1:1234:web:ee873bd1234c0deb7eba61ce',
};

if (!firebase.apps.length) {
    firebase.initializeApp(firebaseConfig);
}

export { firebase };</code></pre><p>Você consegue toda a informação a partir do <em><a href="https://console.firebase.google.com/">Firebase Console</a> &nbsp;-&gt; Project Settings</em></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/09/1_RU6D6YeIhpIprROu8lmOOw.png" class="kg-image" alt="1_RU6D6YeIhpIprROu8lmOOw" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/09/1_RU6D6YeIhpIprROu8lmOOw.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/09/1_RU6D6YeIhpIprROu8lmOOw.png 800w" sizes="(min-width: 720px) 720px" width="800" height="276" loading="lazy"></figure><h2 id="3-configura-o-da-estrutura-de-pastas-rotas-e-navega-o">3. Configuração da estrutura de pastas, rotas e navegação</h2><ul><li>Crie a estrutura da pasta executando</li></ul><pre><code>mkdir src/screens src/screens/LoginScreen src/screens/RegistrationScreen src/screens/HomeScreen</code></pre><ul><li>Crie a estrutura de arquivos executando</li></ul><pre><code>touch src/screens/index.js src/screens/LoginScreen/LoginScreen.js src/screens/LoginScreen/styles.js src/screens/RegistrationScreen/RegistrationScreen.js src/screens/styles.js src/screens/HomeScreen/HomeScreen.js src/screens/HomeScreen/styles.js</code></pre><ul><li>Adicione este código a <em><em>src/screens/index.js</em></em></li></ul><pre><code class="language-javascript">export { default as LoginScreen } from './LoginScreen/LoginScreen'

export { default as HomeScreen } from './HomeScreen/HomeScreen'

export { default as RegistrationScreen } from './RegistrationScreen/RegistrationScreen'
</code></pre><p>Não se preocupe se o projeto apresentar erros! Tudo fará sentido daqui a pouco.</p><ul><li>Configure as rotas e navegações</li></ul><p>Sobrescreva o arquivo <em>App.js</em> com o seguinte código:</p><pre><code class="language-javascript">import 'react-native-gesture-handler';
import React, { useEffect, useState } from 'react'
import { NavigationContainer } from '@react-navigation/native'
import { createStackNavigator } from '@react-navigation/stack'
import { LoginScreen, HomeScreen, RegistrationScreen } from './src/screens'
import {decode, encode} from 'base-64'
if (!global.btoa) {  global.btoa = encode }
if (!global.atob) { global.atob = decode }

const Stack = createStackNavigator();

export default function App() {

  const [loading, setLoading] = useState(true)
  const [user, setUser] = useState(null)

  return (
    &lt;NavigationContainer&gt;
      &lt;Stack.Navigator&gt;
        { user ? (
          &lt;Stack.Screen name="Home"&gt;
            {props =&gt; &lt;HomeScreen {...props} extraData={user} /&gt;}
          &lt;/Stack.Screen&gt;
        ) : (
          &lt;&gt;
            &lt;Stack.Screen name="Login" component={LoginScreen} /&gt;
            &lt;Stack.Screen name="Registration" component={RegistrationScreen} /&gt;
          &lt;/&gt;
        )}
      &lt;/Stack.Navigator&gt;
    &lt;/NavigationContainer&gt;
  );
}</code></pre><h2 id="4-implementa-o-da-interface">4. Implementação da interface</h2><p>Agora que temos a base da aplicação, vamos continuar e implementar os componentes para a interface de todas as telas. Não falaremos sobre layout flexível, nem sobre estilização do React Native, visto que esses pontos estão fora do escopo deste tutorial. Vamos focar na integração entre React Native e Firebase.</p><p>Sobrescreva os arquivos a seguir:</p><ul><li>src/LoginScreen/LoginScreen.js</li></ul><pre><code class="language-javascript">import React, { useState } from 'react'
import { Image, Text, TextInput, TouchableOpacity, View } from 'react-native'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import styles from './styles';

export default function LoginScreen({navigation}) {
    const [email, setEmail] = useState('')
    const [password, setPassword] = useState('')

    const onFooterLinkPress = () =&gt; {
        navigation.navigate('Registration')
    }

    const onLoginPress = () =&gt; {
    }

    return (
        &lt;View style={styles.container}&gt;
            &lt;KeyboardAwareScrollView
                style={{ flex: 1, width: '100%' }}
                keyboardShouldPersistTaps="always"&gt;
                &lt;Image
                    style={styles.logo}
                    source={require('../../../assets/icon.png')}
                /&gt;
                &lt;TextInput
                    style={styles.input}
                    placeholder='E-mail'
                    placeholderTextColor="#aaaaaa"
                    onChangeText={(text) =&gt; setEmail(text)}
                    value={email}
                    underlineColorAndroid="transparent"
                    autoCapitalize="none"
                /&gt;
                &lt;TextInput
                    style={styles.input}
                    placeholderTextColor="#aaaaaa"
                    secureTextEntry
                    placeholder='Password'
                    onChangeText={(text) =&gt; setPassword(text)}
                    value={password}
                    underlineColorAndroid="transparent"
                    autoCapitalize="none"
                /&gt;
                &lt;TouchableOpacity
                    style={styles.button}
                    onPress={() =&gt; onLoginPress()}&gt;
                    &lt;Text style={styles.buttonTitle}&gt;Log in&lt;/Text&gt;
                &lt;/TouchableOpacity&gt;
                &lt;View style={styles.footerView}&gt;
                    &lt;Text style={styles.footerText}&gt;Don't have an account? &lt;Text onPress={onFooterLinkPress} style={styles.footerLink}&gt;Sign up&lt;/Text&gt;&lt;/Text&gt;
                &lt;/View&gt;
            &lt;/KeyboardAwareScrollView&gt;
        &lt;/View&gt;
    )
}</code></pre><ul><li>src/LoginScreen/styles.js</li></ul><pre><code class="language-javascript">import { StyleSheet } from 'react-native';

export default StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center'
    },
    title: {

    },
    logo: {
        flex: 1,
        height: 120,
        width: 90,
        alignSelf: "center",
        margin: 30
    },
    input: {
        height: 48,
        borderRadius: 5,
        overflow: 'hidden',
        backgroundColor: 'white',
        marginTop: 10,
        marginBottom: 10,
        marginLeft: 30,
        marginRight: 30,
        paddingLeft: 16
    },
    button: {
        backgroundColor: '#788eec',
        marginLeft: 30,
        marginRight: 30,
        marginTop: 20,
        height: 48,
        borderRadius: 5,
        alignItems: "center",
        justifyContent: 'center'
    },
    buttonTitle: {
        color: 'white',
        fontSize: 16,
        fontWeight: "bold"
    },
    footerView: {
        flex: 1,
        alignItems: "center",
        marginTop: 20
    },
    footerText: {
        fontSize: 16,
        color: '#2e2e2d'
    },
    footerLink: {
        color: "#788eec",
        fontWeight: "bold",
        fontSize: 16
    }
})</code></pre><ul><li>src/RegistrationScreen/RegistrationScreen.js</li></ul><pre><code class="language-javascript">import React, { useState } from 'react'
import { Image, Text, TextInput, TouchableOpacity, View } from 'react-native'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import styles from './styles';

export default function RegistrationScreen({navigation}) {
    const [fullName, setFullName] = useState('')
    const [email, setEmail] = useState('')
    const [password, setPassword] = useState('')
    const [confirmPassword, setConfirmPassword] = useState('')

    const onFooterLinkPress = () =&gt; {
        navigation.navigate('Login')
    }

    const onRegisterPress = () =&gt; {
    }

    return (
        &lt;View style={styles.container}&gt;
            &lt;KeyboardAwareScrollView
                style={{ flex: 1, width: '100%' }}
                keyboardShouldPersistTaps="always"&gt;
                &lt;Image
                    style={styles.logo}
                    source={require('../../../assets/icon.png')}
                /&gt;
                &lt;TextInput
                    style={styles.input}
                    placeholder='Full Name'
                    placeholderTextColor="#aaaaaa"
                    onChangeText={(text) =&gt; setFullName(text)}
                    value={fullName}
                    underlineColorAndroid="transparent"
                    autoCapitalize="none"
                /&gt;
                &lt;TextInput
                    style={styles.input}
                    placeholder='E-mail'
                    placeholderTextColor="#aaaaaa"
                    onChangeText={(text) =&gt; setEmail(text)}
                    value={email}
                    underlineColorAndroid="transparent"
                    autoCapitalize="none"
                /&gt;
                &lt;TextInput
                    style={styles.input}
                    placeholderTextColor="#aaaaaa"
                    secureTextEntry
                    placeholder='Password'
                    onChangeText={(text) =&gt; setPassword(text)}
                    value={password}
                    underlineColorAndroid="transparent"
                    autoCapitalize="none"
                /&gt;
                &lt;TextInput
                    style={styles.input}
                    placeholderTextColor="#aaaaaa"
                    secureTextEntry
                    placeholder='Confirm Password'
                    onChangeText={(text) =&gt; setConfirmPassword(text)}
                    value={confirmPassword}
                    underlineColorAndroid="transparent"
                    autoCapitalize="none"
                /&gt;
                &lt;TouchableOpacity
                    style={styles.button}
                    onPress={() =&gt; onRegisterPress()}&gt;
                    &lt;Text style={styles.buttonTitle}&gt;Create account&lt;/Text&gt;
                &lt;/TouchableOpacity&gt;
                &lt;View style={styles.footerView}&gt;
                    &lt;Text style={styles.footerText}&gt;Already got an account? &lt;Text onPress={onFooterLinkPress} style={styles.footerLink}&gt;Log in&lt;/Text&gt;&lt;/Text&gt;
                &lt;/View&gt;
            &lt;/KeyboardAwareScrollView&gt;
        &lt;/View&gt;
    )
}</code></pre><ul><li>src/RegistrationScreen/styles.js</li></ul><pre><code class="language-javascript">import { StyleSheet } from 'react-native';

export default StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center'
    },
    title: {

    },
    logo: {
        flex: 1,
        height: 120,
        width: 90,
        alignSelf: "center",
        margin: 30
    },
    input: {
        height: 48,
        borderRadius: 5,
        overflow: 'hidden',
        backgroundColor: 'white',
        marginTop: 10,
        marginBottom: 10,
        marginLeft: 30,
        marginRight: 30,
        paddingLeft: 16
    },
    button: {
        backgroundColor: '#788eec',
        marginLeft: 30,
        marginRight: 30,
        marginTop: 20,
        height: 48,
        borderRadius: 5,
        alignItems: "center",
        justifyContent: 'center'
    },
    buttonTitle: {
        color: 'white',
        fontSize: 16,
        fontWeight: "bold"
    },
    footerView: {
        flex: 1,
        alignItems: "center",
        marginTop: 20
    },
    footerText: {
        fontSize: 16,
        color: '#2e2e2d'
    },
    footerLink: {
        color: "#788eec",
        fontWeight: "bold",
        fontSize: 16
    }
})</code></pre><ul><li>src/HomeScreen/HomeScreen.js</li></ul><pre><code class="language-javascript">import React from 'react'
import { Text, View } from 'react-native'

export default function HomeScreen(props) {
    return (
        &lt;View&gt;
            &lt;Text&gt;Home Screen&lt;/Text&gt;
        &lt;/View&gt;
    )
}</code></pre><ul><li>src/HomeScreen/styles.js</li></ul><pre><code class="language-javascript">import { StyleSheet } from 'react-native';

export default StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center'
    },
    input: {
        height: 48,
        borderRadius: 5,
        overflow: 'hidden',
        backgroundColor: 'white',
        marginTop: 10,
        marginBottom: 10,
        marginLeft: 30,
        marginRight: 30,
        paddingLeft: 16
    }
})</code></pre><p>Até aqui, sua aplicação deve executar corretamente e mostrar as seguintes telas (interface apenas):</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/09/1_rwQyQ3ZCE7rgHukTAeLliw.png" class="kg-image" alt="1_rwQyQ3ZCE7rgHukTAeLliw" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/09/1_rwQyQ3ZCE7rgHukTAeLliw.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/09/1_rwQyQ3ZCE7rgHukTAeLliw.png 800w" sizes="(min-width: 720px) 720px" width="800" height="600" loading="lazy"></figure><p>Você pode mudar entre as duas telas pressionando os links no rodapé.</p><p>Agora que temos uma interface bonita de login e registro, vamos ver como podemos integrar nossa aplicação do React Native (e Expo) com o Firebase.</p><h2 id="5-react-native-com-firebase-registro">5. React Native com Firebase  —  registro</h2><p>Vamos começar criando uma conta com o Firebase Auth, pois o login acontece depois. Para isso, vamos adicionar a lógica do Firebase para criar uma conta com e-mail e senha em <em>RegistrationScreen.js</em>, implementando o método <em>onRegisterPress,</em> assim:</p><pre><code class="language-javascript">//...

import { firebase } from '../../firebase/config'

//...

export default function RegistrationScreen({navigation}) {
    //...

    const onRegisterPress = () =&gt; {
        if (password !== confirmPassword) {
            alert("Passwords don't match.")
            return
        }
        firebase
            .auth()
            .createUserWithEmailAndPassword(email, password)
            .then((response) =&gt; {
                const uid = response.user.uid
                const data = {
                    id: uid,
                    email,
                    fullName,
                };
                const usersRef = firebase.firestore().collection('users')
                usersRef
                    .doc(uid)
                    .set(data)
                    .then(() =&gt; {
                        navigation.navigate('Home', {user: data})
                    })
                    .catch((error) =&gt; {
                        alert(error)
                    });
            })
            .catch((error) =&gt; {
                alert(error)
        });
    }
    
    //...
}</code></pre><p>No fluxo de criação de senha acima, fizemos algumas coisas importantes:</p><ul><li>Chamamos a função <code>createUserWithEmailAndPassword</code>, da API do Firebase Auth (linha 13), que cria uma conta que será mostrada na tabela em <em>Firebase Console -&gt; Authentication</em>.</li><li>Se o registro da conta foi feito com sucesso, também armazenamos os dados do usuário no Firebase Firestore (linha 24). Isso é necessário para armazenar informações adicionais do usuário, como nome completo, URL da foto de perfil e assim por diante, que não podem ser armazenadas na tabela de autenticação.</li><li>Se o registro foi feito com sucesso, vamos navegar para a tela de início, também passando os dados do usuário como objeto.</li><li>Se qualquer erro ocorrer, simplesmente mostramos um alerta mostrando o erro. Erros podem acontecer por causa de conexão de rede, senha muito curta, e-mail inválido e assim por diante.</li></ul><p>Recarregue sua aplicação e teste o registro. Se você criou uma conta com sucesso, veja se ela aparece em <em>Firebase Console</em> &nbsp;-&gt; &nbsp;<em>Authentication</em>:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2020/05/1_qy_k5wsgw4MAALmIeBxYpg.png" class="kg-image" alt="1_qy_k5wsgw4MAALmIeBxYpg" width="600" height="400" loading="lazy"></figure><h2 id="6-react-native-com-firebase-login">6. React Native com Firebase  —  login</h2><p>Agora que conseguimos criar as contas, vamos implementar a funcionalidade de login. O SDK do Firebase cuida de todos os passos necessários para que o login tenha autorização e autenticação seguras.</p><p>Abra <em>LoginScreen.js</em>, importe o firebase e complete o método <em><code>onLoginPress</code></em>:</p><pre><code class="language-javascript">//...

import { firebase } from '../../firebase/config'

//...

export default function LoginScreen({navigation}) {

    //...

    const onLoginPress = () =&gt; {
        firebase
            .auth()
            .signInWithEmailAndPassword(email, password)
            .then((response) =&gt; {
                const uid = response.user.uid
                const usersRef = firebase.firestore().collection('users')
                usersRef
                    .doc(uid)
                    .get()
                    .then(firestoreDocument =&gt; {
                        if (!firestoreDocument.exists) {
                            alert("User does not exist anymore.")
                            return;
                        }
                        const user = firestoreDocument.data()
                        navigation.navigate('Home', {user})
                    })
                    .catch(error =&gt; {
                        alert(error)
                    });
            })
            .catch(error =&gt; {
                alert(error)
            })
    }

    //...

}</code></pre><p>Recarregue sua aplicação e faça login com uma conta existente. A aplicação deverá enviar você para a tela de início se as credenciais estiverem corretas, ou mostrará um alerta com um erro, se algo der errado.</p><h2 id="7-persist-ncia-de-credenciais-de-login">7. Persistência de credenciais de login</h2><p>Você verá que, se sair da aplicação e abri-la novamente, ela mostrará a tela de login de novo. Para uma boa experiência do usuário, queremos redirecionar todos os usuários para a tela de início. Ninguém quer digitar suas credenciais de login toda vez que quiser usar a aplicação.</p><p>Isso também é conhecido como persistência de login. Felizmente, o SDK do Firebase cuida disso para nós, lidando com todas as questões de segurança. A persistência de login está habilitada por padrão no Firebase. Então, tudo que precisamos fazer é buscar pelo usuário atual que fez login.</p><p>Abra o arquivo <em>App.js</em> e vamos implementar a funcionalidade de persistência de login:</p><pre><code class="language-javascript">//...

import { firebase } from './src/firebase/config'

//...

export default function App() {

  //...

  if (loading) {	
    return (	
      &lt;&gt;&lt;/&gt;	
    )	
  }

  useEffect(() =&gt; {
    const usersRef = firebase.firestore().collection('users');
    firebase.auth().onAuthStateChanged(user =&gt; {
      if (user) {
        usersRef
          .doc(user.uid)
          .get()
          .then((document) =&gt; {
            const userData = document.data()
            setLoading(false)
            setUser(userData)
          })
          .catch((error) =&gt; {
            setLoading(false)
          });
      } else {
        setLoading(false)
      }
    });
  }, []);

  //...

}</code></pre><p><em>onAuthStateChanged</em> retorna o usuário atual que fez login. Então, procuramos toda a informação adicional que armazenamos no Firebase e a indicamos no estado atual do componente. Isso renderizará a aplicação novamente, o que mostrará a tela de início.</p><p>Note que, como usamos a função pela primeira vez, a aplicação carregou utilizando o hook <a href="https://reactjs.org/docs/hooks-effect.html">useEffect</a>.</p><h2 id="8-escrita-e-leitura-de-dados-a-partir-do-firebase-firestore">8. Escrita e leitura de dados a partir do Firebase Firestore</h2><p>Já usamos o Firebase acima, para salvar informações adicionais sobre nossos usuários (o nome completo). Nesta seção, vamos ver como podemos escrever dados no Firebase e como podemos buscá-los.</p><p>Vamos compreender como observar (ouvir) mudanças na coleção do Firebase e refleti-las na tela, em tempo real. Isso pode ser muito útil em aplicações de tempo real, como o <a href="https://www.instamobile.io/app-templates/video-chat-app-in-react-native/">React Native Chat</a>.</p><p>Para simplificar, vamos salvar alguns itens textuais na coleção do Firestore chamada "entities" (entidades). Pense nesses itens como se fossem tarefas, postagens, tweets, qualquer coisa. Vamos criar um arquivo simples, que adiciona uma nova entidade, e vamos também listar todas as entidades que pertencem ao usuário atual que fez login. Além disso, a lista será atualizada em tempo real.</p><ul><li>Implemente <em>HomeScreen.js</em> reescrevendo-o com o código abaixo</li></ul><pre><code class="language-javascript">import React, { useEffect, useState } from 'react'
import { FlatList, Keyboard, Text, TextInput, TouchableOpacity, View } from 'react-native'
import styles from './styles';
import { firebase } from '../../firebase/config'

export default function HomeScreen(props) {

    const [entityText, setEntityText] = useState('')
    const [entities, setEntities] = useState([])

    const entityRef = firebase.firestore().collection('entities')
    const userID = props.extraData.id

    useEffect(() =&gt; {
        entityRef
            .where("authorID", "==", userID)
            .orderBy('createdAt', 'desc')
            .onSnapshot(
                querySnapshot =&gt; {
                    const newEntities = []
                    querySnapshot.forEach(doc =&gt; {
                        const entity = doc.data()
                        entity.id = doc.id
                        newEntities.push(entity)
                    });
                    setEntities(newEntities)
                },
                error =&gt; {
                    console.log(error)
                }
            )
    }, [])

    const onAddButtonPress = () =&gt; {
        if (entityText &amp;&amp; entityText.length &gt; 0) {
            const timestamp = firebase.firestore.FieldValue.serverTimestamp();
            const data = {
                text: entityText,
                authorID: userID,
                createdAt: timestamp,
            };
            entityRef
                .add(data)
                .then(_doc =&gt; {
                    setEntityText('')
                    Keyboard.dismiss()
                })
                .catch((error) =&gt; {
                    alert(error)
                });
        }
    }

    const renderEntity = ({item, index}) =&gt; {
        return (
            &lt;View style={styles.entityContainer}&gt;
                &lt;Text style={styles.entityText}&gt;
                    {index}. {item.text}
                &lt;/Text&gt;
            &lt;/View&gt;
        )
    }

    return (
        &lt;View style={styles.container}&gt;
            &lt;View style={styles.formContainer}&gt;
                &lt;TextInput
                    style={styles.input}
                    placeholder='Add new entity'
                    placeholderTextColor="#aaaaaa"
                    onChangeText={(text) =&gt; setEntityText(text)}
                    value={entityText}
                    underlineColorAndroid="transparent"
                    autoCapitalize="none"
                /&gt;
                &lt;TouchableOpacity style={styles.button} onPress={onAddButtonPress}&gt;
                    &lt;Text style={styles.buttonText}&gt;Add&lt;/Text&gt;
                &lt;/TouchableOpacity&gt;
            &lt;/View&gt;
            { entities &amp;&amp; (
                &lt;View style={styles.listContainer}&gt;
                    &lt;FlatList
                        data={entities}
                        renderItem={renderEntity}
                        keyExtractor={(item) =&gt; item.id}
                        removeClippedSubviews={true}
                    /&gt;
                &lt;/View&gt;
            )}
        &lt;/View&gt;
    )
}</code></pre><ul><li>Estilize a tela inicial, sobrescrevendo <em>HomeScreen/styles.js </em>com:</li></ul><pre><code class="language-javascript">import { StyleSheet } from 'react-native';

export default StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center'
    },
    formContainer: {
        flexDirection: 'row',
        height: 80,
        marginTop: 40,
        marginBottom: 20,
        flex: 1,
        paddingTop: 10,
        paddingBottom: 10,
        paddingLeft: 30,
        paddingRight: 30,
        justifyContent: 'center',
        alignItems: 'center'
    },
    input: {
        height: 48,
        borderRadius: 5,
        overflow: 'hidden',
        backgroundColor: 'white',
        paddingLeft: 16,
        flex: 1,
        marginRight: 5
    },
    button: {
        height: 47,
        borderRadius: 5,
        backgroundColor: '#788eec',
        width: 80,
        alignItems: "center",
        justifyContent: 'center'
    },
    buttonText: {
        color: 'white',
        fontSize: 16
    },
    listContainer: {
        marginTop: 20,
        padding: 20,
    },
    entityContainer: {
        marginTop: 16,
        borderBottomColor: '#cccccc',
        borderBottomWidth: 1,
        paddingBottom: 16
    },
    entityText: {
        fontSize: 20,
        color: '#333333'
    }
})</code></pre><ul><li>Recarregue a aplicação e observe a nova tela inicial. Digite algo e pressione o botão <em>Add.</em></li><li>Nada acontece.</li><li>Crie um índice nas entidades da coleção do Firestore</li></ul><p>Você verá que a lista de entidades não foi renderizada. Se conferirmos os registros, veremos um aviso sobre "a busca precisar de um índice", seguido de um grande URL:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/09/1_bfOrtReOOo9B_pDR4_Zm9w.png" class="kg-image" alt="1_bfOrtReOOo9B_pDR4_Zm9w" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/09/1_bfOrtReOOo9B_pDR4_Zm9w.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/09/1_bfOrtReOOo9B_pDR4_Zm9w.png 800w" sizes="(min-width: 720px) 720px" width="800" height="119" loading="lazy"></figure><p>Isso nos informa que não podemos buscar tabelas de entidades pelo <em>authorID</em> e classificar os dados por <em>createdAt</em> em ordem decrescente, a menos que criemos um índice. Criar um índice é bem fácil, na verdade—simplesmente clique no URL e, então, clique no botão:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/09/1_72kARyPWnDypbCko7e4U1Q.png" class="kg-image" alt="1_72kARyPWnDypbCko7e4U1Q" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/09/1_72kARyPWnDypbCko7e4U1Q.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/09/1_72kARyPWnDypbCko7e4U1Q.png 800w" sizes="(min-width: 720px) 720px" width="800" height="318" loading="lazy"></figure><ul><li>Recarregue a aplicação novamente</li></ul><p>Agora, tudo funciona como o esperado:</p><ul><li>A aplicação lista todas as entidades na coleção entities, em ordem decrescente de acordo com a criação</li><li>Adicionar uma nova entidade funciona</li><li>A lista atualiza em tempo real (tente apagar um registro diretamente do banco de dados ou adicionar um novo diretamente da aplicação)</li></ul><p>Seu banco de dados do Firestore, agora, fica assim:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/09/1_zPT7lLNr6kvtdazgN50eKg.png" class="kg-image" alt="1_zPT7lLNr6kvtdazgN50eKg" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/09/1_zPT7lLNr6kvtdazgN50eKg.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/09/1_zPT7lLNr6kvtdazgN50eKg.png 800w" sizes="(min-width: 720px) 720px" width="800" height="214" loading="lazy"></figure><p>É assim que você lê e escreve no Firestore em React Native. Vamos para a última seção.</p><p>Brinque com a aplicação, adicionando novas entidades. Este é o projeto final:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/09/react-native-firebase-2.png" class="kg-image" alt="react-native-firebase-2" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/09/react-native-firebase-2.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/09/react-native-firebase-2.png 800w" sizes="(min-width: 720px) 720px" width="800" height="600" loading="lazy"></figure><h2 id="conclus-o"><strong>Conclusão</strong></h2><p>O Firebase facilita o suporte para autenticação e banco de dados para qualquer aplicação em React Native. O SDK do Firebase é extremamente poderoso, suporta vários padrões de leitura e escrita em bancos de dados.</p><p>Além do React Native, o SDK do Firebase providencia suporte para várias outras linguagens, como <a href="https://www.iosapptemplates.com/blog/swift-programming/firebase-swift-tutorial-login-registration-ios">Swift</a>, &nbsp;<a href="https://www.instakotlin.com/templates/android-starter-kit-with-firebase/">Kotlin</a> ou <a href="https://www.instaflutter.com/app-templates/flutter-login-screen/">Flutter</a>. Confira esses links para ver instruções para os iniciantes no Firebase em várias linguagens.</p><p>Mostramos o básico neste tutorial de React Native com Firebase. Nos próximos, vamos abranger mais funcionalidades avançadas, como Firebase Storage (carregamento de arquivos) e notificações por push.</p><p>Se você gostou deste tutorial, dê uma estrela no <a href="https://github.com/instamobile/react-native-firebase">repositório do GitHub</a> e compartilhe este artigo com sua comunidade. Você pode conferir muitos <a href="https://www.instamobile.io/mobile-templates/react-native-templates-free/">projetos gratuitos em React Native</a> no Instamobile. Abraços!</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
