Baixe o app para aproveitar ainda mais
Prévia do material em texto
Estas anotações são sobre o bootcamp Desenvolvedor Front-End da instituição IGTI iniciado em 20/11/2020. https://online.igti.com.br/courses Sumário MÓDULO 01 4 Aula 01 - Introdução 5 Aula 02 - Node.js 6 Aula 03 – Noções HTML 8 Aula 04 – Noções CSS 9 Aula 05 - Introdução ao JavaScript 10 Aula 06 - JavaScript - comandos de bloco 11 Aula 07 - JavaScript - manipulação do DOM 15 Aula 08 - JavaScript - formulários e manipulação de eventos 18 Aula 09 - CRUD com HTML, CSS e JavaScript 23 Aula 10 - JavaScript moderno – Introdução 36 Aula 11 - JavaScript moderno - Manipulação de arrays 39 Aula 12 - JavaScript moderno - Rest/Spread operator e destructuring 44 Aula 13 - Refatoração do projeto de CRUD 46 Aula 14 - Introdução à programação assíncrona com JavaScript 47 Aula 15 - Funções setTimeout e setInterval 48 Aula 16 - Requisições HTTP com JavaScript 49 MÓDULO 02 52 Aula 01 – Apresentação do curso 53 Aula 02 – Conceitos e termos 54 Aula 03 - Instalação do Node.js - Visual Studio Code 55 Aula 04 - Primeira App React 56 Aula 05 - Conhecendo nossa App 57 Aula 06 - Calculadora 60 Aula 07 - Estilizando a nossa calculadora 66 Aula 08 - Dando funções à nossa Calculadora 74 Aula 09 - Axios 87 Aula 10 - Nossa APP 88 Aula 11 – Design da App 89 Aula 12 – Criando a App 90 Aula 13 - Criando a instância do Axios 91 Aula 14 - Aula teórica : Promisses 92 Aula 15 - Iniciando a nossa controller genérico 93 Aula 16 - Testando a nossa função de Schema 94 Aula 17 - Criando e testando a nossa função getByld 96 Aula 18 - Entendendo Generics 98 Aula 19 – GetAll 100 Aula 20 - Criando nosso GetByPartialName 105 Aula 21 – Aula teórica: Hooks 107 Aula 22 - Material UI 108 Aula 23 – TypeScript 109 Aula 24 - Apresentando o componente Detalhe 110 Aula 25 - Criando a estrutura genérica de dados 111 Aula 26 – Componente de detalhe 112 Aula 27 - Criando o hook useAsync 117 Aula 28 – Implementando o componente detalhe 119 Aula 29 - Componente lista 123 Aula 30 - Roteando nossa app 124 Aula 31 – Utilizando useHistory 125 Aula 32 – Terminando nossa App 126 Módulo 03 127 Aula 01 - Introdução e Preparação do Ambiente 128 Aula 02 - Arquitetura e Estrutura da Aplicação 129 Aula 03 - Componentes e Templates 130 Aula 04 - Desafio Guiado 1 135 Aula 05 – Diretivas Estruturais 141 Aula 06 - Comunicação Entre Componentes 147 Aula 07: Estilização de Componentes 156 MÓDULO 01 Aula 01 - Introdução Aula 02 - Node.js Para iniciar um projeto node.js é necessário criar um pacote do tipo .json, para isto, basta abrir o cmd, navegar até a pasta em que se deseja criar os pacotes e então digitar o seguinte: npm init -y E estará criado o projeto node.js em formato .json. Existem dois tipos de dependências: · Dependency: o tipo de dependência padrão local; · Dev-dependency: o tipo de dependência que é utilizado pelo desenvolvedor apenas para o desenvolvimento, ou seja, isso não será utilizado no deploy quando a aplicação for para o servidor de fato; Para criar uma dev-dependency para desenvolver com node.js, basta navegar pelo terminal até a pasta desejada e digitar: npm install live-server –-save-dev Então este comando irá inserir no package.json uma área responsável pelo controle de dependências de desenvolvimento (devDependencies); e também irá criar um arquivo, que não deve ser modificado, chamado package-lock.json que é responsável pelo controle de versões destas dependências. Isto é de extrema utilidade pois cria um arquivo .json que grava quais dependências o projeto tem e caso seja necessário trocar de máquina o projeto, movendo apenas estes arquivos e executando o comando ‘npm install’ o npm instalará todas as dependências necessárias. Para rodar o live-server, primeiro deve-se configurar que ele será responsável por rodar a aplicação e para isto, deve-se adicionar a key “start” no package.json com o valor “live-server”, ficando assim: { "name": "aula-02-node-js", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "live-server" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "live-server": "^1.2.1" } } E então no terminal, na pasta em que o arquivo se encontra rodar: npm start Pronto, o navegador abrirá a aplicação. Aula 03 – Noções HTML Aula 04 – Noções CSS Aula 05 - Introdução ao JavaScript Aula 06 - JavaScript - comandos de bloco Sintaxes: // Comparando 2 números com if/else var a = 3; var b = 5; if (a < b) { console.log(a + ' é menor que ' + b); } else { if (a > b) { console.log(a + ' é maior que ' + b); } else { console.log(a + ' e ' + b + ' são iguais'); } } // Validação de dia com if/else var day = 1; if (day === 1) { console.log('Domingo'); } else { if (day === 2) { console.log('Segunda-feira'); } else { if (day === 3) { console.log('Terça-feira'); } else { if (day === 4) { console.log('Quarta-feira'); } else { if (day === 5) { console.log('Quinta-feira'); } else { if (day === 6) { console.log('Sexta-feira'); } else { if (day === 7) { console.log('Sábado'); } else { console.log('Dia inválido'); } } } } } } } // Validação de dia com switch switch (day) { case 1: console.log('Domingo'); break; case 2: console.log('Segunda-feira'); break; case 3: console.log('Terça-feira'); break; case 4: console.log('Quarta-feira'); break; case 5: console.log('Quinta-feira'); break; case 6: console.log('Sexta-feira'); break; case 7: console.log('Sábado'); break; default: console.log('Dia inválido'); } // Comparando 2 números com operador ternário var compareResult = a > b ? 1 : a < b ? -1 : 0; // Verificando dia da semana com operador ternário var weekDay = day === 1 ? 'Domingo' : day === 2 ? 'Segunda-feira' : day === 3 ? 'Terça-feira' : day === 4 ? 'Quarta-feira' : day === 5 ? 'Quinta-feira' : day === 6 ? 'Sexta-feira' : day === 7 ? 'Sábado' : 'Dia inválido'; Aula 07 - JavaScript - manipulação do DOM document.querySelector() Para manipular elementos da página HTML via JS utiliza-se o método do objeto document ‘querySelector’, ele é responsável por selecionar um ou vários elementos da página HTML, estes podendo ser de três tipos: var input1 = document.querySelector('#idGenerico'); input1.value = 'Raphael Gomide'; var input2 = document.querySelector('.classeGenerica'); input3.value = 'Abilonson'; var input3 = document.querySelector('elementoHTML'); //<p>, <h1>, etc... input3.value = 'Jurema'; document.querySelectorAll(); Semelhante ao ‘querySelector()’ porém retorna uma nodeList com todos os elementor de um tipo especificado no argumento do método; const important = document.querySelectorAll('urgent'); Uma maneira interessante de se manipular o conteúdo do texto de um elemento é após capturar o elemento desejado usando um querySelector, utiliza-se o método ‘.textContent();’ para alterar seu valor. const important = document.querySelectorAll('urgent'); important.textContent = 'Olá Mundo! sou importante!'; Aqui é interessante mostrar que é possível manipular o CSS através do JS. Isto é feito através das classes em CSS. Supões que se deseja alterar a formatação de um texto quando ele for clicado, para isto utilizamos a propriedade onclick no HTML que será responsável por executar esta função, e então criamos uma classe CSS com a propriedade desejada para então escrever uma função para adicionar essa classe à respectiva tag HTML. Ficando com: HTML: <h1 id="toBeEmphasized" onclick="clicked(event)" class="">Olá Mundo!</h1> CSS: .emphasis { font-weight: bold; color: darkgreen; } JS: function clicked() { const text = document.querySelector('#toBeEmphasized'); classList = Array.from(text.classList); //Se tiver a classe emphasis a função irá remover estaclasse, caso contrário irá adicionar: if (classList.includes('emphasis')){ text.classList.remove('emphasis'); text.textContent = 'Sem o ".empahsis" nas classes deste objeto'; } else { text.classList.add('emphasis'); text.textContent = '".emphasis" foi adicionado'; } } Este foi um exemplo de manipulação simples de CSS de um elemento HTML utilizando JS, demonstrando o método ‘remove()’ e ‘add()’ da classList do elemento HTML. Aula 08 - JavaScript - formulários e manipulação de eventos Para criar formulários em HTML utiliza-se a tag <form> e nele podem ser inseridos os campos que serão utilizados. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Formulário</title> </head> <body> <form> <p> <textarea rows="10" cols="10">Conteúdo</textarea> </p> <p> <input type="text" id="input1" placeholder="Informe o seu nome" /> </p> <p> <input type="email" id="input2" placeholder="Informe o seu email" /> </p> <p> <input type="date" id="input3" placeholder="Informe o seu email" /> </p> <p> <input type="checkbox" id="input4" placeholder="Informe o seu nome" /> Rock </p> <p> <input type="radio" name="opcao" id="input2" placeholder="Informe o seu nome" /> Sim <input type="radio" id="input2" name="opcao" placeholder="Informe o seu nome" /> Não </p> <input type="submit" value="Enviar" /> </form> <script src="./js/script.js"></script> </body> </html> Para manipular estes elementos com o JS basta fazer da mesma maneira como se manipularia qualquer outro elemento HTML, através do querySelector: var input1 = document.querySelector('#input1'); input1.value = Bleblio; Então o valor do imput1 será setado para ‘Bleblio’. A função adEventListener() também desempenha um papel muito importante, é através dela que ao se executar uma ação na página o script em JS é executado. function clicked() { const text = document.querySelector('#toBeEmphasized'); classList = Array.from(text.classList); //Se tiver a classe emphasis a função irá remover esta classe, caso contrário irá adicionar: if (classList.includes('emphasis')){ text.classList.remove('emphasis'); text.textContent = 'Sem o ".empahsis" nas classes deste objeto'; } else { text.classList.add('emphasis'); text.textContent = '".emphasis" foi adicionado'; } }; function start() { let textInput = document.querySelector('#textInput'); let form = document.querySelector('form'); textInput.addEventListener('keyup', countTextLength); form.addEventListener('submit', preventSubmit); } function countTextLength(event) { let count = event.target.value.length; let span = document.querySelector('#textLength'); span.textContent = count; } function preventSubmit(event) { event.preventDefault(); alert('ainda não estamos funcionando, volte mais tarde!') } start(); Aula 09 - CRUD com HTML, CSS e JavaScript Parte 01: Nesta aula se aprende a manipular a inserção de elementos HTML através do JS. Isto é útil pois as vezes se deseja ciar uma lista ou coisa similar que sejam anexados elementos quando alguma acao é tomada; no exemplo quando um nome é digitado e apertado ENTER este é anexado a uma lista HTML. A primeira coisa a se fazer é evitar que a página seja recarregada automaticamente ao dar o submit do form, para isto criamos uma função que é executada ao executarmos o submit do form que previne esta ação. function start(){ preventFormSubmit(); }; function preventFormSubmit(){ function handleFormSubmit(event){ event.preventDefault(); } const form = document.querySelector('form'); form.addEventListener('submit', handleFormSubmit); } start(); Nesta lógica, assim que o script for executado ele executará a função start() que será responsável por executar a preventFormSubmit(); Então com o adEventListener observando o form, sempre que for dado um ‘submit’ nele ele executará o handleFormSubmit, que recebe o ‘submit’ como argumento e roda o ‘submit’.preventDefault(); Parte 02: Agora devemos pegar os dados inseridos no formulário, para anexa-los a uma lista de nome. Para isso é interessante ter uma função responsável por observar o formulário e assim que a tecla enter for pressionada pegar estes dados e salvar em um array para depois apresentá-los na tela. Para ficar responsável por pegar estes dados criamos a função activateInput(), que observa e faz o push no array e o render() que renderiza esse array na tela //variaveis globais let inputName; let globalNames = ['test']; //funçoes gerais function preventFormSubmit() { //prevent refresh da página function handleFormSubmit(event) { event.preventDefault(); } const form = document.querySelector('form'); form.addEventListener('submit', handleFormSubmit); } function activateInput() { //observa a tecla enter e quando pressionada adiciona ao globalNames o que for digitado no formulário function handleTyping(event) { if (event.key === 'Enter') { globalNames.push(event.target.value); render(); } } inputName.focus(); inputName.addEventListener('keyup', handleTyping); } function render() { //renderiza as informácoes do aray globalNames em forma de lista, através de push e appendChild let divNames = document.querySelector('#names'); divNames.innerHTML = ''; let ul = document.createElement('ul'); for (let i = 0; i < globalNames.length; i++) { let currentName = globalNames[i]; let li = document.createElement('li'); li.textContent = currentName; ul.appendChild(li); } divNames.appendChild(ul); } //script a ser executado quando a página for carregada function start() { //apelidos inputName = document.querySelector('#inputName'); //açoes a serem executadas preventFormSubmit(); activateInput(); render(); } start(); O principal aqui é como utiliza-se o createElement() e appendChild para criar e anexar os elementos HTML. Parte 03: Inicia-se fazendo alguma otimizações como limpar a tela ao renderizar uma nova lista. Feito isto a deve-se criar a função de remoção de elemento a partir do botão, para isso utilizaremos o mesmo principio de createElement para a lista só que para o botão e para facilitar o entendimento do código, cria-se uma função para criar tal botão. Lembrando que após a remoção do item é necessário renderizar a lista novamente. //variaveis globais let inputName; let globalNames = ['test']; //funçoes gerais function preventFormSubmit() { //prevent refresh da página function handleFormSubmit(event) { event.preventDefault(); } const form = document.querySelector('form'); form.addEventListener('submit', handleFormSubmit); } function activateInput() { //observa a tecla enter e quando pressionada adiciona ao globalNames o que for digitado no formulário function handleTyping(event) { if (event.key === 'Enter') { globalNames.push(event.target.value); render(); } // if (event.key === 'ArrowUp') { // console.log(globalNames); // } } inputName.focus(); inputName.addEventListener('keyup', handleTyping); } function render() { function createDeleteButton(index) { function deleteName() { // console.log(globalNames[index]) globalNames.splice(index, 1); render(); } const button = document.createElement('button'); button.textContent = 'x'; button.classList.add('deleteButton'); button.addEventListener('click', deleteName); return button; } //renderiza as informacoes do aray globalNames em forma de lista, através de push e appendChild let divNames = document.querySelector('#names'); divNames.innerHTML = ''; let ul = document.createElement('ul'); for (let i = 0; i < globalNames.length; i++) {let currentName = globalNames[i]; let li = document.createElement('li'); let button = createDeleteButton(i); let span = document.createElement('span'); span.textContent = currentName; li.appendChild(button); li.appendChild(span); ul.appendChild(li); } divNames.appendChild(ul); clearInput(); } function clearInput() { inputName.value = ''; inputName.focus(); } //script a ser executado quando a página for carregada function start() { //apelidos inputName = document.querySelector('#inputName'); //açoes a serem executadas preventFormSubmit(); activateInput(); render(); } start(); Parte 04: Agora iremos mexer com a edição dos itens da lista. Para isto criamos uma função responsável por passar o valor do item para a barra de input e uma variável global para verificar se o item está sendo editado. //variaveis globais let inputName; let currentIndex = null; let globalNames = ['test','foo','spam']; let isEditing = false; //funçoes gerais function preventFormSubmit() { //prevent refresh da página function handleFormSubmit(event) { event.preventDefault(); } const form = document.querySelector('form'); form.addEventListener('submit', handleFormSubmit); } function activateInput() { //observa a tecla enter e quando pressionada adiciona ao globalNames o que for digitado no formulário function updateName(newName){ //responsável por fazer o update do item selecionado console.log(newName); console.log(currentIndex); globalNames[currentIndex] = newName; } function handleTyping(event) { //verifica se existe texto no input e deixa dar submit para a lista let hasText = !!event.target.value && event.target.value !== ''; if(!hasText){ clearInput(); return; } if (event.key === 'Enter') { if(isEditing) { updateName(event.target.value); } else { globalNames.push(event.target.value); } render(); isEditing = false; clearInput(); } } inputName.focus(); inputName.addEventListener('keyup', handleTyping); } function render() { //renderiza as informacoes do aray globalNames em forma de lista na tela, através de push e appendChild function createDeleteButton(index) { function deleteName() { // ppermite deletar o item da lista globalNames.splice(index, 1); render(); } const button = document.createElement('button'); button.textContent = 'x'; button.classList.add('deleteButton'); button.addEventListener('click', deleteName); return button; } function createSpan(name, index) { //cria o span onde o texto é armazenado function editItem() { //permite edicao do texto inputName.value = name; inputName.focus(); isEditing = true; currentIndex = index; } let span = document.createElement('span'); span.classList.add('clickable'); span.textContent = name; span.addEventListener('click', editItem); return span; } let divNames = document.querySelector('#names'); divNames.innerHTML = ''; let ul = document.createElement('ul'); for (let i = 0; i < globalNames.length; i++) { let currentName = globalNames[i]; let li = document.createElement('li'); let button = createDeleteButton(i); let span = createSpan(currentName, i) li.appendChild(button); li.appendChild(span); ul.appendChild(li); } divNames.appendChild(ul); clearInput(); } function clearInput() { //limpa o input box inputName.value = ''; inputName.focus(); } //script a ser executado quando a página for carregada function start() { //apelidos inputName = document.querySelector('#inputName'); //açoes a serem executadas preventFormSubmit(); activateInput(); render(); } start(); Aula 10 - JavaScript moderno – Introdução A partir do ECMAScript6 o JS introduz alguns conceitos novos: · Declaração de variáveis: var, const e let; · Arrow functions; · Template literals; · Default parameters; O código a seguir demonstra estas novas características: 'use strict'; //vas x let x const //var tem escopo abrangente; function withVar() { for (var i = 0; i < 10; i++) { console.log('var - ' + i); } i = 'sou o "i" criado dentro do loop for'; console.log('sou o consolog fora do loop for: ' + i + '. withVar()'); } //let tem escopo reduzido; function withLet() { for (let i = 0; i < 10; i++) { console.log('let: ' + i); } i = 'sou o "i" criado dentro do loop for'; console.log('sou o consolog fora do loop for: ' + i + '. withLet()'); } //const const c = 10; console.log(c); c = 5; //Uncaught TypeError: Assignment to constant variable. console.log(c); //execução withVar(); withLet(); //Uncaught ReferenceError: i is not defined E aqui o arrow function, default parameters e template literals: //arrow function const sumArrow1 = (a, b) => {return sum(a,b);}; //ou quando só tem um return const sumArrow2 = (a, b) => a + b; //template literals const nome = 'Leo'; const texto = `Meu nome é ${nome}`; console.log(texto); //Meu nome é Leo //default parameters const Aula 11 - JavaScript moderno - Manipulação de arrays Aqui são demonstrados os principal métodos de manipulação de arrays que o JS oferece: · map : (Gera um novo array, transformando dados); · filter : (Gera um novo array filtrando elementos conforme condição); · forEach (percorre todos os elementos do array aplicando uma lógica/função); · reduce : (realiza cálculo iterativo com base nos elementos); · find : (encontra o primeiro elemento de acordo com critério pré-definido); · some : (retorna booleano se há pelo menos um elemento que atenda à preposição); · every : (retorna booleano se todos os elementos atendem à preposição); · sort : (Ordena os elementos com base em um critério); //função que é executada ao iniciar a página function start() { console.log('.map(): ',doMap()); console.log('.filter(): ',doFilter()); console.log('.forEach(): ',doForEach()); console.log('.reduce(): ',doReduce()); console.log('.find(): ',doFind()); console.log('.some(): ',doSome()); console.log('.every(): ',doEvery()); console.log('.sort(): ',doSort()); } //execução da página start(); //Demonstração dos métodos de arrays mais importantes: function doMap() { const nameEmailArray = people.results.map((person) => { return { name: `${person.name.first} ${person.name.last}`, email: person.email, }; }); return nameEmailArray; } function doFilter() { const olderThan50 = people.results.filter((person) => { return person.dob.age >= 50; }); return olderThan50; } function doForEach() { const mappedPeople = doMap(); let res = []; const nameSize = mappedPeople.forEach((person) => { res.push({ name: person.name, nameLength: person.name.length }); return res; }); return res; } function doReduce() { const totalAges = people.results.reduce((accumulator, current) => { return accumulator + current.dob.age; }, 0); return totalAges; } function doFind() { const mo = people.results.find((person) => { return person.location.state == 'Minas Gerais'; }); return mo; } function doSome() { const mo = people.results.some((person) => { return person.location.state == 'Amazonas'; }); return mo; } function doEvery() { const every = people.results.every((person) => { return person.nat == 'BR'; }); return every; } function doSort() { const mappedPeople = people.results.map((person) => { return { name: person.name.first }; }); const filteredPeople = mappedPeople.filter((person) => person.name.startsWith('A') ); const sortedPeople = filteredPeople.sort((a, b) => { return a.name.length - b.name.length; }); return sortedPeople; } Aula 12 - JavaScript moderno - Rest/Spread operator e destructuring O operador ‘...’ é responsável pela manipulação de elementos dentro de um array. · O spread pega um array e ‘espalha’ ele dentro de um array ou algumoutro lugar; · O rest serve para pegar infinitos argumentos em um único array; · O destructuring é um jeito amis fácil de se pegar vária propriedades de uma variável em apenas uma linha; function start() { doSpread(); doRest(1,2,3,4); doDestructuring(); } start(); //---------- function doSpread() { const marriedMen = people.results.filter(person => person.name.title == 'Mr'); const marriedWomen = people.results.filter(person => person.name.title == 'Ms'); const marriedPeople = [...marriedMen, ...marriedWomen]; console.log(marriedPeople); }; function doRest(...numbers) { let sum = numbers.reduce((acc, curr) => acc + curr, 0); console.log(numbers); console.log(sum); }; function doDestructuring () { const first = people.results[0]; const { username, password } = first.login; console.log(username, password); //invés de const usernameR = people.results[0].login.username; const passwordR = people.results[0].login.password; console.log(usernameR, passwordR); }; Aula 13 - Refatoração do projeto de CRUD Aula 14 - Introdução à programação assíncrona com JavaScript teoria Aula 15 - Funções setTimeout e setInterval · O setTimeOut() funciona para postergar uma execução de alguma coisa, ele executa o código dentro dela após tanto tempo a ser definico; · O setTimeInterval serve para executar um código de tanto em tanto tempo; function start() { const timer = document.getElementById('timer'); const text = document.getElementById('text'); let count = 0; const interval = setInterval(() => { timer.textContent = count++; if (count == 4) { this.clearInterval(interval); //Para a contagem } }, 500); setTimeout(() => { text.textContent = 'setTimeOut() funcionou'; }, 2500); } start(); Aula 16 - Requisições HTTP com JavaScript O '.fetch()' é uma função que retorna uma promise, por isso deve-se traalhar de maneira assíncrona com ele. “assim que este '.fetch()' estiver resolvido faça tal coisa”. Sendo que o ‘.catch()’ é usado quando o '.fetch()' encontra algum problema com a requisição e o ‘.then()’ quando a requisição funciona. function start(){ doFetch(); divisionPromise(2,0).then((result) => { console.log(result); }).catch((err) => { console.log('este foi o erro: ' + err); }) asyncDivisionPromise(); asyncDoFetch(); }; start(); //---------- function doFetch() { fetch('https://api.github.com/users/leomac00').then((res) => { res .json() .then((data) => { showData(data); }) .catch((err) => { console.log(err); }); }); } function showData(data) { const user = document.getElementById('user'); user.textContent = data.login + ' : ' + data.name; } function divisionPromise(a, b) { return new Promise((resolve, reject) => { if (b == 0) { reject('nao dá para dividir por zero'); } resolve(a / b); }); } async function asyncDivisionPromise() { const division = await divisionPromise(2,2); console.log(division); } async function asyncDoFetch() { const res = await fetch('https://api.github.com/users/leomac00'); const json = await res.json(); console.log(json.name); } O ‘async’ e ‘await’ deve ser utilizado quando lidando com promises para poder facilitar o entendimento do código. MÓDULO 02 Aula 01 – Apresentação do curso Aula 02 – Conceitos e termos Aula 03 - Instalação do Node.js - Visual Studio Code Para poder utilizar o react é necessário instalar algumas dependências, sendo elas: · NVM que pode ser baixado por este link: https://github.com/nvm-sh/nvm ; · Node.js – utilizando o nvm, que irá gerenciar as versões do node no computador, através do comando no cmd nvm install <version>, neste caso utilizaremos a 14.15.1; Aula 04 - Primeira App React Primeiramente deve-se iniciar o projeto React, para isto basta entrar no diretório em que se deseja criar o projeto pelo cmd e digitar, no exemplo criaremos o projeto ‘hello_world’ através do npx, instalando-o utilizando comando: · npm install -g npx Após instalado criamos o projeto: · npx create-react-app hello_world Então entrando no diretório criado é possível utilizar um comando para iniciar a aplicação; · npm start Aqui vale mostrar alguns comandos possíveis de serem executados ao se criar um novo app React: · npm start: inicia o projeto em um servidor local; · npm build: ‘constrói’ a aplicação pra poder ser enviada para a produção, ou seja, altera nomes de variáveis, exclui dependências de desenvolvimento ou desnecessárias e etc; · npm eject: torna o gerenciamento de dependências manual; Aula 05 - Conhecendo nossa App Parte 01: A primeira coisa a se notar quando abrir a app no VSCode é o package.json, que é responsável por manipular os script que podem ser rodados no terminal e gerenciar as dependências do projeto. As dependências do projeto ficam instaladas na pasta node_modules dentro da pasta da aplicação. Caso as dependências se percam ou precisem ser instaladas por alguma razão basta ir até a pasta onde está a aplicação, ou seja, onde está o package.json e rodar o seguinte comando no terminal: · npm install o comando irá ler o arquivo package.json e instalar todo o conteúdo necessário para rodar a aplicação. Isto pode ser usado também caso haja algum problema com alguma(s) da(s) dependências. Parte 02: Outro ponto a se notar nos arquivos é a pasta public, ela contém todos os arquivos responsáveis pela página, por exemplo o index.html e index.js. Uma coisa legal de se notar aqui é que possível rodar o React em apenas uma parte da página. Para isso é necessário criar uma div onde se quer que seja executada, no caso do projeto pré-existente a div ‘root’ e então indicar que o React deve ser executado nela no index.js. <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <title>React App</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> </body> </html> E no js import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') ); reportWebVitals(); Alguns arquivos também podem ser apagados, como é o caso das imagens que o create-react-app cria automaticamente e afins, por exemplo, só que qualquer modificação feita nestes arquivos, exclusão ou inclusão, deve ser editada no arquivo ./public/manifest.json. E praticamente a todo o conteúdo da pasta ./src pode ser apagada. O componente React reponsável por renderizar as informações na tela é o ReactDOM, que recebe dois parâmetros: 1. O que irá renderizar; 2. Onde irá renderizar; E para que ele funcione ele deve ser importado através do import. Aula 06 - Calculadora Parte 01: A intenção desta aula é aprender o básico de React e o básico de CSS. Aqui se faz importante freezar que se pode utilizar o React apenas em um pedacinho da página através do ReacDOM.render(); Trabalhando com React é uma boa prática criar uma pasta ./src/componentes para armazenar todos os componentes que não são containers, isto é, de forma que esses componente que fazem parte da página possam ficar melhor organizados, por exemplo um arquivo com as funcionalidades da calculadora. De maneira análoga se cria uma pasta ./src/containers, para armazenar os container, no caso, cria-se um arquivo chamado calculator.js para armazenar a calculadora. Outra boa prática ser observada é a de planejar antes de fazer, mesmo parecendo óbvio, costuma-se primeiro criar os arquivos e pastas necessários antes de coda-los. No exemplo, é possível observar que a calculadora possui três componentes principais:Os botões, o display e a calculadora em si. Portanto, organizam-se os elementos da seguinte maneira: Agora dando início ao código: Código do arquivo display.js: //Importa do React a classe Components import React, { Component } from 'react'; //Cria uma classe Display que é uma extensão de Components class Display extends Component { //render() é a função no React.js responsável por renderizar alguma informação para o usuário render() { //no caso irá renderizar uma div com tais classes e tais valores return ( <div className={"display borderBlack"}> {this.props.value} </div> ) } } //usando 'export default' não será necessário usar '{}' na hora de importar export default Display; Perceba que o método render() retorna um HTML que é escrito em JSX (HTML + JSX). Parte 02: “A ideia de separar em vários componentes é dividir responsabilidades.” Código do arquivo button.js: import React, { Component } from 'react'; class Button extends Component { handleClick() { if (this.props.onClick){ this.props.onClick(); } } render() { return( <div className={"button"} onClick={this.handleClick.bind(this)}> {this.props.display} </div> ) } } export default Button; Código do arquivo calculator.js: import {Component} from 'react'; import Button from '../components/button'; import Display from '../components/display'; class Calculator extends Component { render() { return ( <div className={"calculator"}> <Display value={"1234567890"}/> <Button display={"1"}/> <Button display={"2"}/> <Button display={"3"}/> <Button display={"4"}/> <Button display={"5"}/> <Button display={"6"}/> <Button display={"7"}/> <Button display={"8"}/> <Button display={"9"}/> <Button display={"0"}/> <Button display={"+"}/> <Button display={"-"}/> <Button display={"="}/> <Button display={"C"}/> </div> ) } } export default Calculator; Código do arquivo App.js: import React from 'react'; import Calculator from './containers/calculator' export function App() { return ( <div className="scren"> <Calculator/> </div> ) } Código do arquivo index.js: import React from 'react'; import ReactDOM from 'react-dom'; import {App} from './App'; ReactDOM.render( <App/>, document.getElementById('root') ); Aula 07 - Estilizando a nossa calculadora Parte 01: Para estilizar uma aplicação React trabalha-se com o CSS de maneira análoga a quando se trabalha com estilizando direto a página HTML porém já que o HTML é criado nos scripts JS deve ser levado em conta este ao invés do HTML em si, por isso é necessário importar o CSS dentro do arquivo JS que cria o HTML a ser usado. No index.js: import React from 'react'; import ReactDOM from 'react-dom'; import {App} from './App'; import "./index.css"; ReactDOM.render( <App/>, document.getElementById('root') ); No app.js: import React from 'react'; import Calculator from './containers/calculator'; import './app.css'; export function App() { return ( <div className="screen"> <div className="divSup"/> <Calculator /> <div className="divInf"/> </div> ); } No caso o reset do CSS foi feito no index.js e a estilização do app foi feito no app.css e app.js. Noções de Grid no CSS: O Grid do CSS tem por objetivo dividir a tela em frações determinadas pelo dev. No exemplo da calculadora decide-se utilizar uma divisão de linhas de ‘10fr 80fr 10fr’ onde ‘fr’ representa a fração das telas. E para fazer com que a implementação deste código CSS seja feita em toda a vista (view) é necessário determinar que o Height seja igual a 100vh (100% de view height), chegando em: No app.css: .screen { display: grid; grid-template-rows: 10fr 80fr 10fr; height: 100vh; } Parte 02: Aqui é importate freezar que o grid-template-columns divide a tela de maneira horizontal, e o grid-template-rows de maneira vertical. Com isso em mente utilizamos o rows e o columns para centralizar a calculadora na página. No app.css: .screen { display: grid; /* Cria 3 linhas e 3 colunas nas frações indicadas */ grid-template-rows: 10fr 80fr 10fr; grid-template-columns: 35fr 30fr 35fr; height: 100vh; } .calculator { /* Insere os elementos da div 'calculator' na coluna 2, linha 2 */ grid-column: 2; grid-row: 2; border: 1px solid black; border-radius: 5%; padding: 1em; } .button { display: grid; place-items: center; width: 3em; height: 3em; border: 1px solid black; } Parte 03: Aqui consertamos a disposição dos botões na tela para que fiquem centralizados e ao usar o flex-wrap: wrap os botões quando chegarem ao final de seu container eles comecem no inicio dele na próxima linha: .screen { display: grid; /* Cria 3 linhas e 3 colunas nas frações indicadas */ grid-template-rows: 10fr 80fr 10fr; grid-template-columns: 35fr 30fr 35fr; height: 100vh; } .calculator { /* Insere os elementos da div 'calculator' na coluna 2, linha 2 */ grid-column: 2; grid-row: 2; border: 1px solid black; border-radius: 5%; padding: 1em; /* Terá altura máxima de 100% da calculadora e tudo que passar (overflow) estará escondido (hidden) */ max-height: 100%; overflow: hidden; display: flex; flex-direction: column; } .button { display: grid; margin: 1em; place-items: center; width: 3em; height: 3em; border: 1px solid black; background-color: brown; color: white; } .buttonsContainer { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; background-color: burlywood; } Parte 04: Apenas terminamos de estilizar o código obtendo: .screen { display: grid; /* Cria 3 linhas e 3 colunas nas frações indicadas */ grid-template-rows: 10fr 80fr 10fr; grid-template-columns: 35fr 30fr 35fr; height: 100vh; } .calculator { /* Insere os elementos da div 'calculator' na coluna 2, linha 2 */ grid-column: 2; grid-row: 2; border: 1px solid black; border-radius: 5%; padding: 1em; /* Terá altura máxima de 100% da calculadora e tudo que passar (overflow) estará escondido (hidden) */ max-height: 100%; overflow: hidden; display: flex; flex-direction: column; justify-content: center; } .button { display: grid; margin: 1em; place-items: center; width: 3em; height: 3em; border: 1px solid black; background-color: brown; color: white; cursor: pointer; } .buttonsContainer { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; background-color: burlywood; border: 2px solid rgb(48, 156, 80); } .display { display: flex; justify-content: flex-end; border: 1px solid black; background-color: violet; color: white; } Aula 08 - Dando funções à nossa Calculadora Parte 01: Agora voltamos a mexer com o JS do react de fato. Toda classe em React possui um estado inicial que deve ser setada no constructor da classe. Aqui ainda fazemos o display mostrar o valor através da função getValue() e usamos a função putValue() para pegar o valor quando clicado nos botões através do onCLick() neles. import { Component } from 'react'; import Button from '../components/button'; import Display from '../components/display'; class Calculator extends Component { initialState = { firstValue: 0, secondValue: 0 }; constructor(props) { super(props); //Faz herdar o 'props' do Component this.state = this.initialState; } putValue = (value) => { const lastValue = this.state.firstValue; this.setState({ firstValue: lastValue * 10 + value }); }; getValue = () => { return this.state.firstValue; }; render() { return ( <div className={'calculator'}> <div> <Display value={this.getValue()} /> </div> <div className="buttonsContainer"> <Button display={'1'} onClick={() => this.putValue(1)} /> <Button display={'2'} onClick={() => this.putValue(2)} /><Button display={'3'} onClick={() => this.putValue(3)} /> <Button display={'4'} onClick={() => this.putValue(4)} /> <Button display={'5'} onClick={() => this.putValue(5)} /> <Button display={'6'} onClick={() => this.putValue(6)} /> <Button display={'7'} onClick={() => this.putValue(7)} /> <Button display={'8'} onClick={() => this.putValue(8)} /> <Button display={'9'} onClick={() => this.putValue(9)} /> <Button display={'0'} onClick={() => this.putValue(0)} /> <Button display={'+'} /> <Button display={'-'} /> <Button display={'='} /> <Button display={'C'} /> </div> </div> ); } } export default Calculator; Parte 02: Aqui trabalhamos as operações que podem ser executadas com a calculador, utilizando o operator que será uma variável introduzida no initialState para guardar qual operação está sendo executada. import { Component } from 'react'; import Button from '../components/button'; import Display from '../components/display'; class Calculator extends Component { initialState = { firstValue: 0, secondValue: 0, operator: 1, isSum: false }; constructor(props) { super(props); //Faz herdar o 'props' do Component this.state = this.initialState; } putValue = (value) => { const {firstValue, secondValue, operator} = this.state; const lastValue = operator === 1 ? firstValue : secondValue; switch (operator) { case 1: this.setState({ firstValue: (lastValue * 10) + value }); break; case 2: this.setState({ secondValue: (lastValue * 10) + value }); break; } }; getValue = () => { const {firstValue, secondValue, operator, isSum} = this.state switch (operator) { case 1: return firstValue; case 2: return secondValue; case 3: return isSum ? firstValue + secondValue : firstValue - secondValue; } }; pickOperation = (isSum) => { this.setState({ operator: 2, isSum }); }; execOperation = () => { this.setState({ operator: 3 }); }; clear = () => { this.setState(this.initialState); } render() { return ( <div className={'calculator'}> <div> <Display value={this.getValue()} /> </div> <div className="buttonsContainer"> <Button display={'1'} onClick={() => this.putValue(1)} /> <Button display={'2'} onClick={() => this.putValue(2)} /> <Button display={'3'} onClick={() => this.putValue(3)} /> <Button display={'4'} onClick={() => this.putValue(4)} /> <Button display={'5'} onClick={() => this.putValue(5)} /> <Button display={'6'} onClick={() => this.putValue(6)} /> <Button display={'7'} onClick={() => this.putValue(7)} /> <Button display={'8'} onClick={() => this.putValue(8)} /> <Button display={'9'} onClick={() => this.putValue(9)} /> <Button display={'0'} onClick={() => this.putValue(0)} /> <Button display={'+'} onClick={() => this.pickOperation(true)} /> <Button display={'-'} onClick={() => this.pickOperation(false)} /> <Button display={'='} onClick={() => this.execOperation()}/> <Button display={'C'} onClick={() => this.clear()}/> </div> </div> ); } } export default Calculator; Parte 03: Organizamos mais ainda o código e implementamos a função de desabilitar botões através do this.props.disabled NO button.js: import React, { Component } from 'react'; class Button extends Component { handleClick() { const { disabled, onClick } = this.props; if (onClick && !disabled) this.props.onClick(); } render() { const cssButtonClass = this.props.disabled ? 'button disabled' : 'button'; return ( <div className={cssButtonClass} onClick={this.handleClick.bind(this)} > {this.props.display} </div> ); } } export default Button; No calculator.js: import { Component } from 'react'; import Button from '../components/button'; import Display from '../components/display'; class Calculator extends Component { initialState = { firstValue: 0, secondValue: 0, operator: 1, isSum: false }; constructor(props) { super(props); //Faz herdar o 'props' do Component this.state = this.initialState; } putValue = (value) => { const {firstValue, secondValue, operator} = this.state; const lastValue = operator === 1 ? firstValue : secondValue; switch (operator) { case 1: this.setState({ firstValue: (lastValue * 10) + value }); break; case 2: this.setState({ secondValue: (lastValue * 10) + value }); break; } }; getValue = () => { const {firstValue, secondValue, operator, isSum} = this.state switch (operator) { case 1: return firstValue; case 2: return secondValue; case 3: return isSum ? firstValue + secondValue : firstValue - secondValue; } }; pickOperation = (isSum) => { this.setState({ operator: 2, isSum }); }; execOperation = () => { this.setState({ operator: 3 }); }; clear = () => { this.setState(this.initialState); } render() { const {operator} = this.state; return ( <div className={'calculator'}> <div> <Display value={this.getValue()} /> </div> <div className="buttonsContainer"> <Button display={'1'} onClick={() => this.putValue(1)} disabled={operator === 3}/> <Button display={'2'} onClick={() => this.putValue(2)} disabled={operator === 3}/> <Button display={'3'} onClick={() => this.putValue(3)} disabled={operator === 3}/> <Button display={'4'} onClick={() => this.putValue(4)} disabled={operator === 3}/> <Button display={'5'} onClick={() => this.putValue(5)} disabled={operator === 3}/> <Button display={'6'} onClick={() => this.putValue(6)} disabled={operator === 3}/> <Button display={'7'} onClick={() => this.putValue(7)} disabled={operator === 3}/> <Button display={'8'} onClick={() => this.putValue(8)} disabled={operator === 3}/> <Button display={'9'} onClick={() => this.putValue(9)} disabled={operator === 3}/> <Button display={'0'} onClick={() => this.putValue(0)} disabled={operator === 3}/> <Button display={'+'} onClick={() => this.pickOperation(true)} disabled={operator !== 1}/> <Button display={'-'} onClick={() => this.pickOperation(false)} disabled={operator !== 1}/> <Button display={'='} onClick={() => this.execOperation()}disabled={operator === 1}/> <Button display={'C'} onClick={() => this.clear()}/> </div> </div> ); } } export default Calculator; No index.css: html, body { margin: 0; padding: 0; width: 100%; height: 100%; } .disabled { background-color: gray; } Aula 09 - Axios Teórica Axios vs. Fetch Aula 10 - Nossa APP Links utilizados: http://star-wars-brigolini.herokuapp.com/ https://swapi.dev/ https://github.com/brigolini/StarWars https://bcherny.github.io/json-schema-to-typescript-browser/ https://app.quicktype.io/ Aula 11 – Design da App Aqui estão algumas explicações sobre como será o design da App e como funciona a API de star wars Aula 12 – Criando a App Esta App funciona como uma especie de wikipedia do Star Wars. Iniciamos a App criando o diretório através do • npx create-react-app star_wars –-template typescript A partir deste projeto será muito utilizado o typescript como linguagem principal. Uma coisa a se notar é que algumas configurações devem ser feitas, a primeira é no manifest.json é necessário que a propriedade JSX esteja como "jsx": "react". Após isto é de boa prática excluir os logos na pasta public e todo o conteúdo da pasta src exceto pelo reportWebVital.tsx. Aula 13 - Criandoa instância do Axios Aqui iremos criar as instancias do axios para poder fazer os requests para a api import Axios, { AxiosInstance } from 'axios'; const baseURL = 'https://swappi.dev/api'; export const getAxiosInstance = (): AxiosInstance => { let axiosInstance = Axios.create({ baseURL: baseURL }); axiosInstance.interceptors.response.use( (response) => response, //retorna response se houver response (error) => { //se tiver erro e esse erro tiver status maior que 399 retorna a mensagem abaixo const { status } = error.response; if (status > 399) console.info(`Erro na API. Status: ${status}`); } ); return axiosInstance; }; Aula 14 - Aula teórica : Promisses Promises são pedaços do código que são executados após todo o código que não é promises for executado, este é o conceito de programação assíncrona. Lembrando que ‘todo GET retorna uma promisse’. Aula 15 - Iniciando a nossa controller genérico Agora precisamos criar o controller que será responsável por receber os schemas da api. Para isso precisamos fazer um axios.get nos schemas da api, então simplesmente escrevemos: No generic-api.ts import Axios from "axios"; import { getAxiosInstance } from "../axios-instance"; export type SWAPIEndpoint = 'people' | 'films' | 'startships' | 'vehicles' | 'species' | 'planets'; export const genericController = (endpoint: SWAPIEndpoint) => { const axios = getAxiosInstance(); const getSchema = async () => { const response = await axios.get(`/${endpoint}/schema`); return response.data; } return {getSchema} } Aula 16 - Testando a nossa função de Schema Para testar a função getSchema retornada pelo genericController precisamos atribuir ela a uma variável para ser usada. Apó atribuído a ideia é setar um valor para uma variável criada schema para poder mostrar na tela e ver se está tudo ok com o axios criado para lidar com a API; lembrando que toda vez que uma função retorna uma promises ela deve ser acrescida de .then() ou o await/async. No app.tsx import React, { useState } from 'react'; import { genericController } from './api/generic-api'; export const App = () => { //cria-se a variável schema e a função setSchema para lidar com ela e neste exemplo só retorna o description const [schema, setSchema] = useState({description:''}); //de genericController desestruturamos para pegar a função getSchema, de 'people' desta vez para usar como teste const {getSchema} = genericController('people'); //Usamos getSchema que após ter a promise fullfilled retorna o valor da key description de people e através de setSchema atualizamos seu valor. getSchema().then( valor => setSchema(valor) ) //renderiza o valor armazenado na variável schema return <div> {schema.description} </div> }; Aula 17 - Criando e testando a nossa função getByld Agora iremos fazer o mesmo processo, mas para o ID do item na API. Ainda implementamos uma interface para garantir que o tipo que está sendo usado e devolvido é o do utilizado pela função. Após isto direcionamos a atenção para o app.tsx, onde pegamos a função criada getByID e usamos novamente o setSchema para dar um valor ao schema para renderizá-lo. Aqui temos que resolver um problema gerado na execução do novo estado que cria um loop infinito (pois quando um novo estado é gerado a função inicia novamente e entra num loop) e para isso usamos o useEffect, que recebe dois argumentos e dita a quantidade de vezes que o primeiro argumento será executado. No generic-api.ts: import { getAxiosInstance } from "./axios-instance"; export type SWAPIEndpoint = 'people' | 'films' | 'startships' | 'vehicles' | 'species' | 'planets'; export interface ResourceReturn { getSchema:() =>void; getByID:(id: number) => Promise<any> } export const genericController = (endpoint: SWAPIEndpoint) => { const axios = getAxiosInstance(); const getSchema = async () => { const response = await axios.get(`/${endpoint}/schema`); return response.data; } const getByID = async (id: number) => { const response = await axios.get(`/${endpoint}/${id}`); return response.data; } return {getSchema, getByID} } No app.tsx: import React, { useEffect, useState } from 'react'; import { genericController } from './api/generic-api'; export const App = () => { const [schema, setSchema] = useState({name:''}); const {getByID} = genericController('people'); useEffect(() => { getByID(1).then( valor => setSchema(valor) ) }) return <div> {schema.name} </div> }; Aula 18 - Entendendo Generics Nesta aula introduz-se o conceito de generics que é basicamente um jeito de ‘tipar’ de maneira genérica o resultado da promises, no exemplo dado, se você der request em algo do tipo ‘People’ ele vai retornar uma promises do tipo ‘People’. Isto é interessante por permite uma manutenção mais fácil do código posteriormente e isso garante que seu controller não receba valores erados. Dito isso vamos ao código: No generic-api.ts: import { getAxiosInstance } from "./axios-instance"; export type SWAPIEndpoint = 'people' | 'films' | 'startships' | 'vehicles' | 'species' | 'planets'; export interface ResourceReturn<T> { getSchema:() =>void; getByID:(id: number) => Promise<T> } export const genericController = <T>(endpoint: SWAPIEndpoint):ResourceReturn<T> => { const axios = getAxiosInstance(); const getSchema = async () => { const response = await axios.get(`/${endpoint}/schema`); return response.data; } const getByID = async (id: number):Promise<T> => { const response = await axios.get(`/${endpoint}/${id}`); return response.data; } return {getSchema, getByID} } No app.tsx: import React, { useEffect, useState } from 'react'; import { genericController } from './api/generic-api'; import { People } from './api/schemas/people' export const App = () => { //com esse novo atributo de tipo ele irá selecionar tudo do tipo passado para ele, no caso usamos People para testar const [schema, setSchema] = useState<People>(); const {getByID} = genericController<People>('people'); useEffect(() => { getByID(1).then( valor => setSchema(valor) ) }) //aqui implementamos apenas um teste para saber se o schema existe return <div> {schema?schema.name:null} </div> }; Aula 19 – GetAll Parte 01: Começamos definindo o tipo de dados que o getAll pode retornar. No generic-api.ts import { getAxiosInstance } from "./axios-instance"; export type SWAPIEndpoint = 'people' | 'films' | 'startships' | 'vehicles' | 'species' | 'planets'; export interface PageData { proximo: number | null; anterior: number | null; total: number; totalRegistros: number; } export interface PageableResponse<T> { dados: T[]; page: PageData; } export interface ResourceReturn<T> { getSchema:() =>void; getByID:(id: number) => Promise<T>; getAll: (page: number) => Promise<PageableResponse<T>>; } export const genericController = <T>(endpoint: SWAPIEndpoint):ResourceReturn<T> => { const axios = getAxiosInstance(); const getSchema = async () => { const response = await axios.get(`/${endpoint}/schema`); return response.data; } const getByID = async (id: number):Promise<T> => { const response = await axios.get(`/${endpoint}/${id}`); return response.data; } return {getSchema, getByID} } Parte 02: Aqui criamos as funções getPageNumber e getPageData para trazer os dados da página para a função getAll que é responsável por pegar todos os dados da página. No generic-api.ts import { AxiosResponse } from "axios"; import { getAxiosInstance } from "./axios-instance"; export type SWAPIEndpoint = 'people' | 'films' | 'startships' | 'vehicles' | 'species' | 'planets'; export interface PageData { proximo: number | null; anterior: number | null; total: number; totalRegistros: number; } export interface PageableResponse<T> { dados: T[]; page: PageData; } export interface ResourceReturn<T> { getSchema: () => void; getByID: (id:number) => Promise<T>; getAll: (page: number) => Promise<PageableResponse<T>>; } export const genericController = <T>(endpoint: SWAPIEndpoint): ResourceReturn<T> => { const axios = getAxiosInstance(); const getSchema = async () => { const response = await axios.get(`/${endpoint}/schema`); return response.data; } const getByID = async (id: number): Promise<T> => { const response = await axios.get(`/${endpoint}/${id}`); return response.data; } const getAll = async (pageNumber: number): Promise<PageableResponse<T>> => { const response = await axios.get(`/${endpoint}/?page=${pageNumber}`); const dados: T[] = response.data.results; const page: PageData = getPageData(response); return {page, dados}; } const getPageData = (response: AxiosResponse): PageData => { const proximo: number | null = getPageNumber(response.data.next); const anterior: number | null = getPageNumber(response.data.previous); const resultSize = response.data.results.length; const count = response.data.count; const total: number = (count % resultSize) === 0 ? (count / resultSize) : Math.floor(count / resultSize) + 1; return {proximo, anterior, total, totalRegistros: count} } const getPageNumber = (url: string | null): number | null => { if(!url) return null; const matchs = url.match(`page=\\d+`); if(matchs) return parseInt(matchs[0].replace('page=','')); return null; } return { getSchema, getByID, getAll } } Parte 03: Agora começamos a testar o método getAll que será importado par ao arquivo App.tsx. Aqui é interessante salientar o uso do React fragmente que permite o navegador entender apenas um elemento pai do tipo div para que mais de uma div possa ser carregada na hora de renderizar. import React, { useEffect, useState } from 'react'; import { genericController, PageData } from './api/generic-api'; import { People } from './api/schemas/people' export const App = () => { const { getAll } = genericController<People>('people'); const [people, setPeople] = useState<People[]>(); const [page, setPage] = useState<PageData>(); useEffect(() => { getAll(1).then( valor => { setPeople(valor.dados) setPage(valor.page) } )}, []) if (people) { return ( <> <div>Paginação: Anterior: {page?.anterior} - Próximo: {page?.proximo} </div> {people.map(person => <div>{person.name}</div>)} </> ); } else { return (<div></div>); } }; Aula 20 - Criando nosso GetByPartialName Agora está praticamente terminada toda a interface para pegar os dados da API. Então podemos começar a focar na parte gráfica. Mas primeiro ciramos uma função para pegar um dado de uma página X pelo partial Name. No generic-api.tsx import { AxiosResponse } from "axios"; import { getAxiosInstance } from "./axios-instance"; export type SWAPIEndpoint = 'people' | 'films' | 'startships' | 'vehicles' | 'species' | 'planets'; export interface PageData { proximo: number | null; anterior: number | null; total: number; totalRegistros: number; } export interface PageableResponse<T> { dados: T[]; page: PageData; } export interface ResourceReturn<T> { getSchema: () => void; getByID: (id: number) => Promise<T>; getAll: (page: number) => Promise<PageableResponse<T>>; getByPartialName: (searchTerm: string, pageNumber: number) => Promise<PageableResponse<T>>; } export const genericController = <T>(endpoint: SWAPIEndpoint): ResourceReturn<T> => { const axios = getAxiosInstance(); const getSchema = async () => { const response = await axios.get(`/${endpoint}/schema`); return response.data; } const getByID = async (id: number): Promise<T> => { const response = await axios.get(`/${endpoint}/${id}`); return response.data; } const getAll = async (pageNumber: number): Promise<PageableResponse<T>> => { const response = await axios.get(`/${endpoint}/?page=${pageNumber}`); const dados: T[] = response.data.results; const page: PageData = getPageData(response); return {page, dados}; } const getByPartialName = async (searchTerm: string, pageNumber: number): Promise<PageableResponse<T>> => { const response = await axios.get(`/${endpoint}/?search=${searchTerm}/?page=${pageNumber}`); const dados: T[] = response.data.results; const page: PageData = getPageData(response); return {dados, page}; }; E então para a próxima aula intalamos o matéria-ui/core usando o npm install. Aula 21 – Aula teórica: Hooks “Hooks veio parar separar a regra de negocia da renderização. “ A partir da versão 16.8 do React foi introduzido o hooks que é um componente externo ao componente que será renderizado que tem a regra de negócio para alterar o componente renderizável. Esses hooks são funções do React que fazem essa leitura de estado e alteram, de alguma maneira, a renderização. · useState: const [state, setState] = useState(initialState); Retorna um valor e uma função para atualizar o valor. · useEffect: useEffect(didUpdate); Aceita uma função que contém um código imperativo, possivelmente efetivo. Por padrão, os efeitos são executados após cada renderização concluída, mas você pode optar por dispará-los somente quando determinados valores receberam atualização. · useMemo: const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]); É usado para aumentar a performance, o useMemo salva um determinado valor e usa esse valor até que haja uma modificação nos parâmetros observados. No exemplo ele só chama a função computerExpensiveValue() quando houver alteração nos valores dentro do array passado sempre como segundo argumento. · useCallBack: const memoizedCallback = useCallback(() => { doSomething(a, b);}, [a, b]); Basicamente a mesma coisa que o useMemo, faz alguma coisa quando houve alteração nos valore sobservados. É importante lembrar que sempre que um hook é alterado o componente é renderizado novamente. Hook adiciona complexidade, e não pode ser cirado dentro de laços e dentro de funções ou condicionais. Aula 22 - Material UI Material UI é uma biblioteca gráfica que pode ser utilizada no desenvolvimento. É baseada no https://material.io/design . Aula 23 – TypeScript TS é a versão tipada de JS, ou seja, é possível colocar tipos para parâmetros e variáveis do JS. Isso facilita no uso de grandes equipes, na escalabilidade do código e a segurança que os tipos trazem. Aula 24 - Apresentando o componente Detalhe Aqui começamos a escrever o novo componente que será responsável por mostrar os detalhes das informações que se quer mostrar. Src/componentes/detalhe.tsx import React from "react"; import { SWAPIEndpoint } from "../../api/generic-api"; import {CircularProgress} from "@material-ui/core"; interface DetalheProps { id: number, controller: SWAPIEndpoint, } const Detalhe = (props: DetalheProps) => { const { id, controller } = props; const {isLoading, result, error} = useDetalhe(id, controller); if(isLoading) return <div><CircularProgress/></div> if(!result) return null; return ( ); }; Perceba que ainda não foi criado o componente use-detalhe mas já trabalhamos com ele como se estivesse pronto pois assim que estiver ele irá funcionar. Aula 25 - Criando a estrutura genérica de dados Aqui vale lembrar que uma função do tipo getter pode acabar retornando recebendo um valor undefined e caso não consiga tratar este valor pode dar algum problema por isso é sempre interessante que se cheque se o valor pego existe, caso não exista deve retornar null caso contrario deve continuar com a função. Aqui criamos o getDetailData que nos traz as colunas que devem ser mostradas ao selecionar um tipo de informação para pesquisar No controller-defs.ts import { SWAPIEndpoint } from "./generic-api"; interface ControllerDefs { detailData: string[]; } const controllerData : Map<string, ControllerDefs> = new Map(); controllerData.set(`people`,{ detailData:[`name`, `mass`,`hair_color`,`skin_color`,`gender`] }) controllerData.set(`films`,{ detailData: [`title`,`director`,`producer`,`opening_crawl`] }) export const getDetailData = (controller:SWAPIEndpoint):string[] | null => { const coluna = controllerData.get(controller); if(!coluna) return null; return coluna.detailData; } Aula 26 – Componente de detalhe Parte 01: Aqui começamos a codar a parte de renderização dos campos para cada item. Para isso criamos dois elementos <Grid>, que vem do material-ui, um deles contendo o botão de voltar e o outro contendo os items encontrados. O botão é bem simples, o código inserido ali é um <Button> que vem do material-ui. Já o grid com as informações usamos um Object.entries para retornar um array das propriedades de chave/valor de um determinado objeto, no caso de ‘result’. Então filtramos os valores trazidos pelo resultado para que ele bata com os valores trazidos pelo getDetailData() e então usamos o .map() para retornar um <Field>, que ainda será criado, para cada item filtrado do result conendo sua key (nome do campo) e seu value (seu valor). No detalhe.tsx: import React from 'react'; import { SWAPIEndpoint } from '../../api/generic-api'; import { Button, CircularProgress, Grid } from '@material-ui/core'; import { getDetailData } from '../../api/controller-defs'; interface DetalheProps { id: number; controller: SWAPIEndpoint; } const Detalhe = (props: DetalheProps) => { const { id, controller } = props; const { isLoading, result, error } = useDetalhe(id, controller); const colunas = getDetailData(controller); if (isLoading) return ( <div> <CircularProgress /> </div> ); if (!result) return null; return ( <Grid container direction={'column'} spacing={2} alignItems={'stretch'}> <Grid> {Object.entries(result) .filter((item) => { const [key] = item; return colunas?.find((campo) => campo === key); }) .map((item) => { const [key, value] = item; return <Field nome={key} value={value} />; })} </Grid> <Grid> <Button variant={'outlined'}>Voltar</Button> </Grid> </Grid> ); }; Parte 02: Agora montamos o render accordion que será responsável por mostrar o sumário e o detail dos valores que forem puxados da API. No detalhe/field.tsx: import { Accordion, AccordionDetails, AccordionSummary, Grid } from "@material-ui/core"; import React from "react"; import ExpandMoreIcon from "@material-ui/icons/ExpandMore" interface FieldProps { nome: string; valor: string; } export const Field = (props: FieldProps) => { const {nome, valor} = props; return ( <Grid item xs={12}> <Accordion> <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="panella-content" id="panella-header"> </AccordionSummary> <AccordionDetails> </AccordionDetails> </Accordion> </Grid> ); } No detalhe.tsx: import React from 'react'; import { SWAPIEndpoint } from '../../api/generic-api'; import { Button, CircularProgress, Grid } from '@material-ui/core'; import { getDetailData } from '../../api/controller-defs'; import { Field } from './field'; interface DetalheProps { id: number; controller: SWAPIEndpoint; } const Detalhe = (props: DetalheProps) => { const { id, controller } = props; const { isLoading, result, error } = useDetalhe(id, controller); const colunas = getDetailData(controller); if (isLoading) return ( <div> <CircularProgress /> </div> ); if (!result) return null; return ( <Grid container direction={'column'} spacing={2} alignItems={'stretch'}> <Grid> {Object.entries(result) .filter((item) => { const [key] = item; return colunas?.find((campo) => campo === key); }) .map((item) => { const [key, value] = item; return <Field nome={key} valor={value}/>; })} </Grid> <Grid> <Button variant={'outlined'}>Voltar</Button> </Grid> </Grid> ); }; Aula 27 - Criando o hook useAsync Aqui criamos o useAsync para capturar os dados de isLoading, Error e result que são passados para a renderização dos items. No useEffect, como segundoparametro passamos as dependências para que ele seja executado, dentro do array vao as funções setError, setIsLoading, setResult e asyncFn, dessa forma o useEffect só será rodado quando após cada uma das dependências for atualizada. Só que existe um “porém”, o React, por definição não pode ter funções assincornas sendo executadas dentro dele, para brular este problema, criamos uma função foo() que executa o código asincorono e passa o resultado para a função do React executar. Isto se trata de um arranjo técnico. No src/comum/hooks/use-aync.ts: import { useEffect, useState } from "react" interface AsyncReturnType<T> { isLoading: boolean; error: boolean; result: T | undefined; } export const useAsync = <T>(asyncFn: Promise<T>): AsyncReturnType => { const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(false); const [result, setResult] = useState<T>(); useEffect(() => { const foo = async () =>{ setIsLoading(true); setError(false); try{ const result = await asyncFn; setIsLoading(false); setResult(result); } catch (err) { setIsLoading(false); setError(true); }} foo(); }, [setIsLoading, setResult, setError, asyncFn]); return {isLoading, error, result}; } Aula 28 – Implementando o componente detalhe Parte 01: Agora iremos fazer o use-detalhe, nele é necessário o uso do useMemo() pois só queremos que o getByID seja chamado caso haja alguma mudança no tipo do controller ou caso o ID mude. Utilizamos aqui também o use-async para carregar os dados. A renderização é feita toda no detalhe, que pegará o valor passado para o fields e renderizará. Aqui existe um detalhe a se notar, foi necessário trocar o tipo do ‘valor’ no FieldProps para ‘any’ pois no map do detalhe.tsx ele diz que o ‘value’ é desconhecido e por isso nao deixa compilar. No detalhe.tsx: import React from 'react'; import { SWAPIEndpoint } from '../../api/generic-api'; import { Button, CircularProgress, Grid } from '@material-ui/core'; import { getDetailData } from '../../api/controller-defs'; import { Field } from './field'; import { useDetalhe } from './use-detallhe'; import { People } from '../../api/schemas/people'; interface DetalheProps { id: number; controller: SWAPIEndpoint; } export const Detalhe = (props: DetalheProps) => { const { id, controller } = props; const { isLoading, result, error } = useDetalhe<People>(id, controller); const colunas = getDetailData(controller); if (isLoading) return ( <div> <CircularProgress /> </div> ); if (!result) return null; return ( <Grid container direction={'column'} spacing={2} alignItems={'stretch'}> <Grid> {Object.entries(result) .filter((item) => { const [key] = item; return colunas?.find((campo) => campo === key); }) .map(item => { const [key, value] = item; return <Field nome={key} valor={value}/>; })} </Grid> <Grid> <Button variant={'outlined'}>Voltar</Button> </Grid> </Grid> ); }; No use-async.tsx: import { useEffect, useState } from "react" export interface AsyncReturnType<T> { isLoading: boolean; error: boolean; result: T | undefined; } export const useAsync = <T>(asyncFn: Promise<T>): AsyncReturnType<T> => { const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(false); const [result, setResult] = useState<T>(); useEffect(() => { const foo = async () =>{ setIsLoading(true); setError(false); try{ const result = await asyncFn; setIsLoading(false); setResult(result); } catch (err) {setIsLoading(false); setError(true); }} foo(); }, [setIsLoading, setResult, setError, asyncFn]); return {isLoading, error, result}; } No use-detalhe.tsx: import { useMemo } from "react"; import { genericController, SWAPIEndpoint } from "../../api/generic-api"; import { AsyncReturnType, useAsync } from "../../comum/hooks/use-async"; export const useDetalhe = <T> (id:number, controllerName:SWAPIEndpoint):AsyncReturnType<T> => { const controller = useMemo(() => genericController<T>(controllerName).getByID(id),[controllerName, id]); const {error, isLoading, result} = useAsync<T>(controller); return {error, isLoading, result}; } Parte 02: Aqui alteramos alguns parâmetros de tipo existentes no detalhe.tsx, no app.tsx e criamos um css básico para resetar o css do site. html, body { margin: 0; padding: 0; border: 0; box-sizing: border-box; } .centralizado { display: grid; place-items: center; width: 100vw; height: 100vh; } Aula 29 - Componente lista Daqui para frente o professor parou de codar durante a aula, o código já vinha pronto para a aula, então ficaram apenas algumas anotações que achei interessante levar em conta. Lembrando que todo o código está disponível em https://github.com/brigolini/StarWars useDebounce: é um módulo importado que permite atualizar alguma varíavel apenas depois de algum tempo. No caso ele utiliza esta função para atualizar a variável ‘search’ apensar depois de 500ms para que a aplicação não fique buscando por um resultado a cada caractere digitado pelo usuário. Isto garante que a API não fique senod acessada a cada caractere, o que dependendo da busca ou do usuário poderia levar ao impedimento do acesso da mesma. Aula 30 - Roteando nossa app O pacote que usamos responsável pelas rotas dentro da aplicação é o react-router-dom, ele é reponsável por enviar o usuário de uma página até a outra. Para instalar basta fazer um yarn add ou npm install de react-router-dom. OU caso esteja mexendo com TS (que é o caso desta app) utilizar o add/install @types/react-router-dom; pois o pacote não vem tipado. Aula 31 – Utilizando useHistory Aqui todas as rotas já funcionam, agora implementasse o useHistory que ativa o botão responsável por navegar entre as páginas do item pesquisado. O useHistory é um método importado que foi atribuído a uma variável ‘history’, ela é utilizada na função handleRowClick para levar o usuário para a página de detalhes do item selecionado. Para isto o professor usa o useMemo para que o código de handleRowClick só seja executado quando a variável ‘history’ e/ou ‘controller’ for alterada. O mesmo é feito utilizando para o botão voltar usando o handleVoltar através do ‘history,goBack()’ Então é criado o handlePageChange que é atribuído ao atributo onPageChange da tag DataGrid para que ao ter a página alterado essa alteração seja entendida pelo history para que possa voltar e ir de acordo. Aula 32 – Terminando nossa App Módulo 03 Aula 01 - Introdução e Preparação do Ambiente Aqui instalamos a extensão no VSCode chamada angular language servisse e rodamos o comando npm install -g @angular/cli Aula 02 - Arquitetura e Estrutura da Aplicação Parte 01: Criamos nossa primeira app em Angular utilizando o comando · ng new hello-world Após baixar a internet um app é criado e pode ser criado um servidor de desenvolvimento através do comando · ng serve –open Parte 02: O app.component.html é o arquivo responsável pelo html da SPA. Aula 03 - Componentes e Templates O Angular é baseado em componentes e templates Parte 01 - Introdução à componentes e templates: Todo component deve ter um jeito certo para ser nomeado, no exemplo a seguir criamos o arquivo c1.component.ts; o arquivo sempre terá seu nome, seguido de “.component.ts” para indicas ao Angular que é um componente e sempre na extensão TS pois o Angular é baseado em TS. O componente também deve começar importando Component de @angular/core e exportando a classe que ele cria. O componente deve fazer uso de um ou mais decorators que segundo a documentação: Components are the most basic UI building block of an Angular app. An Angular app contains a tree of Angular components. Angular components are a subset of directives, always associated with a template. Unlike other directives, only one component can be instantiated for a given element in a template. A component must belong to an NgModule in order for it to be available to another component or application. To make it a member of an NgModule, list it in the declarations field of the NgModule metadata. Após criar o componente ele deve se parecer com algo do tipo: No c2.component.ts: import { Component } from '@angular/core'; @Component ({ selector: 'c1', //como o componente será chamado por outro modulo como tag html templateUrl: './c1.component.html', //onde o template deste component está, o template é o que aparece na tela }) export class C1Component { } Então este componente deve ser incluído no módulo, para fazer isso no app.component.ts deve ser importado o componente recém criado. No app.component.ts: import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { C1Component } from './c1.component'; @NgModule({ declarations: [ AppComponent, C1Component ], imports: [ BrowserModule, AppRoutingModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } Agora podemos usar a tag c1 no html do app No app.component.ts: Hello World! <c1></c1> E esta página deve mostrar: Importante ressaltar aqui que todo componente montado usando o Angular é do tipo inLine a menos que esse comportamento seja alterado por estilo ou coisa do gênero. Outra maneira de criar um componente é através do comando · ng generate component nome-do-componente-aqui Esta é uma boa prática pois o comando ng já faz toda a organização do componente recém gerado para nós, criando o componente e todos seus arquivos relacionados dentro de um diretório, bem como já faz sua importação no app.component.ts. Parte 02 - Data binding: Data binding é uma característica do Angular que faz com que a interface reaja à mudanças de estados sem que haja a manipulação direta do DOM. Para fazer isso vamos montar um exemplo de um componente utilizando seu respectivo comando “ng” para criar um counter. No counter.component.ts: import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-counter', templateUrl: './counter.component.html', styleUrls: ['./counter.component.css'] }) export class CounterComponent { titulo: string = 'Contador'; valor: number = 0 increment() { this.valor++; } } Aqui criamos esse componente HTML através do Angular que pode ser adicionado apágina através da Tag <app-counter>, a ele atribuímos um atributo chamado titulo que é do tipo string e contém o valor ‘Contador’ e outro denominado ‘valor’ do tipo number que contém o valor 0. Ainda nesta classe, existe a função incremente() que adiciona 1 ao ‘this.value’ toda vez que é executada. Para executar, adicionamos esta função a um botão no criado no template do componente. Perceba que no código abaixo a sintaxe é um pouco diferente do que se faz em HTML puro, pois o angular permite isso. No exemplo a seguir também usamos o propety binding, que é atrelar uma propriedade do DOM a uma propriedade do componente. No counter.component.html: <!-- Mostra o atributo dentro do {{}} no DOM --> <p>Título: {{titulo}}</p> <p>Valor: {{valor}}</p> <!-- Faz o property binding desabilitando o botao quando o atributo 'valor' da classe for maior ou igual a 3 --> <button [disabled]="valor >= 3" (click)="increment()">Increment!</button> Aula 04 - Desafio Guiado 1 Parte 01: Iniciamos criando o ap usando o ng new tic-tac-toe e então dentro deste novo projeto criamos um componente chamado também tic-tac-toe.
Compartilhar