├── .gitignore
├── sesion-1-conceptos-basicos
└── rick-morty-app
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ ├── src
│ ├── App.css
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── components
│ │ └── CharacterCard.tsx
│ ├── index.css
│ ├── index.tsx
│ ├── react-app-env.d.ts
│ ├── reportWebVitals.ts
│ ├── setupTests.ts
│ └── types.ts
│ └── tsconfig.json
├── sesion-2-usestate-useeffect
└── taller-twitch
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ ├── src
│ ├── App.css
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── components
│ │ ├── MessageComponent.tsx
│ │ ├── MiniCodeComplexState.tsx
│ │ ├── MiniCodeEffectMount.tsx
│ │ ├── MiniCodeEffectUnmount.tsx
│ │ ├── MiniCodeInput.tsx
│ │ └── MiniCodeStateCallback.tsx
│ ├── index.css
│ ├── index.tsx
│ ├── react-app-env.d.ts
│ └── setupTests.ts
│ └── tsconfig.json
├── sesion-3-hooks
├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── src
│ ├── App.css
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── components
│ │ ├── MiniCodeInterval.tsx
│ │ ├── MiniCodeTaxCalculator.tsx
│ │ ├── MiniCodeUseRef.tsx
│ │ ├── MiniCodeUseRefCss .tsx
│ │ └── UseRefExamples.tsx
│ ├── index.css
│ ├── index.tsx
│ ├── react-app-env.d.ts
│ ├── reportWebVitals.ts
│ └── setupTests.ts
└── tsconfig.json
├── sesion-4-hooks-usememo-usecallback
├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── src
│ ├── App.css
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── components
│ │ ├── BasicUseCallback.tsx
│ │ ├── BasicUseMemo.tsx
│ │ ├── MiniCodePosts.tsx
│ │ ├── MiniCodePostsAsync.tsx
│ │ ├── MiniCodeTabManager.tsx
│ │ ├── MiniCodeUseCallback.tsx
│ │ └── MiniCodeUseMemo.tsx
│ ├── index.css
│ ├── index.tsx
│ ├── react-app-env.d.ts
│ ├── reportWebVitals.ts
│ └── setupTests.ts
└── tsconfig.json
├── sesion-5-custom-hooks
├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── src
│ ├── App.css
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── components
│ │ ├── MiniCodeCounter.tsx
│ │ ├── MiniCodeDebounce.tsx
│ │ ├── MiniCodeTabManager.tsx
│ │ └── ThirdCount.tsx
│ ├── hooks
│ │ ├── useCount.ts
│ │ └── useDebounceString.ts
│ ├── index.css
│ ├── index.tsx
│ ├── react-app-env.d.ts
│ ├── reportWebVitals.ts
│ └── setupTests.ts
└── tsconfig.json
├── sesion-6-react-router
├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── src
│ ├── App.css
│ ├── App.tsx
│ ├── data
│ │ └── heroes.ts
│ ├── index.css
│ ├── index.tsx
│ ├── pages
│ │ ├── Hero.tsx
│ │ ├── Heroes.tsx
│ │ └── Home.tsx
│ └── react-app-env.d.ts
└── tsconfig.json
├── sesion-7-context
├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ ├── robots.txt
│ └── schema.PNG
├── src
│ ├── App.css
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── components
│ │ ├── Header.tsx
│ │ └── ThemeSwitcher.tsx
│ ├── context
│ │ └── ThemeContext.tsx
│ ├── index.css
│ ├── index.tsx
│ └── react-app-env.d.ts
└── tsconfig.json
└── sesion-8-forms
├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
├── src
├── App.css
├── App.tsx
├── components
│ ├── ErrorMessage.tsx
│ ├── HookForm.tsx
│ ├── HookFormRefactored.tsx
│ ├── Input.tsx
│ ├── RefForm.tsx
│ └── StateForm.tsx
├── index.css
├── index.tsx
├── react-app-env.d.ts
└── types.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/README.md:
--------------------------------------------------------------------------------
1 | # MiniCodeLab - Taller de React desde 0 Parte 1
2 |
3 | Repositorio creado en el primer Taller de React desde Cero de MiniCodeLab 🦄.
4 |
5 | Aquí te dejamos el vídeo resubido del taller para ver cómo hemos desarrollado este código: https://youtu.be/iXZYPN3Nilc
6 |
7 | Y los artículos de la web donde podrás repasar todo lo que hemos trabajado juntos:
8 |
9 | - https://www.minicodelab.dev/feed/react-0-basicos-1
10 | - https://www.minicodelab.dev/feed/react-0-basicos-2
11 |
12 | Gracias por participar en esta comunidad ♥
13 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "taller-0-minicodelab",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.2",
8 | "@testing-library/user-event": "^13.5.0",
9 | "@types/jest": "^27.4.0",
10 | "@types/node": "^16.11.22",
11 | "@types/react": "^17.0.39",
12 | "@types/react-dom": "^17.0.11",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "5.0.0",
16 | "typescript": "^4.5.5",
17 | "web-vitals": "^2.1.4"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test"
23 | },
24 | "eslintConfig": {
25 | "extends": [
26 | "react-app",
27 | "react-app/jest"
28 | ]
29 | },
30 | "browserslist": {
31 | "production": [
32 | ">0.2%",
33 | "not dead",
34 | "not op_mini all"
35 | ],
36 | "development": [
37 | "last 1 chrome version",
38 | "last 1 firefox version",
39 | "last 1 safari version"
40 | ]
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-1-conceptos-basicos/rick-morty-app/public/favicon.ico
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | Minicode React 0
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-1-conceptos-basicos/rick-morty-app/public/logo192.png
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-1-conceptos-basicos/rick-morty-app/public/logo512.png
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | background-color: #282c34;
3 | color: white;
4 | min-height: 100vh;
5 | padding: 1rem;
6 | text-align: center;
7 | }
8 |
9 | .character-list {
10 | align-items: flex-start;
11 | display: flex;
12 | flex-wrap: wrap;
13 | gap: 2rem;
14 | justify-content: center;
15 | list-style-type: none;
16 | margin: 0 auto;
17 | max-width: 1024px;
18 | }
19 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | render( );
7 | const linkElement = screen.getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/src/App.tsx:
--------------------------------------------------------------------------------
1 | // Hola MiniCoder! Te dejamos aquí el reto de refactorización del código:
2 | // - Debes conseguir que el archivo App.tsx quede lo más limpio posible
3 | // - Esto lo conseguiremos sacando la request a la API con useEffect y el useState a un componente CharacterList
4 | // - Ese componente devolverá en su return la lista mapeada que contiene los CharacterCard
5 | // En la guía de la web tienes los ejemplos de esto: https://www.minicodelab.dev/feed/react-0-parte-2
6 | // Y el video del taller en Youtube para repasar: https://youtu.be/iXZYPN3Nilc
7 | import React from 'react';
8 | import './App.css';
9 | import CharacterCard from './components/CharacterCard';
10 | import { ApiResponse, Character } from './types';
11 |
12 | // Mock = informacion falsa para desarrollar
13 | // const charactersMock: Character[] = [
14 | // {
15 | // id: 1,
16 | // name: 'Rick Sanchez',
17 | // status: 'Alive',
18 | // },
19 | // {
20 | // id: 2,
21 | // name: 'Morty Smith',
22 | // status: 'Alive',
23 | // },
24 | // ];
25 |
26 | // useState & useEffect
27 | const App = () => {
28 | const [characters, setCharacters] = React.useState([]);
29 |
30 | React.useEffect(() => {
31 | // IIFE
32 | (async () => {
33 | const charactersResponse = (await fetch(
34 | 'https://rickandmortyapi.com/api/character/'
35 | ).then((res) => res.json())) as ApiResponse;
36 |
37 | console.log({ charactersResponse });
38 | // Seteo en el state los personajes de la API cuando la request termine
39 | setCharacters(charactersResponse.results);
40 | })();
41 |
42 | // Con el [] useEffect solo se ejecuta cuando el componente ha montado por primera vez
43 | }, []);
44 |
45 | console.log('Pintando componente App');
46 | console.log({ characters });
47 |
48 | return (
49 |
50 |
MiniCodeLab Rick&Morty
51 |
52 | {/* Esto ya no lo usaremos en nuestro código final: */}
53 | {/*
{
55 | setCharacters(charactersMock);
56 | }}
57 | >
58 | Cargar personajes
59 | */}
60 |
61 |
62 | {characters.map((character) => {
63 | return ;
64 | })}
65 |
66 |
67 | );
68 | };
69 |
70 | export default App;
71 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/src/components/CharacterCard.tsx:
--------------------------------------------------------------------------------
1 | import { Character } from '../types';
2 |
3 | type Props = {
4 | character: Character;
5 | };
6 |
7 | const CharacterCard: React.FC = ({ character }) => {
8 | return (
9 |
10 | {character.name}
11 | Status: {character.status}
12 |
13 |
14 |
15 | );
16 | };
17 |
18 | export default CharacterCard;
19 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
15 | * {
16 | box-sizing: border-box;
17 | }
18 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | );
12 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from 'web-vitals';
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/src/types.ts:
--------------------------------------------------------------------------------
1 | export type Character = {
2 | id: number;
3 | name: string;
4 | status: string;
5 | image: string;
6 | };
7 |
8 | export type ApiResponse = {
9 | results: Character[];
10 | };
11 |
--------------------------------------------------------------------------------
/sesion-1-conceptos-basicos/rick-morty-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/README.md:
--------------------------------------------------------------------------------
1 | # MiniCodeLab - Taller de React desde 0 Parte 1
2 |
3 | Repositorio creado en el segundo Taller de React desde Cero de MiniCodeLab, donde hemos visto los Hooks `useState` y `useEffect` en profundidad 🚀.
4 |
5 | Aquí te dejamos el vídeo resubido del taller para ver cómo hemos desarrollado este código: https://youtu.be/5EiAGGHunOA
6 |
7 | Y el artículos de la web donde podrás repasar todo lo que hemos trabajado juntos:
8 |
9 | https://www.minicodelab.dev/feed/react-0-hooks-parte-1
10 |
11 | Gracias por participar en esta comunidad ♥
12 |
13 | ---
14 |
15 | # Ejercicios propuestos
16 |
17 | Como te comentamos en el taller en directo (o en youtube si lo has visto en diferido), te proponemos una serie de ejercicios para que practiques y compartas el resultado con nosotros en Discord. Aquí te dejamos el documento con los enunciados 🧙♂️
18 |
19 | ## Ejercicios UseState y UseEffect
20 |
21 | ¡Hey 👋 MiniCoder! ha llegado el momento de ponernos a prueba, después de todo lo que hemos aprendido estaría chachi hacer algunos ejercicios para comprobar si han calado los conocimientos. ¡Vamos a por ello!
22 |
23 | Recuerda que tenemos un canal de Discord en el que podrás conocer a más personas del mundo de desarrollo, anunciamos los artículos y comentamos los ejercicios 😊 Si te quieres unir revisa nuestros talleres en directo en [Twitch](https://www.twitch.tv/minicodelab) y vídeos en [Youtube](https://www.youtube.com/channel/UCN1SyK4zRHbdIO6HptDoDOA) para ver como acceder.
24 |
25 | ---
26 |
27 | ### Valor del Bitcoin 💶
28 |
29 | Vamos a realizar un conversor de euros a Bitcoin. Como el valor fluctúa tanto podemos decir que un euro son 0.01 Bitcoin. El user introduce un valor en euros y se mostrará el valor del Bitcoin.
30 |
31 | ```tsx
32 | const BitcoinConversor = (): React.FC => {
33 | // Añade los estados que necesites...
34 |
35 | return (
36 |
37 |
Convierte Euros a Bitcoins 💶
38 |
39 | {/* Aquí deberíamos tener un input! */}
40 |
41 | {euro} EUR
42 | {bitcoin} BTC
43 |
44 | );
45 | };
46 | ```
47 |
48 | ---
49 |
50 | ### Select Avenger
51 |
52 | Desarrollar una aplicación sencilla en la que, al pulsar sobre cada uno de los botones representados por el nombre un avenger, debe cargarse la imagen del Avenger. ¡Punto extra si consigues ocultarla y mostrarla con cada click! 🦸♂️
53 |
54 | ```tsx
55 | const myAvenger = {
56 | ironMan:
57 | 'https://www.sideshow.com/storage/product-images/500846U/the-invincible-iron-man_marvel_silo_lg.png',
58 | spiderMan:
59 | 'https://pbs.twimg.com/media/EYbVjfXXgAEe2yG?format=jpg&name=4096x4096',
60 | blackPanther:
61 | 'https://www.lafuerzararuna.com/files/products/avengers-37-alex-ross-black-panther-timeless-var-7eb333d8eb012e32.jpg?width=600&quality=100'
62 | };
63 |
64 | const AvengersPanel = (): React.FC => {
65 | // Deberíamos tener un state más complejo para manejar los botones
66 |
67 | return (
68 | {/* Aquí mapearemos el objeto myAvenger convertido en array */}
69 | );
70 | };
71 | ```
72 |
73 | ---
74 |
75 | ### Cambia el nombre en el Render
76 |
77 | Crea un componente que cuando se monte cambie el valor del state inicial, es decir, tienes un useState con un valor inicial y en el effect debería cambiar por otro que queráis. ¡Recuerda el segundo argumento del effect!
78 |
79 | Ahora añade un input a través del cual al cambiar su valor, cambie el state del nombre. Y añade un useEffect que cambie ese state nuevamente a un nombre en mayúsculas 🧙♂️
80 |
81 | ```tsx
82 | const ChangeName = (): React.FC => {
83 | const [name, setName] = useState('MiniCodeLab');
84 |
85 | // Aquí cambiaremos el nombre en el render inicial, ¿como lo harás?
86 | // useEffect(() => {});
87 |
88 | return (
89 | El nombre es: {name}
90 |
91 | // Y un input que nos permita cambiarlo...
92 | );
93 | };
94 | ```
95 |
96 | ---
97 |
98 | ### Renderizado en el click 🔄
99 |
100 | Crea un componente en el que cada vez que se haga click sobre un botón su state cambie y por lo tanto se lance un repintado de nuestra aplicación. ¡Punto extra si consigues hacer un contador con las veces que lo hemos renderizado!
101 |
102 | ```tsx
103 | const RenderOnClick = (): React.FC => {};
104 | ```
105 |
106 | ---
107 |
108 | ### Filtrado de elementos
109 |
110 | Crea un filtro de personajes de Rick and Morty, para ello debes recuperar los valores de la API y después filtrar cada uno de los personajes (solo en el cliente, no desde la API) en base a un input. ¡Este es el más complejo, ten un poquito de paciencia y prueba a conseguirlo!
111 |
112 | ```tsx
113 | const RickMortyFiler = (): React.FC => {
114 | const [characters, setCharacters] = useState([]);
115 | const [filter, setFilter] = useState('');
116 |
117 | useEffect(() => {
118 | // Pediremos los personajes a la API...
119 | }, []);
120 |
121 | return (
122 |
123 | {}} // Cambiamos el state cuando cambie el input
127 | />
128 |
129 |
Los personajes:
130 | {/* Listamos los personajes... */}
131 |
132 | );
133 | };
134 | ```
135 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "taller-hooks-minicodelab",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.2",
8 | "@testing-library/user-event": "^13.5.0",
9 | "@types/jest": "^27.4.0",
10 | "@types/node": "^16.11.24",
11 | "@types/react": "^17.0.39",
12 | "@types/react-dom": "^17.0.11",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "5.0.0",
16 | "typescript": "^4.5.5",
17 | "web-vitals": "^2.1.4"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-2-usestate-useeffect/taller-twitch/public/favicon.ico
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-2-usestate-useeffect/taller-twitch/public/logo192.png
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-2-usestate-useeffect/taller-twitch/public/logo512.png
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | background-color: #282c34;
4 | color: white;
5 | min-height: 100vh;
6 | }
7 |
8 | h1,
9 | h2,
10 | h3,
11 | h4 {
12 | margin-top: 0;
13 | padding: 0.5rem;
14 | }
15 |
16 | hr {
17 | border: 1px solid #9e9e9e70;
18 | }
19 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | render( );
7 | const linkElement = screen.getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/src/App.tsx:
--------------------------------------------------------------------------------
1 | // import React from 'react'
2 | // Podríamos usar los hooks de esta forma también:
3 | // React.useState
4 | // React.useEffect
5 | import './App.css';
6 | import MiniCodeComplexState from './components/MiniCodeComplexState';
7 | import MiniCodeEffectMount from './components/MiniCodeEffectMount';
8 | import MiniCodeEffectUnmount from './components/MiniCodeEffectUnmount';
9 | import MiniCodeInput from './components/MiniCodeInput';
10 | import MiniCodeStateCallback from './components/MiniCodeStateCallback';
11 |
12 | const App: React.FC = () => {
13 | return (
14 |
15 |
Ejemplos con useState
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Ejemplos con useEffect
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | export default App;
42 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/src/components/MessageComponent.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 |
3 | type Props = {
4 | name: string;
5 | };
6 |
7 | const MessageComponent: React.FC = ({ name }) => {
8 | useEffect(() => {
9 | const interval = setInterval(() => {
10 | console.log('MessageComponent:', name);
11 | }, 1000);
12 |
13 | // Va a escuchar al componente desmontarse "cleanup"
14 | return () => {
15 | console.log('Desmontado!');
16 | clearInterval(interval);
17 | };
18 | }, []);
19 |
20 | return {name} ;
21 | };
22 |
23 | export default MessageComponent;
24 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/src/components/MiniCodeComplexState.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 |
3 | type Avenger = {
4 | name: string;
5 | surname: string;
6 | };
7 |
8 | const MiniCodeComplexState: React.FC = () => {
9 | const [avenger, setAvenger] = useState({
10 | name: 'Thor',
11 | surname: 'Odinson',
12 | });
13 |
14 | const { name, surname } = avenger;
15 |
16 | return (
17 | <>
18 |
19 | Nombre: {name} | Apellido: {surname}
20 |
21 |
22 | Cambiar el nombre:
23 | {
27 | const newName = e.target.value;
28 |
29 | setAvenger({
30 | ...avenger,
31 | name: newName,
32 | });
33 | }}
34 | />
35 |
36 |
37 |
38 | Cambiar el apellido:
39 | {
43 | const newSurname = e.target.value;
44 |
45 | setAvenger({
46 | ...avenger,
47 | surname: newSurname,
48 | });
49 | }}
50 | />
51 | >
52 | );
53 | };
54 |
55 | export default MiniCodeComplexState;
56 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/src/components/MiniCodeEffectMount.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 |
3 | const MiniCodeEffectMount: React.FC = () => {
4 | const [myName, setMyName] = useState('David Bowie');
5 |
6 | // Ocurre siempre cuando el componente se monta
7 | useEffect(() => {
8 | // console.log('El componente se ha montado!');
9 |
10 | // Se lanza tantas veces como escribamos en el input
11 | setTimeout(() => {
12 | console.log('Cambiando a ziggy');
13 | setMyName('Ziggy Stardust');
14 | }, 2000);
15 | // });
16 | }, []);
17 |
18 | // Al pasar el state por param, no hace falta llevarlo al array de deps
19 | function sayHello(name: string): void {
20 | console.log(name);
21 | }
22 |
23 | // También se lanza cuando myName cambie
24 | useEffect(() => {
25 | // console.log('Ha cambiado myName:', myName);
26 |
27 | sayHello(myName);
28 | }, [myName]);
29 |
30 | return (
31 |
32 |
{myName}
33 |
34 | setMyName(e.target.value)}
38 | />
39 |
40 | );
41 | };
42 |
43 | export default MiniCodeEffectMount;
44 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/src/components/MiniCodeEffectUnmount.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import MessageComponent from './MessageComponent';
3 |
4 | const MiniCodeEffectUnmount: React.FC = () => {
5 | const [visible, setVisible] = useState(false);
6 |
7 | return (
8 | <>
9 | {visible ? : null}
10 | Pulsa el botón para montar/desmontar un componente...
11 | setVisible(!visible)}>I'm inevitable
12 | >
13 | );
14 | };
15 |
16 | export default MiniCodeEffectUnmount;
17 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/src/components/MiniCodeInput.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 |
3 | const MiniCodeInput: React.FC = () => {
4 | const [name, setName] = useState('Alberto');
5 |
6 | return (
7 | <>
8 | Mi nombre es {name}
9 | {
12 | setName(e.target.value);
13 |
14 | // Se queda el último valor porque el render no es inmediato!
15 | setName(e.target.value.toUpperCase());
16 |
17 | // Después de dos segundos, manda este setState
18 | setTimeout(() => {
19 | setName(e.target.value.toUpperCase());
20 | }, 2000);
21 | }}
22 | />
23 | >
24 | );
25 | };
26 |
27 | export default MiniCodeInput;
28 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/src/components/MiniCodeStateCallback.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 |
3 | const MiniCodeStateCallback: React.FC = () => {
4 | const [count, setCount] = useState(0);
5 | const [multiplier, setMultiplier] = useState(1);
6 |
7 | return (
8 | <>
9 | Total: {count}
10 |
11 | {
16 | setMultiplier(e.target.valueAsNumber);
17 | }}
18 | />
19 |
20 |
21 |
22 | {
24 | for (let i = 1; i <= multiplier; i++) {
25 | // Con el callback tenemos como argumento el estado anterior cambiado
26 | // inluso antes de que react haga un RERENDER
27 | setCount((prevCount) => {
28 | return prevCount + 1;
29 | });
30 | }
31 | }}
32 | >
33 | Sumar
34 |
35 | >
36 | );
37 | };
38 |
39 | export default MiniCodeStateCallback;
40 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | );
12 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/sesion-2-usestate-useeffect/taller-twitch/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/sesion-3-hooks/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/sesion-3-hooks/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/sesion-3-hooks/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sesion-3-hook-useref",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.3",
8 | "@testing-library/user-event": "^13.5.0",
9 | "@types/jest": "^27.4.0",
10 | "@types/node": "^16.11.25",
11 | "@types/react": "^17.0.39",
12 | "@types/react-dom": "^17.0.11",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "5.0.0",
16 | "typescript": "^4.5.5",
17 | "web-vitals": "^2.1.4"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/sesion-3-hooks/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-3-hooks/public/favicon.ico
--------------------------------------------------------------------------------
/sesion-3-hooks/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/sesion-3-hooks/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-3-hooks/public/logo192.png
--------------------------------------------------------------------------------
/sesion-3-hooks/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-3-hooks/public/logo512.png
--------------------------------------------------------------------------------
/sesion-3-hooks/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/sesion-3-hooks/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/sesion-3-hooks/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | min-height: 100vh;
4 | background-color: #282c34;
5 | font-size: calc(10px + 2vmin);
6 | color: white;
7 | }
8 |
9 | h1,
10 | h2,
11 | h3,
12 | h4 {
13 | margin: 0;
14 | padding: 1rem;
15 | }
16 |
17 | .box {
18 | color: black;
19 | background-color: coral;
20 | }
21 |
22 | .box-modify {
23 | color: white;
24 | background-color: royalblue;
25 | }
26 |
--------------------------------------------------------------------------------
/sesion-3-hooks/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | render( );
7 | const linkElement = screen.getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/sesion-3-hooks/src/App.tsx:
--------------------------------------------------------------------------------
1 | import './App.css';
2 | import UseRefExamples from './components/UseRefExamples';
3 |
4 | const App = () => {
5 | return (
6 |
7 |
8 |
9 | );
10 | };
11 |
12 | export default App;
13 |
--------------------------------------------------------------------------------
/sesion-3-hooks/src/components/MiniCodeInterval.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef, useState } from 'react';
2 |
3 | const MiniCodeInterval = () => {
4 | const [toggle, setToggle] = useState(false);
5 | // Creo un ref para almacenar el intervalo
6 | const intervalRef = useRef(null);
7 |
8 | useEffect(() => {
9 | // Cuando activo el toggle POR PRIMERA VEZ
10 | // y NO TENGO INTERVALO guardado en intervalRef
11 | if (toggle && !intervalRef.current) {
12 | let time = 0;
13 |
14 | // Guardo el intervalo en intervalRef y por tanto,
15 | // ya no entra nunca más en el if
16 | intervalRef.current = setInterval(() => {
17 | time += 1000;
18 |
19 | console.log('Intervalo!', time);
20 | // Cuando time sea 30... puedo hacer una request
21 | }, 1000);
22 | }
23 | }, [toggle]);
24 |
25 | // Solamente LIMPIA el intervalo al desmontar el componente
26 | useEffect(() => {
27 | return () => {
28 | if (intervalRef.current) {
29 | clearInterval(intervalRef.current);
30 | }
31 | };
32 | }, []);
33 |
34 | console.log('Renderizando MiniCodeInterval!');
35 |
36 | return (
37 |
38 |
MiniCodeInterval
39 |
40 | setToggle(!toggle)}>Activar intervalo!
41 |
42 | );
43 | };
44 |
45 | export default MiniCodeInterval;
46 |
--------------------------------------------------------------------------------
/sesion-3-hooks/src/components/MiniCodeTaxCalculator.tsx:
--------------------------------------------------------------------------------
1 | import { useRef, useState } from 'react';
2 |
3 | const MiniCodeTaxCalculator: React.FC = () => {
4 | const grossIncomeRef = useRef(null);
5 | const taxPercentRef = useRef(null);
6 | const [total, setTotal] = useState(0);
7 |
8 | const getNetIncome = (): void => {
9 | const grossIncome = grossIncomeRef.current?.valueAsNumber as number;
10 | const taxPercent = taxPercentRef.current?.valueAsNumber as number;
11 |
12 | const totalCalc = grossIncome - grossIncome * (taxPercent / 100);
13 | console.log('The net total is:', totalCalc);
14 | setTotal(totalCalc);
15 | };
16 |
17 | console.log('Renderizando MiniCodeTaxCalculator');
18 | return (
19 | <>
20 | Calculadora sueldo neto
21 |
22 | Sueldo bruto
23 |
31 |
32 |
33 |
34 | Porcentaje de impuestos
35 |
44 |
45 |
46 |
47 | Obtener sueldo neto
48 |
49 | El total es: {total}
50 | >
51 | );
52 | };
53 |
54 | export default MiniCodeTaxCalculator;
55 |
--------------------------------------------------------------------------------
/sesion-3-hooks/src/components/MiniCodeUseRef.tsx:
--------------------------------------------------------------------------------
1 | import { FormEvent, useEffect, useRef, useState } from 'react';
2 |
3 | const MiniCodeUseRef: React.FC = () => {
4 | const textInputRef = useRef(null);
5 | const [name, setName] = useState('Alberto');
6 | const [count, setCount] = useState(0);
7 |
8 | const handleSubmit = (e: FormEvent) => {
9 | e.preventDefault();
10 |
11 | const nameInputValue = textInputRef.current?.value;
12 |
13 | if (nameInputValue) {
14 | setName(nameInputValue);
15 |
16 | if (textInputRef.current) {
17 | textInputRef.current.value = '';
18 | }
19 | }
20 | };
21 |
22 | const printValue = () => {
23 | const inputValue = textInputRef.current?.value;
24 | if (inputValue) setName(inputValue);
25 |
26 | console.log('Imprime nombre:', inputValue);
27 | };
28 |
29 | console.log('Renderizando componente MiniCodeUseRef');
30 | console.log('La referencia:', textInputRef);
31 |
32 | useEffect(() => {
33 | console.log('Se ha montado el componente, hacemos focus al input:');
34 |
35 | textInputRef.current?.focus();
36 | }, []);
37 |
38 | return (
39 |
40 |
Hola soy {name}
41 |
42 |
56 |
57 | );
58 | };
59 |
60 | export default MiniCodeUseRef;
61 |
--------------------------------------------------------------------------------
/sesion-3-hooks/src/components/MiniCodeUseRefCss .tsx:
--------------------------------------------------------------------------------
1 | import { useRef, useState } from 'react';
2 | import '../App.css';
3 |
4 | const MiniCodeUseRefCss = () => {
5 | const [classColor, setClassColor] = useState('box');
6 | const colorRef = useRef(null);
7 |
8 | const changeColorState = () => {
9 | setClassColor(classColor === 'box' ? 'box-modify' : 'box');
10 | };
11 |
12 | const changeColorRef = () => {
13 | if (colorRef.current) {
14 | const newClass =
15 | colorRef.current.className === 'box' ? 'box-modify' : 'box';
16 |
17 | colorRef.current.className = newClass;
18 | }
19 | };
20 |
21 | console.log('Renderizando MiniCodeUseRefCss');
22 |
23 | return (
24 | <>
25 | I´m in a Box STATE
26 | Modify color REF
27 |
28 |
29 | I´m in a Box REF
30 |
31 | Modify color REF
32 | >
33 | );
34 | };
35 |
36 | export default MiniCodeUseRefCss;
37 |
--------------------------------------------------------------------------------
/sesion-3-hooks/src/components/UseRefExamples.tsx:
--------------------------------------------------------------------------------
1 | import MiniCodeInterval from './MiniCodeInterval';
2 | import MiniCodeTaxCalculator from './MiniCodeTaxCalculator';
3 | import MiniCodeUseRef from './MiniCodeUseRef';
4 | import MiniCodeUseRefCss from './MiniCodeUseRefCss ';
5 |
6 | const UseRefExamples: React.FC = () => {
7 | return (
8 |
9 |
Ejemplos con useRef
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | );
26 | };
27 |
28 | export default UseRefExamples;
29 |
--------------------------------------------------------------------------------
/sesion-3-hooks/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/sesion-3-hooks/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | );
12 |
--------------------------------------------------------------------------------
/sesion-3-hooks/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/sesion-3-hooks/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from 'web-vitals';
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/sesion-3-hooks/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/sesion-3-hooks/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sesion-4-hooks-usememo-usecallback",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.3",
8 | "@testing-library/user-event": "^13.5.0",
9 | "@types/jest": "^27.4.1",
10 | "@types/node": "^16.11.26",
11 | "@types/react": "^17.0.39",
12 | "@types/react-dom": "^17.0.11",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "5.0.0",
16 | "typescript": "^4.5.5",
17 | "web-vitals": "^2.1.4"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-4-hooks-usememo-usecallback/public/favicon.ico
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-4-hooks-usememo-usecallback/public/logo192.png
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-4-hooks-usememo-usecallback/public/logo512.png
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | padding: 2rem;
4 | }
5 |
6 | .tab-manager {
7 | max-width: 100%;
8 | }
9 |
10 | .tab-header {
11 | display: flex;
12 | align-items: center;
13 | justify-content: center;
14 | margin-bottom: 2rem;
15 | padding: 1rem;
16 | gap: 1rem;
17 | border-bottom: 1px solid #94a1b2;
18 | }
19 |
20 | .tab-header > button {
21 | background: #7f5af0;
22 | color: black;
23 | border: 2px solid black;
24 | border-radius: 3px;
25 | font-size: 1rem;
26 | padding: 0.5rem;
27 | font-weight: bold;
28 | cursor: pointer;
29 | }
30 |
31 | .tab-header .active {
32 | background: #2cb67d;
33 | }
34 |
35 | .rows {
36 | display: flex;
37 | align-items: center;
38 | justify-content: center;
39 | gap: 2rem;
40 | }
41 |
42 | ul {
43 | list-style-type: none;
44 | }
45 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | render( );
7 | const linkElement = screen.getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/App.tsx:
--------------------------------------------------------------------------------
1 | import './App.css';
2 | import MiniCodeTabManager from './components/MiniCodeTabManager';
3 | import MiniCodeUseCallback from './components/MiniCodeUseCallback';
4 | import MiniCodeUseMemo from './components/MiniCodeUseMemo';
5 |
6 | function App() {
7 | return (
8 |
9 |
MiniCodeLab - [Hooks useMemo & useCallback]
10 |
11 |
26 |
27 | );
28 | }
29 |
30 | export default App;
31 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/components/BasicUseCallback.tsx:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useMemo, useState } from 'react';
2 |
3 | const BasicUseCallback: React.FC = () => {
4 | const [count, setCount] = useState(0);
5 |
6 | // useCallback almacena la función que definimos siempre que cambie el contenido
7 | // del array de dependencias
8 | const resetCount = useCallback(() => {
9 | console.log('Count vale', count);
10 |
11 | setCount(0);
12 | }, []);
13 |
14 | // const resetCountNoCb = () => setCount(0);
15 |
16 | useEffect(() => {
17 | console.log('Lanzando el efecto!');
18 | }, [resetCount]);
19 | // }, [resetCountNoCb]);
20 |
21 | return (
22 |
23 |
Cuenta useCallback
24 |
25 |
La cuenta es: {count}
26 |
27 |
setCount((prevCount) => prevCount + 1)}>
28 | Sumar
29 |
30 |
setCount((prevCount) => prevCount - 1)}>
31 | Restar
32 |
33 |
34 |
Resetear
35 |
36 | );
37 | };
38 |
39 | export default BasicUseCallback;
40 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/components/BasicUseMemo.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo, useState } from 'react';
2 |
3 | const numbersArray = [1, 2, 1, 4, 0, 6];
4 |
5 | const mapScores = (scores: number[], caller: string) => {
6 | console.log('Invocamos mapScores =>', caller);
7 |
8 | return scores.map((num, index) => {
9 | const calc = (num * 3) / 2;
10 | const color = calc < 3 ? '🔴' : '🟢';
11 |
12 | return (
13 |
14 | {calc} {color}
15 |
16 | );
17 | });
18 | };
19 |
20 | const BasicUseMemo: React.FC = () => {
21 | const [rerender, setRerender] = useState(false);
22 |
23 | const marksContent = mapScores(numbersArray, 'no-memo');
24 |
25 | const marksContentMemo = useMemo(() => {
26 | return mapScores(numbersArray, 'memo');
27 | }, []);
28 |
29 | return (
30 |
31 |
32 |
33 |
No memo
34 | {marksContent}
35 |
36 |
37 |
38 |
Usando memo
39 | {marksContentMemo}
40 |
41 |
42 |
43 |
setRerender(!rerender)}>Rerender
44 |
45 | );
46 | };
47 |
48 | export default BasicUseMemo;
49 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/components/MiniCodePosts.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo, useState } from 'react';
2 |
3 | const posts = [
4 | {
5 | slug: 'vue-desde-0',
6 | date: 'Fri Oct 06 2023 10:45:00 GMT+0200 (Central European Summer Time)',
7 | },
8 | {
9 | slug: 'react-desde-0',
10 | date: 'Thu Feb 17 2022 18:15:00 GMT+0100 (Central European Standard Time)',
11 | },
12 | {
13 | slug: 'angular-desde-0',
14 | date: 'Tue Aug 23 2022 13:21:00 GMT+0200 (Central European Summer Time)',
15 | },
16 | ];
17 |
18 | export const MiniCodePosts: React.FC = () => {
19 | const [rerender, setRerender] = useState(false);
20 |
21 | // Menuda locura de cálculos! 🤯
22 | const orderedPostsWithTitle = useMemo(() => {
23 | console.log('Calculando los posts con useMemo');
24 |
25 | return posts
26 | .map((post) => {
27 | return {
28 | ...post,
29 | date: new Date(post.date),
30 | title: post.slug.split('-').join(' ').toUpperCase(),
31 | };
32 | })
33 | .sort((a, b) => a.date.getTime() - b.date.getTime())
34 | .map((post) => ({
35 | ...post,
36 | date: new Intl.DateTimeFormat('es-ES').format(post.date),
37 | }));
38 | }, []);
39 |
40 | return (
41 |
42 |
Talleres destacados 😍
43 |
44 |
45 | {orderedPostsWithTitle.map((post) => (
46 |
47 | {post.title} {post.date}
48 |
49 |
50 | ))}
51 |
52 |
53 |
setRerender(!rerender)}>Rerender
54 |
55 | );
56 | };
57 |
58 | export default MiniCodePosts;
59 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/components/MiniCodePostsAsync.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useMemo, useState } from 'react';
2 |
3 | type Post = {
4 | slug: string;
5 | date: string;
6 | title?: string;
7 | };
8 |
9 | const posts = [
10 | {
11 | slug: 'vue-desde-0',
12 | date: 'Fri Oct 06 2023 10:45:00 GMT+0200 (Central European Summer Time)',
13 | },
14 | {
15 | slug: 'react-desde-0',
16 | date: 'Thu Feb 17 2022 18:15:00 GMT+0100 (Central European Standard Time)',
17 | },
18 | {
19 | slug: 'angular-desde-0',
20 | date: 'Tue Aug 23 2022 13:21:00 GMT+0200 (Central European Summer Time)',
21 | },
22 | ];
23 |
24 | // Imagina que esto pide info de una API
25 | const fetchPosts = (): Promise => {
26 | return new Promise((resolve) => {
27 | setTimeout(() => {
28 | resolve(posts);
29 | }, 2000);
30 | });
31 | };
32 |
33 | export const MiniCodePostsAsync: React.FC = () => {
34 | const [allPosts, setAllPosts] = useState([]);
35 | const [rerender, setRerender] = useState(false);
36 |
37 | useEffect(() => {
38 | // Simulamos que pedimos info a una API
39 | fetchPosts().then((postsResponse) => {
40 | console.log('Formateando posts');
41 | const formattedPosts = postsResponse
42 | .map((post) => {
43 | return {
44 | ...post,
45 | date: new Date(post.date),
46 | title: post.slug.split('-').join(' ').toUpperCase(),
47 | };
48 | })
49 | .sort((a, b) => a.date.getTime() - b.date.getTime())
50 | .map((post) => ({
51 | ...post,
52 | date: new Intl.DateTimeFormat('es-ES').format(post.date),
53 | }));
54 |
55 | setAllPosts(formattedPosts);
56 | });
57 | }, []);
58 |
59 | // Menuda locura de cálculos! 🤯
60 | // const orderedPostsWithTitle = useMemo(() => {
61 | // console.log('Calculando los posts con useMemo');
62 |
63 | // return allPosts
64 | // .map((post) => {
65 | // return {
66 | // ...post,
67 | // date: new Date(post.date),
68 | // title: post.slug.split('-').join(' ').toUpperCase(),
69 | // };
70 | // })
71 | // .sort((a, b) => a.date.getTime() - b.date.getTime())
72 | // .map((post) => ({
73 | // ...post,
74 | // date: new Intl.DateTimeFormat('es-ES').format(post.date),
75 | // }));
76 | // }, [allPosts]);
77 |
78 | return (
79 |
80 |
Talleres destacados 😍
81 |
82 |
83 | {allPosts.map((post) => (
84 |
85 | {post.title} {post.date}
86 |
87 |
88 | ))}
89 |
90 |
91 |
setRerender(!rerender)}>Rerender
92 |
93 | );
94 | };
95 |
96 | export default MiniCodePostsAsync;
97 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/components/MiniCodeTabManager.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | const MiniCodeTabManager: React.FC = ({
4 | tabs,
5 | defaultTab,
6 | withUnset = false,
7 | }) => {
8 | const [currentTab, setCurrentTab] = useState(
9 | defaultTab || null
10 | );
11 |
12 | const TabToShow = tabs.find(({ slug }) => slug === currentTab)?.content;
13 |
14 | return (
15 |
16 |
17 | {tabs.map((tab) => (
18 | setCurrentTab(tab.slug)}
22 | >
23 | {tab.title}
24 |
25 | ))}
26 |
27 | {withUnset ? (
28 | setCurrentTab(null)}
31 | >
32 | Eliminar
33 |
34 | ) : null}
35 |
36 |
37 |
{TabToShow ? : null}
38 |
39 | );
40 | };
41 |
42 | export type Props = {
43 | defaultTab?: string;
44 | tabs: {
45 | content: React.FC;
46 | slug: string;
47 | title: string;
48 | }[];
49 | withUnset?: boolean;
50 | };
51 |
52 | export default MiniCodeTabManager;
53 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/components/MiniCodeUseCallback.tsx:
--------------------------------------------------------------------------------
1 | import BasicUseCallback from './BasicUseCallback';
2 | import BasicUseMemo from './BasicUseMemo';
3 | import MiniCodePosts from './MiniCodePosts';
4 | import MiniCodePostsAsync from './MiniCodePostsAsync';
5 | import MiniCodeTabManager from './MiniCodeTabManager';
6 |
7 | function MiniCodeUseCallback() {
8 | return (
9 |
10 |
Ejemplos de uso - useMemo
11 |
12 |
21 |
22 | );
23 | }
24 |
25 | export default MiniCodeUseCallback;
26 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/components/MiniCodeUseMemo.tsx:
--------------------------------------------------------------------------------
1 | import BasicUseMemo from './BasicUseMemo';
2 | import MiniCodePosts from './MiniCodePosts';
3 | import MiniCodePostsAsync from './MiniCodePostsAsync';
4 | import MiniCodeTabManager from './MiniCodeTabManager';
5 |
6 | function MiniCodeUseMemo() {
7 | return (
8 |
9 |
Ejemplos de uso - useMemo
10 |
11 |
30 |
31 | );
32 | }
33 |
34 | export default MiniCodeUseMemo;
35 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/index.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
2 |
3 | body {
4 | -moz-osx-font-smoothing: grayscale;
5 | -webkit-font-smoothing: antialiased;
6 | background-color: #16161a;
7 | font-family: 'Roboto', sans-serif;
8 | margin: 0;
9 | min-height: 100vh;
10 | color: white;
11 | }
12 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from 'web-vitals';
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/sesion-4-hooks-usememo-usecallback/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sesion-5-custom-hooks",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.3",
8 | "@testing-library/user-event": "^13.5.0",
9 | "@types/jest": "^27.4.1",
10 | "@types/node": "^16.11.26",
11 | "@types/react": "^17.0.39",
12 | "@types/react-dom": "^17.0.13",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "5.0.0",
16 | "typescript": "^4.6.2",
17 | "web-vitals": "^2.1.4"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-5-custom-hooks/public/favicon.ico
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-5-custom-hooks/public/logo192.png
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-5-custom-hooks/public/logo512.png
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | padding: 2rem;
4 | }
5 |
6 | ul {
7 | list-style-type: none;
8 | }
9 |
10 | .tab-manager {
11 | max-width: 100%;
12 | }
13 |
14 | .tab-header {
15 | display: flex;
16 | align-items: center;
17 | justify-content: center;
18 | margin-bottom: 2rem;
19 | padding: 1rem;
20 | gap: 1rem;
21 | border-bottom: 1px solid #94a1b2;
22 | }
23 |
24 | .tab-header > button {
25 | background: #7f5af0;
26 | color: black;
27 | border: 2px solid black;
28 | border-radius: 3px;
29 | font-size: 1rem;
30 | padding: 0.5rem;
31 | font-weight: bold;
32 | cursor: pointer;
33 | }
34 |
35 | .tab-header .active {
36 | background: #2cb67d;
37 | }
38 |
39 | .rows {
40 | display: flex;
41 | align-items: center;
42 | justify-content: center;
43 | gap: 2rem;
44 | }
45 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | render( );
7 | const linkElement = screen.getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/src/App.tsx:
--------------------------------------------------------------------------------
1 | import './App.css';
2 | import MiniCodeCounter from './components/MiniCodeCounter';
3 | import MiniCodeDebounce from './components/MiniCodeDebounce';
4 | import MiniCodeTabManager from './components/MiniCodeTabManager';
5 |
6 | function App() {
7 | return (
8 |
9 |
24 |
25 | );
26 | }
27 |
28 | export default App;
29 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/src/components/MiniCodeCounter.tsx:
--------------------------------------------------------------------------------
1 | import useCount from '../hooks/useCount';
2 | import ThirdCount from './ThirdCount';
3 |
4 | const MiniCodeCounter = () => {
5 | const firstCount = useCount();
6 | const secondCount = useCount();
7 |
8 | return (
9 | <>
10 |
11 |
Contador 1 🚴♀️
12 |
13 |
14 |
El contador 1 vale {firstCount.count}
15 |
16 | Sumar
17 |
18 | Restar
19 |
20 |
21 |
22 |
23 |
Contador 2 🚴
24 |
25 |
26 |
El contador 2 vale {secondCount.count}
27 |
28 | Sumar
29 |
30 | Restar
31 |
32 |
33 |
34 | {firstCount.count > 5 ? : null}
35 | >
36 | );
37 | };
38 |
39 | export default MiniCodeCounter;
40 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/src/components/MiniCodeDebounce.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import useDebounceString from '../hooks/useDebounceString';
3 |
4 | const getStartWarsMovies = async (title: string) => {
5 | console.log('Pidiendo las pelis a la API con título', title);
6 |
7 | const response = await new Promise((resolve) => {
8 | setTimeout(() => {
9 | if (title.includes('imperio')) {
10 | resolve([{ title: 'El imperio contraataca' }]);
11 | } else {
12 | resolve([]);
13 | }
14 | }, 1000);
15 | });
16 |
17 | return response;
18 | };
19 |
20 | const MiniCodeDebounce = () => {
21 | const [filter, setFilter] = useState('');
22 | const debouncedFilter = useDebounceString(filter, 250);
23 |
24 | console.log({ filter, debouncedFilter });
25 |
26 | useEffect(() => {
27 | if (debouncedFilter) {
28 | getStartWarsMovies(debouncedFilter).then((movies) => {
29 | console.log(movies);
30 | });
31 | }
32 | }, [debouncedFilter]);
33 |
34 | return (
35 |
36 |
Star Wars API
37 |
38 |
39 | setFilter(e.target.value)}
43 | />
44 |
45 |
46 | );
47 | };
48 |
49 | export default MiniCodeDebounce;
50 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/src/components/MiniCodeTabManager.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | const MiniCodeTabManager = ({ tabs, defaultTab, withUnset = false }: Props) => {
4 | const [currentTab, setCurrentTab] = useState(
5 | defaultTab || null
6 | );
7 |
8 | const TabToShow = tabs.find(({ slug }) => slug === currentTab)?.content;
9 |
10 | return (
11 |
12 |
13 | {tabs.map((tab) => (
14 | setCurrentTab(tab.slug)}
18 | >
19 | {tab.title}
20 |
21 | ))}
22 |
23 | {withUnset ? (
24 | setCurrentTab(null)}
27 | >
28 | Eliminar
29 |
30 | ) : null}
31 |
32 |
33 |
{TabToShow ? : null}
34 |
35 | );
36 | };
37 |
38 | export type Props = {
39 | defaultTab?: string;
40 | tabs: {
41 | content: React.FC;
42 | slug: string;
43 | title: string;
44 | }[];
45 | withUnset?: boolean;
46 | };
47 |
48 | export default MiniCodeTabManager;
49 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/src/components/ThirdCount.tsx:
--------------------------------------------------------------------------------
1 | import useCount from '../hooks/useCount';
2 |
3 | const ThirdCount = () => {
4 | const { count, add, substract } = useCount();
5 |
6 | return (
7 |
8 |
Contador 3 🚀
9 |
10 |
11 |
El contador 3 vale {count}
12 |
13 | Sumar
14 |
15 | Restar
16 |
17 |
18 | );
19 | };
20 |
21 | export default ThirdCount;
22 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/src/hooks/useCount.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useState } from 'react';
2 |
3 | type UseCountReturn = {
4 | count: number;
5 | add: () => void;
6 | substract: () => void;
7 | };
8 |
9 | function useCount(): UseCountReturn {
10 | const [count, setCount] = useState(0);
11 |
12 | const add = useCallback(() => setCount((prevCount) => prevCount + 1), []);
13 | const substract = useCallback(
14 | () => setCount((prevCount) => prevCount - 1),
15 | []
16 | );
17 |
18 | return { count, add, substract };
19 | }
20 |
21 | export default useCount;
22 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/src/hooks/useDebounceString.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 |
3 | function useDebounceString(value: string, timeout = 500): string {
4 | const [debouncedValue, setDebouncedValue] = useState(value || '');
5 |
6 | useEffect(() => {
7 | const timeoutId = setTimeout(() => {
8 | setDebouncedValue(value);
9 | }, timeout);
10 |
11 | return () => {
12 | clearTimeout(timeoutId);
13 | };
14 | }, [timeout, value]);
15 |
16 | return debouncedValue;
17 | }
18 |
19 | export default useDebounceString;
20 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/src/index.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
2 |
3 | body {
4 | -moz-osx-font-smoothing: grayscale;
5 | -webkit-font-smoothing: antialiased;
6 | background-color: #16161a;
7 | font-family: 'Roboto', sans-serif;
8 | margin: 0;
9 | min-height: 100vh;
10 | color: white;
11 | }
12 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from 'web-vitals';
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/sesion-5-custom-hooks/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/sesion-6-react-router/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/sesion-6-react-router/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/sesion-6-react-router/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sesion-6-react-router",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.4",
8 | "@testing-library/user-event": "^13.5.0",
9 | "@types/jest": "^27.4.1",
10 | "@types/node": "^16.11.26",
11 | "@types/react": "^17.0.40",
12 | "@types/react-dom": "^17.0.13",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-router-dom": "^6.2.2",
16 | "react-scripts": "5.0.0",
17 | "typescript": "^4.6.2",
18 | "web-vitals": "^2.1.4"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/sesion-6-react-router/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-6-react-router/public/favicon.ico
--------------------------------------------------------------------------------
/sesion-6-react-router/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/sesion-6-react-router/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-6-react-router/public/logo192.png
--------------------------------------------------------------------------------
/sesion-6-react-router/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-6-react-router/public/logo512.png
--------------------------------------------------------------------------------
/sesion-6-react-router/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/sesion-6-react-router/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/sesion-6-react-router/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | padding: 2rem;
4 | }
5 |
6 | ul {
7 | list-style-type: none;
8 | }
9 |
10 | .hero-links {
11 | display: flex;
12 | gap: 1rem;
13 | justify-content: center;
14 | }
15 |
16 | .nav {
17 | border-bottom: 1px solid #94a1b2;
18 | display: flex;
19 | gap: 1rem;
20 | justify-content: center;
21 | padding-bottom: 1rem;
22 | }
23 |
--------------------------------------------------------------------------------
/sesion-6-react-router/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { NavLink, Outlet } from 'react-router-dom';
2 | import './App.css';
3 |
4 | function App() {
5 | return (
6 |
7 |
MiniCodeLab - React Router
8 |
9 |
10 | Ir a Home
11 | Ver héroes
12 |
13 |
14 |
15 | {/* Renderiza las rutas hijas escogidas por Routes */}
16 |
17 |
18 |
19 | );
20 | }
21 |
22 | export default App;
23 |
--------------------------------------------------------------------------------
/sesion-6-react-router/src/data/heroes.ts:
--------------------------------------------------------------------------------
1 | export type Hero = {
2 | id: number;
3 | name: string;
4 | age: number;
5 | alias: string;
6 | slug: string;
7 | };
8 |
9 | export let heroes: Hero[] = [
10 | {
11 | id: 1,
12 | name: 'Superman',
13 | age: 65,
14 | alias: 'Clark Kent',
15 | slug: 'superman',
16 | },
17 | {
18 | id: 2,
19 | name: 'Batman',
20 | age: 55,
21 | slug: 'batman',
22 | alias: 'Bruce Wayne',
23 | },
24 | {
25 | id: 3,
26 | name: 'Wonder Woman',
27 | age: 1555,
28 | alias: 'Diana',
29 | slug: 'wonder-woman',
30 | },
31 | ];
32 |
33 | export const getHeroes = () => heroes;
34 |
35 | export const getHeroBySlug = async (
36 | slug: string
37 | ): Promise => {
38 | return heroes.find((hero) => hero.slug === slug);
39 | };
40 |
41 | export const deleteHeroById = async (id: number): Promise => {
42 | heroes = heroes.filter((hero) => hero.id !== id);
43 | };
44 |
--------------------------------------------------------------------------------
/sesion-6-react-router/src/index.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
2 |
3 | body {
4 | -moz-osx-font-smoothing: grayscale;
5 | -webkit-font-smoothing: antialiased;
6 | background-color: #16161a;
7 | font-family: 'Roboto', sans-serif;
8 | margin: 0;
9 | min-height: 100vh;
10 | color: white;
11 | }
12 |
13 | :root {
14 | --black: #000;
15 | --green: #2cb67d;
16 | --grey: #94a1b2;
17 | --purple: #7f5af0;
18 | --red: #a31621;
19 | }
20 |
21 | a {
22 | color: white;
23 | }
24 | a:visited {
25 | color: var(--purple);
26 | }
27 | a:active,
28 | a.active {
29 | color: var(--green);
30 | }
31 |
32 | h1 {
33 | margin-top: 0;
34 | }
35 |
--------------------------------------------------------------------------------
/sesion-6-react-router/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { BrowserRouter, Route, Routes } from 'react-router-dom';
4 | import './index.css';
5 | import App from './App';
6 | import Home from './pages/Home';
7 | import Heroes from './pages/Heroes';
8 | import Hero from './pages/Hero';
9 |
10 | ReactDOM.render(
11 |
12 |
13 |
14 | }>
15 | } />
16 |
17 | }>
18 | } />
19 |
20 |
21 |
22 |
23 | ,
24 | document.getElementById('root')
25 | );
26 |
--------------------------------------------------------------------------------
/sesion-6-react-router/src/pages/Hero.tsx:
--------------------------------------------------------------------------------
1 | // http://localhost:3000/heroes/superman
2 | // Un parámetro dinámico de la URL se llama URL PARAM
3 | // http://localhost:3000/heroes/:heroSlug
4 |
5 | import { useEffect, useState } from 'react';
6 | import { useNavigate, useParams } from 'react-router-dom';
7 | import { deleteHeroById, getHeroBySlug, Hero } from '../data/heroes';
8 |
9 | const HeroPage = () => {
10 | const navigate = useNavigate();
11 | const { heroSlug } = useParams();
12 |
13 | const [hero, setHero] = useState(null);
14 |
15 | useEffect(() => {
16 | if (heroSlug) {
17 | getHeroBySlug(heroSlug).then((hero) => {
18 | if (hero) {
19 | setHero(hero);
20 | }
21 | });
22 | }
23 | }, [heroSlug]);
24 |
25 | const handleDeleteHero = (id: number) => {
26 | deleteHeroById(id).then(() => {
27 | navigate('/');
28 | });
29 | };
30 |
31 | return (
32 | <>
33 | Hero Profile ✨
34 |
35 | {hero ? (
36 |
37 |
38 | {hero.name} - {hero.age} años
39 |
40 |
handleDeleteHero(hero.id)}>
41 | Borrar super
42 |
43 |
44 | ) : (
45 | Este héroe no está disponible 🚔
46 | )}
47 | >
48 | );
49 | };
50 |
51 | export default HeroPage;
52 |
--------------------------------------------------------------------------------
/sesion-6-react-router/src/pages/Heroes.tsx:
--------------------------------------------------------------------------------
1 | import { NavLink, Outlet } from 'react-router-dom';
2 | import { heroes } from '../data/heroes';
3 |
4 | const Heroes = () => {
5 | return (
6 | <>
7 | Heroes Page 🦸
8 |
9 |
10 | {heroes.map((hero) => (
11 |
12 | {hero.name}
13 |
14 | ))}
15 |
16 |
17 |
18 |
19 |
20 | >
21 | );
22 | };
23 |
24 | export default Heroes;
25 |
--------------------------------------------------------------------------------
/sesion-6-react-router/src/pages/Home.tsx:
--------------------------------------------------------------------------------
1 | const Home = () => {
2 | return Home Page 🏠 ;
3 | };
4 |
5 | export default Home;
6 |
--------------------------------------------------------------------------------
/sesion-6-react-router/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/sesion-6-react-router/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/sesion-7-context/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/sesion-7-context/README.md:
--------------------------------------------------------------------------------
1 | # React Context & useContext - MiniCodeLab
2 |
3 |
4 |
--------------------------------------------------------------------------------
/sesion-7-context/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sesion-7-context",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.4",
8 | "@testing-library/user-event": "^13.5.0",
9 | "@types/jest": "^27.4.1",
10 | "@types/node": "^16.11.26",
11 | "@types/react": "^17.0.40",
12 | "@types/react-dom": "^17.0.13",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-router-dom": "^6.2.2",
16 | "react-scripts": "5.0.0",
17 | "typescript": "^4.6.2",
18 | "web-vitals": "^2.1.4"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/sesion-7-context/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-7-context/public/favicon.ico
--------------------------------------------------------------------------------
/sesion-7-context/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | MiniCodeLab - useContext
12 |
13 |
14 | You need to enable JavaScript to run this app.
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/sesion-7-context/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-7-context/public/logo192.png
--------------------------------------------------------------------------------
/sesion-7-context/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-7-context/public/logo512.png
--------------------------------------------------------------------------------
/sesion-7-context/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/sesion-7-context/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/sesion-7-context/public/schema.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-7-context/public/schema.PNG
--------------------------------------------------------------------------------
/sesion-7-context/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .header {
6 | align-items: center;
7 | display: flex;
8 | justify-content: space-around;
9 | padding: 0.5rem 0;
10 | border-bottom: 1px solid var(--grey);
11 | }
12 |
13 | .header,
14 | .header a {
15 | transition: all 200ms linear;
16 | }
17 |
18 | .header-light {
19 | background-color: whitesmoke;
20 | }
21 |
22 | .header-dark {
23 | background-color: var(--dark);
24 | }
25 |
26 | .header-nav {
27 | align-items: center;
28 | display: flex;
29 | gap: 1rem;
30 | }
31 |
32 | .header-nav-light a:not(.active) {
33 | color: black;
34 | }
35 |
36 | .header-content {
37 | align-items: center;
38 | display: flex;
39 | gap: 1rem;
40 | }
41 |
42 | .theme-button {
43 | background-color: transparent;
44 | border: 0;
45 | cursor: pointer;
46 | font-size: 1.25rem;
47 | }
48 |
49 | .header-search {
50 | }
51 |
--------------------------------------------------------------------------------
/sesion-7-context/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | render( );
7 | const linkElement = screen.getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/sesion-7-context/src/App.tsx:
--------------------------------------------------------------------------------
1 | import './App.css';
2 | import Header from './components/Header';
3 | import { ThemeContextProvider } from './context/ThemeContext';
4 |
5 | const App = () => {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 | );
13 | };
14 |
15 | export default App;
16 |
--------------------------------------------------------------------------------
/sesion-7-context/src/components/Header.tsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 | import { Link, NavLink } from 'react-router-dom';
3 | import { ThemeContext } from '../context/ThemeContext';
4 | import ThemeSwitcher from './ThemeSwitcher';
5 |
6 | const Header = () => {
7 | const { theme } = useContext(ThemeContext);
8 |
9 | return (
10 |
33 | );
34 | };
35 |
36 | export default Header;
37 |
--------------------------------------------------------------------------------
/sesion-7-context/src/components/ThemeSwitcher.tsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 | import { ThemeContext } from '../context/ThemeContext';
3 |
4 | const ThemeSwitcher = () => {
5 | // Accedemos a los valores de ThemeContext
6 | const { theme, toggleTheme } = useContext(ThemeContext);
7 |
8 | return (
9 |
10 | {theme === 'dark' ? '🌞' : '🌚'}
11 |
12 | );
13 | };
14 |
15 | export default ThemeSwitcher;
16 |
--------------------------------------------------------------------------------
/sesion-7-context/src/context/ThemeContext.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | createContext,
3 | useCallback,
4 | useEffect,
5 | useLayoutEffect,
6 | useState,
7 | } from 'react';
8 |
9 | export type ThemeContextType = {
10 | theme: 'light' | 'dark';
11 | toggleTheme: () => void;
12 | };
13 |
14 | export const ThemeContext = createContext({
15 | theme: 'dark',
16 | toggleTheme: () => null,
17 | });
18 |
19 | export const ThemeContextProvider = ({
20 | children,
21 | }: {
22 | children: React.ReactNode;
23 | }) => {
24 | // Definimos el control que haremos sobre los datos del Provider
25 | const [theme, setTheme] = useState(
26 | // Inicializamos el state de forma lazy para leer localStorage primero
27 | () => (localStorage.getItem('theme') as ThemeContextType['theme']) || 'dark'
28 | );
29 |
30 | const toggleTheme = useCallback(() => {
31 | setTheme((prevTheme) => (prevTheme === 'dark' ? 'light' : 'dark'));
32 | }, []);
33 |
34 | // Cada vez que theme cambie, lo guardo actualizado en localStorage
35 | useEffect(() => {
36 | localStorage.setItem('theme', theme);
37 | }, [theme]);
38 |
39 | return (
40 |
46 | {children}
47 |
48 | );
49 | };
50 |
--------------------------------------------------------------------------------
/sesion-7-context/src/index.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
2 |
3 | :root {
4 | --black: #000;
5 | --green: #2cb67d;
6 | --grey: #94a1b2;
7 | --purple: #7f5af0;
8 | --red: #a31621;
9 | --dark: #16161a;
10 | }
11 |
12 | body {
13 | -moz-osx-font-smoothing: grayscale;
14 | -webkit-font-smoothing: antialiased;
15 | background-color: var(--dark);
16 | color: white;
17 | font-family: 'Roboto', sans-serif;
18 | margin: 0;
19 | min-height: 100vh;
20 | }
21 |
22 | * {
23 | box-sizing: border-box;
24 | }
25 |
26 | a {
27 | color: white;
28 | }
29 | a.active {
30 | color: var(--green);
31 | }
32 |
33 | ul {
34 | list-style-type: none;
35 | padding-left: 0;
36 | }
37 |
--------------------------------------------------------------------------------
/sesion-7-context/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { BrowserRouter } from 'react-router-dom';
4 | import './index.css';
5 | import App from './App';
6 |
7 | ReactDOM.render(
8 |
9 |
10 |
11 |
12 | ,
13 | document.getElementById('root')
14 | );
15 |
--------------------------------------------------------------------------------
/sesion-7-context/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/sesion-7-context/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/sesion-8-forms/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/sesion-8-forms/README.md:
--------------------------------------------------------------------------------
1 | # React Hook Form
2 |
3 | Link a la documentación https://react-hook-form.com/
4 |
--------------------------------------------------------------------------------
/sesion-8-forms/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sesion-8-forms",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.2",
7 | "@testing-library/react": "^12.1.4",
8 | "@testing-library/user-event": "^13.5.0",
9 | "@types/jest": "^27.4.1",
10 | "@types/node": "^16.11.26",
11 | "@types/react": "^17.0.40",
12 | "@types/react-dom": "^17.0.13",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-hook-form": "^7.28.0",
16 | "react-scripts": "5.0.0",
17 | "typescript": "^4.6.2",
18 | "web-vitals": "^2.1.4"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/sesion-8-forms/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-8-forms/public/favicon.ico
--------------------------------------------------------------------------------
/sesion-8-forms/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | MiniCodeLab React Forms
12 |
13 |
14 | You need to enable JavaScript to run this app.
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/sesion-8-forms/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-8-forms/public/logo192.png
--------------------------------------------------------------------------------
/sesion-8-forms/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniCodeLab/taller-react-desde-cero/6816c4d8971f340c1e02bb6010f7ea8f905d584a/sesion-8-forms/public/logo512.png
--------------------------------------------------------------------------------
/sesion-8-forms/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/sesion-8-forms/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/sesion-8-forms/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | form {
6 | }
7 |
8 | fieldset {
9 | border: 0;
10 | display: flex;
11 | align-items: center;
12 | gap: 1rem;
13 | justify-content: center;
14 | }
15 |
16 | .error-message {
17 | color: red;
18 | }
19 |
--------------------------------------------------------------------------------
/sesion-8-forms/src/App.tsx:
--------------------------------------------------------------------------------
1 | import './App.css';
2 | import HookForm from './components/HookForm';
3 | // import HookFormRefactored from './components/HookFormRefactored';
4 | // import RefForm from './components/RefForm';
5 | // import StateForm from './components/StateForm';
6 |
7 | const App = () => {
8 | return (
9 |
10 |
MiniCodeLab - Forms
11 | {/* */}
12 | {/* */}
13 | {/* */}
14 |
15 |
16 | );
17 | };
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/sesion-8-forms/src/components/ErrorMessage.tsx:
--------------------------------------------------------------------------------
1 | export type Props = {
2 | message: string;
3 | };
4 |
5 | export const ErrorMessage = ({ message }: Props) => {
6 | return {message}
;
7 | };
8 |
--------------------------------------------------------------------------------
/sesion-8-forms/src/components/HookForm.tsx:
--------------------------------------------------------------------------------
1 | import { useForm } from 'react-hook-form';
2 | import { Form } from '../types';
3 | import { ErrorMessage } from './ErrorMessage';
4 |
5 | const HookForm = () => {
6 | const {
7 | handleSubmit,
8 | register,
9 | formState: { errors },
10 | } = useForm
84 | );
85 | };
86 |
87 | export default HookForm;
88 |
--------------------------------------------------------------------------------
/sesion-8-forms/src/components/HookFormRefactored.tsx:
--------------------------------------------------------------------------------
1 | import { useForm } from 'react-hook-form';
2 | import { Form } from '../types';
3 | import { Input } from './Input';
4 |
5 | const HookFormRefactored = () => {
6 | const {
7 | handleSubmit,
8 | register,
9 | formState: { errors },
10 | } = useForm
76 | );
77 | };
78 |
79 | export default HookFormRefactored;
80 |
--------------------------------------------------------------------------------
/sesion-8-forms/src/components/Input.tsx:
--------------------------------------------------------------------------------
1 | import { UseFormRegister } from 'react-hook-form';
2 | import { Form } from '../types';
3 | import { ErrorMessage } from './ErrorMessage';
4 |
5 | type Props = {
6 | type: 'text' | 'password';
7 | id: string;
8 | label: string;
9 | name: keyof Form;
10 | register: UseFormRegister