├── 10_MINI_BLOG └── miniblog │ ├── .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.js │ ├── App.test.js │ ├── components │ ├── Footer.js │ ├── Footer.module.css │ ├── Navbar.js │ ├── Navbar.module.css │ ├── PostDetail.js │ └── PostDetail.module.css │ ├── contexts │ └── AuthContext.js │ ├── firebase │ └── config.js │ ├── hooks │ ├── useAuthentication.js │ ├── useDeleteDocument.js │ ├── useFetchDocument.js │ ├── useFetchDocuments.js │ ├── useInsertDocument.js │ ├── useQuery.js │ └── useUpdateDocument.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── pages │ ├── About │ │ ├── About.js │ │ └── About.module.css │ ├── CreatePost │ │ ├── CreatePost.js │ │ └── CreatePost.module.css │ ├── Dashboard │ │ ├── Dashboard.js │ │ └── Dashboard.module.css │ ├── EditPost │ │ ├── EditPost.js │ │ └── EditPost.module.css │ ├── Home │ │ ├── Home.js │ │ └── Home.module.css │ ├── Login │ │ ├── Login.js │ │ └── Login.module.css │ ├── Post │ │ ├── Post.js │ │ └── Post.module.css │ ├── Register │ │ ├── Register.js │ │ └── Register.module.css │ └── Search │ │ ├── Search.js │ │ └── Search.module.css │ ├── reportWebVitals.js │ └── setupTests.js ├── 11_REACT_HOOKS └── reacthooks │ ├── .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.js │ ├── App.test.js │ ├── components │ ├── HookCustom.js │ ├── HookUseCallback.js │ ├── HookUseContext.js │ ├── HookUseEffect.js │ ├── HookUseEffectLayout.js │ ├── HookUseImperativeHandle.js │ ├── HookUseMemo.js │ ├── HookUseReducer.js │ ├── HookUseRef.js │ ├── HookUseState.js │ ├── List.js │ └── SomeComponent.js │ ├── hooks │ └── usePrevious.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── pages │ ├── About.js │ └── Home.js │ ├── reportWebVitals.js │ └── setupTests.js ├── 12_REACTGRAM ├── backend │ ├── .env │ ├── .gitignore │ ├── app.js │ ├── config │ │ └── db.js │ ├── controllers │ │ ├── PhotoController.js │ │ └── UserController.js │ ├── middlewares │ │ ├── authGuard.js │ │ ├── handleValidations.js │ │ ├── imageUpload.js │ │ ├── photoValidations.js │ │ └── userValidations.js │ ├── models │ │ ├── Photo.js │ │ └── User.js │ ├── package-lock.json │ ├── package.json │ ├── routes │ │ ├── PhotoRoutes.js │ │ ├── Router.js │ │ └── UserRoutes.js │ └── uploads │ │ ├── photos │ │ └── .gitkeep │ │ └── users │ │ └── .gitkeep ├── frontend │ ├── .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.js │ │ ├── App.test.js │ │ ├── components │ │ ├── Footer.css │ │ ├── Footer.js │ │ ├── LikeContainer.css │ │ ├── LikeContainer.js │ │ ├── Message.css │ │ ├── Message.js │ │ ├── Navbar.css │ │ ├── Navbar.js │ │ ├── PhotoItem.css │ │ └── PhotoItem.js │ │ ├── hooks │ │ ├── useAuth.js │ │ ├── useQuery.js │ │ └── useResetComponentMessage.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── pages │ │ ├── Auth │ │ │ ├── Auth.css │ │ │ ├── Login.js │ │ │ └── Register.js │ │ ├── EditProfile │ │ │ ├── EditProfile.css │ │ │ └── EditProfile.js │ │ ├── Home │ │ │ ├── Home.css │ │ │ └── Home.js │ │ ├── Photo │ │ │ ├── Photo.css │ │ │ └── Photo.js │ │ ├── Profile │ │ │ ├── Profile.css │ │ │ └── Profile.js │ │ └── Search │ │ │ ├── Search.css │ │ │ └── Search.js │ │ ├── reportWebVitals.js │ │ ├── services │ │ ├── authService.js │ │ ├── photoService.js │ │ └── userService.js │ │ ├── setupTests.js │ │ ├── slices │ │ ├── authSlice.js │ │ ├── photoSlice.js │ │ └── userSlice.js │ │ ├── store.js │ │ └── utils │ │ └── config.js └── images │ ├── f1.jpg │ ├── f2.jpg │ ├── f3.jpg │ ├── f4.jpg │ ├── f5.jpg │ ├── f6.jpg │ ├── f7.jpg │ ├── f8.jpg │ ├── f9.jpg │ ├── u1.jpg │ ├── u2.jpg │ └── u3.jpg ├── 13_CURSO_GRATUITO └── todo │ ├── .gitignore │ ├── README.md │ ├── data │ └── db.json │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ └── setupTests.js ├── 2_FUNDAMENTOS └── fundamentos │ ├── .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.js │ ├── App.test.js │ ├── components │ ├── Challenge.js │ ├── Events.js │ ├── FirstComponent.js │ ├── MyComponent.js │ └── TemplateExpressions.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ └── setupTests.js ├── 3_AVANCANDO_NO_REACT └── avancando │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── img1.jpg │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── assets │ └── city.jpg │ ├── components │ ├── CarDetails.js │ ├── ChangeMessageState.js │ ├── ConditionalRender.js │ ├── Container.js │ ├── ExecuteFunction.js │ ├── Fragment.js │ ├── ListRender.js │ ├── ManageData.js │ ├── MessageState.js │ └── ShowUserName.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ └── setupTests.js ├── 4_CSS_REACT ├── challengecss │ ├── .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.js │ │ ├── App.test.js │ │ ├── components │ │ ├── Car.js │ │ └── Car.module.css │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── reportWebVitals.js │ │ └── setupTests.js └── css │ ├── .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.js │ ├── App.test.js │ ├── components │ ├── MyComponent.css │ ├── MyComponent.js │ ├── Title.js │ └── Title.module.css │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ └── setupTests.js ├── 5_FORM_EM_REACT └── 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.js │ ├── App.test.js │ ├── components │ ├── MyForm.css │ └── MyForm.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ └── setupTests.js ├── 6_PROJETO_SECRET_WORD └── secretword │ ├── .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.js │ ├── App.test.js │ ├── components │ ├── Game.css │ ├── Game.js │ ├── GameOver.css │ ├── GameOver.js │ ├── StartScreen.css │ └── StartScreen.js │ ├── data │ └── words.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ └── setupTests.js ├── 7_REQ_HTTP_REACT └── httpreact │ ├── .gitignore │ ├── README.md │ ├── data │ ├── db.json │ └── db.json.backup │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── hooks │ └── useFetch.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reportWebVitals.js │ └── setupTests.js ├── 8_REACT_ROUTER └── reactrouter │ ├── .gitignore │ ├── README.md │ ├── data │ ├── db.json │ └── db.json.backup │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── components │ ├── Navbar.css │ ├── Navbar.js │ └── SearchForm.js │ ├── hooks │ └── useFetch.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── pages │ ├── About.js │ ├── Home.css │ ├── Home.js │ ├── Info.js │ ├── NotFound.js │ ├── Product.js │ └── Search.js │ ├── reportWebVitals.js │ └── setupTests.js └── 9_CONTEXT └── context ├── .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.js ├── App.test.js ├── components ├── ChangeCounter.js ├── Navbar.css └── Navbar.js ├── context ├── CounterContext.js └── TitleColorContext.js ├── hooks ├── useCounterContext.js └── useTitleColorContext.js ├── index.css ├── index.js ├── logo.svg ├── pages ├── About.js ├── Home.css ├── Home.js └── Products.js ├── reportWebVitals.js └── setupTests.js /10_MINI_BLOG/miniblog/.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 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniblog", 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 | "firebase": "^9.6.7", 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2", 12 | "react-router-dom": "^6.2.1", 13 | "react-scripts": "5.0.0", 14 | "web-vitals": "^2.1.4" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": [ 24 | "react-app", 25 | "react-app/jest" 26 | ] 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/10_MINI_BLOG/miniblog/public/favicon.ico -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/10_MINI_BLOG/miniblog/public/logo192.png -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/10_MINI_BLOG/miniblog/public/logo512.png -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/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 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/App.css: -------------------------------------------------------------------------------- 1 | .container { 2 | min-height: 60vh; 3 | margin-bottom: 5em; 4 | } 5 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import styles from "./Footer.module.css"; 2 | 3 | const Footer = () => { 4 | return ( 5 | 9 | ); 10 | }; 11 | 12 | export default Footer; 13 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/components/Footer.module.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | height: 250px; 3 | display: flex; 4 | flex-direction: column; 5 | align-items: center; 6 | justify-content: center; 7 | background-color: #edf3f6; 8 | } 9 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/components/Navbar.module.css: -------------------------------------------------------------------------------- 1 | .navbar { 2 | display: flex; 3 | box-shadow: rgba(0, 0, 0, 0.15) 0px -2px 10px 0px; 4 | justify-content: space-between; 5 | align-items: center; 6 | padding: 0.5em 2em; 7 | } 8 | 9 | .brand { 10 | font-size: 1.2em; 11 | } 12 | 13 | .brand span { 14 | font-weight: 900; 15 | text-transform: uppercase; 16 | } 17 | 18 | .links_list { 19 | display: flex; 20 | list-style: none; 21 | } 22 | 23 | .links_list li { 24 | margin-right: 1em; 25 | } 26 | 27 | .links_list li a { 28 | padding: 0.4em 0.6em; 29 | } 30 | 31 | .active { 32 | background-color: #000; 33 | color: #fff; 34 | } 35 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/components/PostDetail.js: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom"; 2 | 3 | import styles from "./PostDetail.module.css"; 4 | 5 | const PostDetail = ({ post }) => { 6 | return ( 7 |
8 | {post.title} 9 |

{post.title}

10 |

por: {post.createdBy}

11 |
12 | {post.tags.map((tag) => ( 13 |

14 | # 15 | {tag} 16 |

17 | ))} 18 |
19 | 20 | Ler 21 | 22 |
23 | ); 24 | }; 25 | 26 | export default PostDetail; 27 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/components/PostDetail.module.css: -------------------------------------------------------------------------------- 1 | .post_detail { 2 | margin-bottom: 2em; 3 | } 4 | 5 | .post_detail img { 6 | max-width: 600px; 7 | } 8 | 9 | .post_detail h2 { 10 | margin-bottom: 0.4em; 11 | } 12 | 13 | .post_detail .tags { 14 | margin-bottom: 1.2em; 15 | display: flex; 16 | } 17 | 18 | .tags p { 19 | margin-right: 0.5em; 20 | } 21 | 22 | .tags span { 23 | font-weight: bold; 24 | } 25 | 26 | .createdby { 27 | font-style: italic; 28 | color: #444; 29 | font-size: 0.8em; 30 | margin-bottom: 1em; 31 | } 32 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/contexts/AuthContext.js: -------------------------------------------------------------------------------- 1 | import { useContext, createContext } from "react"; 2 | 3 | const AuthContext = createContext(); 4 | 5 | export function AuthProvider({ children, value }) { 6 | return {children}; 7 | } 8 | 9 | export function useAuthValue() { 10 | return useContext(AuthContext); 11 | } 12 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/firebase/config.js: -------------------------------------------------------------------------------- 1 | import { initializeApp } from "firebase/app"; 2 | import { getFirestore } from "firebase/firestore"; 3 | 4 | const firebaseConfig = { 5 | apiKey: "AIzaSyCLvEGhcQo1QjbHP-0D7bRaKrFAaGQW_sg", 6 | authDomain: "miniblog-ref.firebaseapp.com", 7 | projectId: "miniblog-ref", 8 | storageBucket: "miniblog-ref.appspot.com", 9 | messagingSenderId: "411532945241", 10 | appId: "1:411532945241:web:3808bd6c6bf5b1fb774e48", 11 | }; 12 | 13 | const app = initializeApp(firebaseConfig); 14 | 15 | const db = getFirestore(app); 16 | 17 | export { db }; 18 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/hooks/useDeleteDocument.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useReducer } from "react"; 2 | import { db } from "../firebase/config"; 3 | import { doc, deleteDoc } from "firebase/firestore"; 4 | 5 | const initialState = { 6 | loading: null, 7 | error: null, 8 | }; 9 | 10 | const deleteReducer = (state, action) => { 11 | switch (action.type) { 12 | case "LOADING": 13 | return { loading: true, error: null }; 14 | case "DELETED_DOC": 15 | return { loading: false, error: null }; 16 | case "ERROR": 17 | return { loading: false, error: action.payload }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | export const useDeleteDocument = (docCollection) => { 24 | const [response, dispatch] = useReducer(deleteReducer, initialState); 25 | 26 | // deal with memory leak 27 | const [cancelled, setCancelled] = useState(false); 28 | 29 | const checkCancelBeforeDispatch = (action) => { 30 | if (!cancelled) { 31 | dispatch(action); 32 | } 33 | }; 34 | 35 | const deleteDocument = async (id) => { 36 | checkCancelBeforeDispatch({ type: "LOADING" }); 37 | 38 | try { 39 | const deletedDocument = await deleteDoc(doc(db, docCollection, id)); 40 | 41 | checkCancelBeforeDispatch({ 42 | type: "DELETED_DOC", 43 | payload: deletedDocument, 44 | }); 45 | } catch (error) { 46 | checkCancelBeforeDispatch({ type: "ERROR", payload: error.message }); 47 | } 48 | }; 49 | 50 | useEffect(() => { 51 | return () => setCancelled(true); 52 | }, []); 53 | 54 | return { deleteDocument, response }; 55 | }; 56 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/hooks/useFetchDocument.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { db } from "../firebase/config"; 3 | import { doc, getDoc } from "firebase/firestore"; 4 | 5 | export const useFetchDocument = (docCollection, id) => { 6 | const [document, setDocument] = useState(null); 7 | const [error, setError] = useState(null); 8 | const [loading, setLoading] = useState(null); 9 | 10 | useEffect(() => { 11 | const loadDocument = async () => { 12 | setLoading(true); 13 | 14 | try { 15 | const docRef = await doc(db, docCollection, id); 16 | const docSnap = await getDoc(docRef); 17 | 18 | setDocument(docSnap.data()); 19 | } catch (error) { 20 | console.log(error); 21 | setError(error.message); 22 | } 23 | 24 | setLoading(false); 25 | }; 26 | 27 | loadDocument(); 28 | }, [docCollection, id]); 29 | 30 | console.log(document); 31 | 32 | return { document, loading, error }; 33 | }; 34 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/hooks/useInsertDocument.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useReducer } from "react"; 2 | import { db } from "../firebase/config"; 3 | import { collection, addDoc, Timestamp } from "firebase/firestore"; 4 | 5 | const initialState = { 6 | loading: null, 7 | error: null, 8 | }; 9 | 10 | const insertReducer = (state, action) => { 11 | switch (action.type) { 12 | case "LOADING": 13 | return { loading: true, error: null }; 14 | case "INSERTED_DOC": 15 | return { loading: false, error: null }; 16 | case "ERROR": 17 | return { loading: false, error: action.payload }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | export const useInsertDocument = (docCollection) => { 24 | const [response, dispatch] = useReducer(insertReducer, initialState); 25 | 26 | // deal with memory leak 27 | const [cancelled, setCancelled] = useState(false); 28 | 29 | const checkCancelBeforeDispatch = (action) => { 30 | if (!cancelled) { 31 | dispatch(action); 32 | } 33 | }; 34 | 35 | const insertDocument = async (document) => { 36 | checkCancelBeforeDispatch({ type: "LOADING" }); 37 | 38 | try { 39 | const newDocument = { ...document, createdAt: Timestamp.now() }; 40 | 41 | const insertedDocument = await addDoc( 42 | collection(db, docCollection), 43 | newDocument 44 | ); 45 | 46 | checkCancelBeforeDispatch({ 47 | type: "INSERTED_DOC", 48 | payload: insertedDocument, 49 | }); 50 | } catch (error) { 51 | checkCancelBeforeDispatch({ type: "ERROR", payload: error.message }); 52 | } 53 | }; 54 | 55 | useEffect(() => { 56 | return () => setCancelled(true); 57 | }, []); 58 | 59 | return { insertDocument, response }; 60 | }; 61 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/hooks/useQuery.js: -------------------------------------------------------------------------------- 1 | import { useLocation } from "react-router-dom"; 2 | import { useMemo } from "react"; 3 | 4 | export function useQuery() { 5 | const { search } = useLocation(); 6 | 7 | return useMemo(() => new URLSearchParams(search), [search]); 8 | } 9 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/hooks/useUpdateDocument.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useReducer } from "react"; 2 | import { db } from "../firebase/config"; 3 | import { updateDoc, doc } from "firebase/firestore"; 4 | 5 | const initialState = { 6 | loading: null, 7 | error: null, 8 | }; 9 | 10 | const updateReducer = (state, action) => { 11 | switch (action.type) { 12 | case "LOADING": 13 | return { loading: true, error: null }; 14 | case "UPDATED_DOC": 15 | return { loading: false, error: null }; 16 | case "ERROR": 17 | return { loading: false, error: action.payload }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | export const useUpdateDocument = (docCollection) => { 24 | const [response, dispatch] = useReducer(updateReducer, initialState); 25 | 26 | // deal with memory leak 27 | const [cancelled, setCancelled] = useState(false); 28 | 29 | const checkCancelBeforeDispatch = (action) => { 30 | if (!cancelled) { 31 | dispatch(action); 32 | } 33 | }; 34 | 35 | const updateDocument = async (uid, data) => { 36 | checkCancelBeforeDispatch({ type: "LOADING" }); 37 | 38 | try { 39 | const docRef = await doc(db, docCollection, uid); 40 | 41 | console.log(docRef); 42 | 43 | const updatedDocument = await updateDoc(docRef, data); 44 | 45 | console.log(updateDocument); 46 | 47 | checkCancelBeforeDispatch({ 48 | type: "UPDATED_DOC", 49 | payload: updatedDocument, 50 | }); 51 | } catch (error) { 52 | console.log(error); 53 | checkCancelBeforeDispatch({ type: "ERROR", payload: error.message }); 54 | } 55 | }; 56 | 57 | useEffect(() => { 58 | return () => setCancelled(true); 59 | }, []); 60 | 61 | return { updateDocument, response }; 62 | }; 63 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/index.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/pages/About/About.js: -------------------------------------------------------------------------------- 1 | import styles from "./About.module.css"; 2 | 3 | import { Link } from "react-router-dom"; 4 | 5 | const About = () => { 6 | return ( 7 |
8 |

9 | Sobre o Mini Blog 10 |

11 |

12 | Este projeto consiste em um blog feito com React no front-end e Firebase 13 | no back-end. 14 |

15 | 16 | Criar post 17 | 18 |
19 | ); 20 | }; 21 | 22 | export default About; 23 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/pages/About/About.module.css: -------------------------------------------------------------------------------- 1 | .about { 2 | text-align: center; 3 | display: flex; 4 | flex-direction: column; 5 | align-items: center; 6 | } 7 | 8 | .about p { 9 | color: #aaa; 10 | margin-bottom: 2em; 11 | } 12 | 13 | .about a { 14 | display: block; 15 | margin-top: 15px; 16 | padding: 10px 15px; 17 | } 18 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/pages/CreatePost/CreatePost.module.css: -------------------------------------------------------------------------------- 1 | .create_post { 2 | text-align: center; 3 | } 4 | 5 | .create_post h2 { 6 | font-size: 2.2em; 7 | } 8 | 9 | .create_post p { 10 | color: #aaa; 11 | margin-bottom: 2em; 12 | } 13 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/pages/Dashboard/Dashboard.module.css: -------------------------------------------------------------------------------- 1 | .dashboard { 2 | text-align: center; 3 | display: flex; 4 | flex-direction: column; 5 | justify-content: center; 6 | align-items: center; 7 | } 8 | 9 | .dashboard h2 { 10 | font-size: 2.2em; 11 | margin-bottom: 0.5em; 12 | } 13 | 14 | .dashboard p { 15 | color: #aaa; 16 | margin-bottom: 1em; 17 | } 18 | 19 | .noposts { 20 | text-align: center; 21 | } 22 | 23 | .noposts p { 24 | margin-bottom: 1.5em; 25 | } 26 | 27 | .noposts a { 28 | padding: 10px 25px; 29 | } 30 | 31 | .post_header { 32 | display: flex; 33 | justify-content: space-between; 34 | font-weight: bold; 35 | border-bottom: 2px solid #ccc; 36 | width: 80%; 37 | padding: 10px; 38 | } 39 | 40 | .post_row { 41 | display: flex; 42 | justify-content: space-between; 43 | align-items: center; 44 | border-bottom: 1px solid #eee; 45 | width: 80%; 46 | padding: 10px; 47 | } 48 | 49 | .post_row p { 50 | color: #000; 51 | } 52 | 53 | .post_row button, 54 | .post_row a { 55 | margin: 0 5px; 56 | height: 30px; 57 | width: 100px; 58 | font-size: 0.7em; 59 | } 60 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/pages/EditPost/EditPost.module.css: -------------------------------------------------------------------------------- 1 | .edit_post { 2 | text-align: center; 3 | } 4 | 5 | .edit_post h2 { 6 | font-size: 2.2em; 7 | } 8 | 9 | .edit_post p { 10 | color: #aaa; 11 | margin-bottom: 2em; 12 | } 13 | 14 | .edit_post .preview_title { 15 | margin-bottom: 0.2em; 16 | color: #000; 17 | font-weight: bold; 18 | } 19 | 20 | .image_preview { 21 | max-width: 100%; 22 | margin-bottom: 1em; 23 | } 24 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/pages/Home/Home.js: -------------------------------------------------------------------------------- 1 | // CSS 2 | import styles from "./Home.module.css"; 3 | 4 | // hooks 5 | import { useFetchDocuments } from "../../hooks/useFetchDocuments"; 6 | import { useNavigate, Link } from "react-router-dom"; 7 | 8 | // react 9 | import { useState } from "react"; 10 | 11 | // components 12 | import PostDetail from "../../components/PostDetail"; 13 | 14 | const Home = () => { 15 | const { documents: posts, loading } = useFetchDocuments("posts"); 16 | 17 | const navigate = useNavigate(); 18 | 19 | const [query, setQuery] = useState(""); 20 | 21 | const handleSubmit = (e) => { 22 | e.preventDefault(); 23 | 24 | if (query) { 25 | return navigate(`/search?q=${query}`); 26 | } 27 | }; 28 | 29 | console.log(loading); 30 | 31 | return ( 32 |
33 |

Veja os nossos posts mais recentes

34 |
35 | setQuery(e.target.value)} 39 | /> 40 | 41 |
42 |
43 | {loading &&

Carregando...

} 44 | {posts && posts.length === 0 && ( 45 |
46 |

Não foram encontrados posts

47 | 48 | Criar primeiro post 49 | 50 |
51 | )} 52 | {posts && posts.map((post) => )} 53 |
54 |
55 | ); 56 | }; 57 | 58 | export default Home; 59 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/pages/Home/Home.module.css: -------------------------------------------------------------------------------- 1 | .home { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | justify-content: center; 6 | } 7 | 8 | .search_form { 9 | max-width: 100%; 10 | width: 60%; 11 | display: flex; 12 | justify-content: center; 13 | margin-bottom: 2em; 14 | } 15 | 16 | .search_form input { 17 | margin-right: 10px; 18 | width: 50%; 19 | } 20 | 21 | .noposts { 22 | text-align: center; 23 | } 24 | 25 | .noposts p { 26 | margin-bottom: 1.5em; 27 | } 28 | 29 | .noposts a { 30 | padding: 10px 25px; 31 | } 32 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/pages/Login/Login.module.css: -------------------------------------------------------------------------------- 1 | .login { 2 | text-align: center; 3 | } 4 | 5 | .login p { 6 | color: #aaa; 7 | } 8 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/pages/Post/Post.js: -------------------------------------------------------------------------------- 1 | // CSS 2 | import styles from "./Post.module.css"; 3 | 4 | // hooks 5 | import { useFetchDocument } from "../../hooks/useFetchDocument"; 6 | import { useParams } from "react-router-dom"; 7 | 8 | const Post = () => { 9 | const { id } = useParams(); 10 | const { document: post } = useFetchDocument("posts", id); 11 | 12 | return ( 13 |
14 | {post && ( 15 | <> 16 |

{post.title}

17 | {post.title} 18 |

{post.body}

19 |

Este post trata sobre:

20 |
21 | {post.tags.map((tag) => ( 22 |

23 | # 24 | {tag} 25 |

26 | ))} 27 |
28 | 29 | )} 30 |
31 | ); 32 | }; 33 | 34 | export default Post; 35 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/pages/Post/Post.module.css: -------------------------------------------------------------------------------- 1 | .post_container { 2 | text-align: center; 3 | } 4 | 5 | .post_container h3 { 6 | margin-bottom: 0.2em; 7 | } 8 | 9 | .post_container .tags { 10 | display: flex; 11 | justify-content: center; 12 | } 13 | 14 | .tags p { 15 | margin-right: 1em; 16 | } 17 | 18 | .tags span { 19 | font-weight: bold; 20 | } 21 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/pages/Register/Register.module.css: -------------------------------------------------------------------------------- 1 | .register { 2 | text-align: center; 3 | } 4 | 5 | .register p { 6 | color: #aaa; 7 | } 8 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/pages/Search/Search.js: -------------------------------------------------------------------------------- 1 | import styles from "./Search.module.css"; 2 | 3 | // hooks 4 | import { useFetchDocuments } from "../../hooks/useFetchDocuments"; 5 | import { useQuery } from "../../hooks/useQuery"; 6 | 7 | // components 8 | import PostDetail from "../../components/PostDetail"; 9 | import { Link } from "react-router-dom"; 10 | 11 | const Search = () => { 12 | const query = useQuery(); 13 | const search = query.get("q"); 14 | 15 | const { documents: posts } = useFetchDocuments("posts", search); 16 | 17 | return ( 18 |
19 |

Resultados encontrados para: {search}

20 |
21 | {posts && posts.length === 0 && ( 22 | <> 23 |

Não foram encontrados posts a partir da sua busca...

24 | 25 | Voltar 26 | 27 | 28 | )} 29 | {posts && posts.map((post) => )} 30 |
31 |
32 | ); 33 | }; 34 | 35 | export default Search; 36 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/pages/Search/Search.module.css: -------------------------------------------------------------------------------- 1 | .search_container { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | justify-content: center; 6 | } 7 | 8 | .search_container h1 { 9 | margin-bottom: 1em; 10 | } 11 | 12 | .search_container p { 13 | margin-bottom: 30px; 14 | } 15 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /10_MINI_BLOG/miniblog/src/setupTests.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/.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 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reacthooks", 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 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-router-dom": "^6.2.2", 12 | "react-scripts": "5.0.0", 13 | "web-vitals": "^2.1.4" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/11_REACT_HOOKS/reacthooks/public/favicon.ico -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/11_REACT_HOOKS/reacthooks/public/logo192.png -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/11_REACT_HOOKS/reacthooks/public/logo512.png -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/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 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/App.js: -------------------------------------------------------------------------------- 1 | import { BrowserRouter, Routes, Route, Link } from "react-router-dom"; 2 | 3 | import "./App.css"; 4 | 5 | import About from "./pages/About"; 6 | import Home from "./pages/Home"; 7 | 8 | import { HookUseContext } from "./components/HookUseContext"; 9 | 10 | function App() { 11 | return ( 12 |
13 | 14 |

React Hooks

15 | 16 | 17 |
    18 |
  • 19 | Home 20 |
  • 21 |
  • 22 | About 23 |
  • 24 |
25 | 26 | } /> 27 | } /> 28 | 29 |
30 |
31 |
32 | ); 33 | } 34 | 35 | export default App; 36 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/components/HookCustom.js: -------------------------------------------------------------------------------- 1 | import { useDebugValue, useState } from "react"; 2 | import { usePrevious } from "../hooks/usePrevious"; 3 | 4 | const HookCustom = () => { 5 | const [number, setNumber] = useState(0); 6 | const previousValue = usePrevious(number); 7 | 8 | return ( 9 |
10 |

Custom Hook

11 |

Atual: {number}

12 |

Anterior: {previousValue}

13 | 14 |
15 |
16 | ); 17 | }; 18 | 19 | export default HookCustom; 20 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/components/HookUseCallback.js: -------------------------------------------------------------------------------- 1 | import { useCallback, useState } from "react"; 2 | import List from "./List"; 3 | 4 | const HookUseCallback = () => { 5 | const [counter, setCounter] = useState(0); 6 | 7 | // const getItemsFromDatabase = () => { 8 | // return ["a", "b", "c"]; 9 | // }; 10 | 11 | const getItemsFromDatabase = useCallback(() => { 12 | return ["a", "b", "c"]; 13 | }, []); 14 | 15 | return ( 16 |
17 |

useCallback

18 | 19 | 20 |

{counter}

21 |
22 |
23 | ); 24 | }; 25 | 26 | export default HookUseCallback; 27 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/components/HookUseContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | 3 | export const SomeContext = createContext(); 4 | 5 | export const HookUseContext = ({ children }) => { 6 | const contextValue = "testing context"; 7 | 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/components/HookUseEffect.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | const HookUseEffect = () => { 4 | // 1 - useEffect, sem dependencias 5 | // executa sempre ao renderizar componente 6 | useEffect(() => { 7 | console.log("Estou sendo executado!"); 8 | }); 9 | 10 | const [number, setNumber] = useState(1); 11 | 12 | const changeSomething = () => { 13 | setNumber(number + 1); 14 | }; 15 | 16 | // 2 - array de dependências vazio 17 | useEffect(() => { 18 | console.log("Serei executado apenas uma vez!"); 19 | }, []); 20 | 21 | // 3 - item no array de dependências 22 | const [anotherNumber, setAnotherNumber] = useState(0); 23 | 24 | useEffect(() => { 25 | console.log("Sou executado apenas quando anotherNumber muda!"); 26 | }, [anotherNumber]); 27 | 28 | // 4 - limpeza do useEffect 29 | useEffect(() => { 30 | const timer = setTimeout(() => { 31 | console.log("Hello World!"); 32 | 33 | // gera erro sem cleanup 34 | //setAnotherNumber(anotherNumber + 1); 35 | }, 2000); 36 | 37 | // realizar exemplo sem clean up 38 | return () => clearTimeout(timer); 39 | }, [anotherNumber]); 40 | 41 | return ( 42 |
43 |

useEffect

44 |

Number: {number}

45 | 46 |
47 |

Another Number: {anotherNumber}

48 | 51 |
52 |
53 | ); 54 | }; 55 | 56 | export default HookUseEffect; 57 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/components/HookUseEffectLayout.js: -------------------------------------------------------------------------------- 1 | import { useLayoutEffect, useEffect, useState } from "react"; 2 | 3 | const HookUseEffectLayout = () => { 4 | const [name, setName] = useState("Algum nome"); 5 | 6 | useEffect(() => { 7 | console.log("2"); 8 | setName("Mudou de novo!"); 9 | }, []); 10 | 11 | useLayoutEffect(() => { 12 | console.log("1"); 13 | setName("Outro nome"); 14 | }, []); 15 | 16 | console.log(name); 17 | 18 | return ( 19 |
20 |

useEffectLayout

21 |

Nome: {name}

22 |
23 |
24 | ); 25 | }; 26 | 27 | export default HookUseEffectLayout; 28 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/components/HookUseImperativeHandle.js: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import SomeComponent from "./SomeComponent"; 3 | 4 | const HookUseImperativeHandle = () => { 5 | const inputRef = useRef(); 6 | 7 | return ( 8 |
9 |

useImperativeHandle

10 | 11 | 12 |
13 |
14 | ); 15 | }; 16 | 17 | export default HookUseImperativeHandle; 18 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/components/HookUseMemo.js: -------------------------------------------------------------------------------- 1 | import { useState, useMemo, useEffect } from "react"; 2 | 3 | const HookUseMemo = () => { 4 | const [number, setNumber] = useState(0); 5 | 6 | // gera o erro 7 | // const premiumNumbers = ["0", "100", "200"]; 8 | 9 | // resolvendo com o memo 10 | const premiumNumbers = useMemo(() => { 11 | return ["0", "100", "200"]; 12 | }, []); 13 | 14 | useEffect(() => { 15 | console.log("Premium numbers foi alterado!"); 16 | }, [premiumNumbers]); 17 | 18 | return ( 19 |
20 |

useMemo

21 | setNumber(e.target.value)} /> 22 | {premiumNumbers.includes(number) ?

Acertou o número!

: ""} 23 |
24 |
25 | ); 26 | }; 27 | 28 | export default HookUseMemo; 29 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/components/HookUseRef.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef, useState } from "react"; 2 | 3 | const HookUseRef = () => { 4 | // 1 - useRef básico 5 | const numberRef = useRef(0); 6 | const [counter, setCounter] = useState(0); 7 | const [counterB, setCounterB] = useState(0); 8 | 9 | useEffect(() => { 10 | numberRef.current = numberRef.current + 1; 11 | }); 12 | 13 | // 2 - useRef e dom 14 | const inputRef = useRef(); 15 | const [text, setText] = useState(""); 16 | 17 | const handleSubmit = (e) => { 18 | e.preventDefault(); 19 | 20 | setText(""); 21 | 22 | inputRef.current.focus(); 23 | }; 24 | 25 | return ( 26 |
27 |

useRef

28 | {/* 1 - useRef */} 29 |

O componente renderizou: {numberRef.current} vezes.

30 |

Counter 1: {counter}

31 | 32 |

Counter 2: {counterB}

33 | 34 | {/* 2 - useRef e DOM */} 35 |
36 | setText(e.target.value)} 41 | /> 42 | 43 |
44 |
45 |
46 | ); 47 | }; 48 | 49 | export default HookUseRef; 50 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/components/HookUseState.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | const HookUseState = () => { 4 | // 1 - useState 5 | let userName = "João"; 6 | 7 | const [name, setName] = useState("Matheus"); 8 | 9 | const changeNames = () => { 10 | userName = "João Souza"; 11 | 12 | setName("Matheus Battisti"); 13 | 14 | console.log(userName); 15 | console.log(name); 16 | }; 17 | 18 | // 2 - useState e input 19 | const [age, setAge] = useState(18); 20 | 21 | const handleSubmit = (e) => { 22 | e.preventDefault(); 23 | 24 | console.log(age); 25 | }; 26 | 27 | return ( 28 |
29 | {/* 1 - useState */} 30 |

useState

31 |

Variável: {userName}

32 |

useState: {name}

33 | 34 | {/* 2 - useState e inputs */} 35 |

Digite sua idade:

36 |
37 | setAge(e.target.value)} 41 | /> 42 | 43 |
44 |

Você tem {age} anos!

45 |
46 |
47 | ); 48 | }; 49 | 50 | export default HookUseState; 51 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/components/List.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | const List = ({ getItems }) => { 4 | const [myItems, setMyItems] = useState([]); 5 | 6 | useEffect(() => { 7 | console.log("Buscando itens no DB..."); 8 | setMyItems(getItems()); 9 | }, [getItems]); 10 | 11 | return ( 12 |
13 | {myItems.map((item) => ( 14 |

{item}

15 | ))} 16 |
17 | ); 18 | }; 19 | 20 | export default List; 21 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/components/SomeComponent.js: -------------------------------------------------------------------------------- 1 | import { forwardRef, useRef, useImperativeHandle } from "react"; 2 | 3 | const SomeComponent = forwardRef((props, ref) => { 4 | const localInputRef = useRef(); 5 | 6 | useImperativeHandle(ref, () => { 7 | return { 8 | validate: () => { 9 | if (localInputRef.current.value.length > 3) { 10 | localInputRef.current.value = ""; 11 | } 12 | }, 13 | }; 14 | }); 15 | 16 | return ( 17 |
18 |

Insira máximo 3 caracteres!

19 | 20 |
21 | ); 22 | }); 23 | 24 | export default SomeComponent; 25 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/hooks/usePrevious.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useDebugValue } from "react"; 2 | 3 | export const usePrevious = (value) => { 4 | const ref = useRef; 5 | 6 | // useDebugValue 7 | useDebugValue("-- CUSTOM HOOK COM USEDEBUGVALUE --"); 8 | useDebugValue("O número anterior é:" + value); 9 | 10 | useEffect(() => { 11 | ref.current = value; 12 | }); 13 | 14 | return ref.current; 15 | }; 16 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/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 | padding-bottom: 500px; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/index.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/pages/About.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | // useContext 4 | import { useContext } from "react"; 5 | import { SomeContext } from "../components/HookUseContext"; 6 | 7 | const About = () => { 8 | const { contextValue } = useContext(SomeContext); 9 | 10 | return ( 11 |
12 |

About

13 |

Valor do contexto: {contextValue}

14 |
15 |
16 | ); 17 | }; 18 | 19 | export default About; 20 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import HookUseEffect from "../components/HookUseEffect"; 2 | import HookUseReducer from "../components/HookUseReducer"; 3 | import HookUseState from "../components/HookUseState"; 4 | import HookUseRef from "../components/HookUseRef"; 5 | import HookUseCallback from "../components/HookUseCallback"; 6 | import HookUseMemo from "../components/HookUseMemo"; 7 | import HookUseEffectLayout from "../components/HookUseEffectLayout"; 8 | import HookUseImperativeHandle from "../components/HookUseImperativeHandle"; 9 | import HookCustom from "../components/HookCustom"; 10 | 11 | // useContext 12 | import { useContext } from "react"; 13 | import { SomeContext } from "../components/HookUseContext"; 14 | 15 | const Home = () => { 16 | const { contextValue } = useContext(SomeContext); 17 | 18 | return ( 19 |
20 | 21 | 22 | 23 |

useContext

24 |

Valor do contexto: {contextValue}

25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 | ); 34 | }; 35 | 36 | export default Home; 37 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /11_REACT_HOOKS/reacthooks/src/setupTests.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /12_REACTGRAM/backend/.env: -------------------------------------------------------------------------------- 1 | PORT=5000 2 | DB_USER=reactgramref 3 | DB_PASS=BR4rtdDvN2z6Ipzq 4 | JWT_SECRET=thisioursecret -------------------------------------------------------------------------------- /12_REACTGRAM/backend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | uploads/photos/*.png 3 | uploads/photos/*.jpg 4 | uploads/users/*.png 5 | uploads/users/*.jpg -------------------------------------------------------------------------------- /12_REACTGRAM/backend/app.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | 3 | const express = require("express"); 4 | const path = require("path"); 5 | const cors = require("cors"); 6 | 7 | const port = process.env.PORT; 8 | 9 | const app = express(); 10 | 11 | // Config JSON and form data response 12 | app.use(express.json()); 13 | app.use(express.urlencoded({ extended: false })); 14 | 15 | // Solve CORS 16 | app.use(cors({ credentials: true, origin: "http://localhost:3000" })); 17 | 18 | // Upload directory 19 | app.use("/uploads", express.static(path.join(__dirname, "/uploads"))); 20 | 21 | // db connection 22 | require("./config/db.js"); 23 | 24 | // test route 25 | app.get("/", (req, res) => { 26 | res.send("API Working!"); 27 | }); 28 | 29 | // routes 30 | const router = require("./routes/Router.js"); 31 | 32 | app.use(router); 33 | 34 | app.listen(port, () => { 35 | console.log(`App rodando na porta ${port}`); 36 | }); 37 | -------------------------------------------------------------------------------- /12_REACTGRAM/backend/config/db.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const dbUser = process.env.DB_USER; 3 | const dbPassword = process.env.DB_PASS; 4 | 5 | const conn = async () => { 6 | try { 7 | const dbConn = await mongoose.connect( 8 | `mongodb+srv://${dbUser}:${dbPassword}@cluster0.ljrag.mongodb.net/myFirstDatabase?retryWrites=true&w=majority` 9 | ); 10 | console.log("Conectou ao banco de dados!"); 11 | 12 | return dbConn; 13 | } catch (error) { 14 | console.log(error); 15 | } 16 | }; 17 | 18 | conn(); 19 | 20 | module.exports = conn; 21 | -------------------------------------------------------------------------------- /12_REACTGRAM/backend/middlewares/authGuard.js: -------------------------------------------------------------------------------- 1 | const User = require("../models/User"); 2 | 3 | const jwt = require("jsonwebtoken"); 4 | 5 | const jwtSecret = process.env.JWT_SECRET; 6 | 7 | const authGuard = async (req, res, next) => { 8 | const authHeader = req.headers["authorization"]; 9 | const token = authHeader && authHeader.split(" ")[1]; 10 | 11 | // Check if header has a token 12 | if (!token) return res.status(401).json({ errors: ["Acesso negado!"] }); 13 | 14 | // Check if token is valid 15 | try { 16 | const verified = jwt.verify(token, jwtSecret); 17 | 18 | req.user = await User.findById(verified.id).select("-password"); 19 | 20 | next(); 21 | } catch (err) { 22 | res.status(400).json({ errors: ["O Token é inválido!"] }); 23 | } 24 | }; 25 | 26 | module.exports = authGuard; 27 | -------------------------------------------------------------------------------- /12_REACTGRAM/backend/middlewares/handleValidations.js: -------------------------------------------------------------------------------- 1 | const { validationResult } = require("express-validator"); 2 | 3 | const validate = (req, res, next) => { 4 | const errors = validationResult(req); 5 | 6 | if (errors.isEmpty()) { 7 | return next(); 8 | } 9 | 10 | const extractedErrors = []; 11 | 12 | errors.array().map((err) => extractedErrors.push(err.msg)); 13 | 14 | return res.status(422).json({ 15 | errors: extractedErrors, 16 | }); 17 | }; 18 | 19 | module.exports = validate; 20 | -------------------------------------------------------------------------------- /12_REACTGRAM/backend/middlewares/imageUpload.js: -------------------------------------------------------------------------------- 1 | const multer = require("multer"); 2 | const path = require("path"); 3 | 4 | // Destination to store image 5 | const imageStorage = multer.diskStorage({ 6 | destination: function (req, file, cb) { 7 | let folder = ""; 8 | 9 | if (req.baseUrl.includes("users")) { 10 | folder = "users"; 11 | } else if (req.baseUrl.includes("photos")) { 12 | folder = "photos"; 13 | } 14 | cb(null, `uploads/${folder}/`); 15 | }, 16 | filename: (req, file, cb) => { 17 | cb(null, Date.now() + path.extname(file.originalname)); 18 | }, 19 | }); 20 | 21 | const imageUpload = multer({ 22 | storage: imageStorage, 23 | fileFilter(req, file, cb) { 24 | if (!file.originalname.match(/\.(png|jpg)$/)) { 25 | // upload only png and jpg format 26 | return cb(new Error("Por favor, envie apenas png ou jpg!")); 27 | } 28 | cb(undefined, true); 29 | }, 30 | }); 31 | 32 | module.exports = { imageUpload }; 33 | -------------------------------------------------------------------------------- /12_REACTGRAM/backend/middlewares/photoValidations.js: -------------------------------------------------------------------------------- 1 | const { body } = require("express-validator"); 2 | 3 | const photoInsertValidation = () => { 4 | return [ 5 | body("title") 6 | .not() 7 | .equals("undefined") 8 | .withMessage("O título é obrigatório") 9 | .isString() 10 | .withMessage("O título é obrigatório") 11 | .isLength({ min: 3 }) 12 | .withMessage("O nome precisa ter no mínimo 3 caracteres."), 13 | body("image").custom((value, { req }) => { 14 | if (!req.file) { 15 | throw new Error("A imagem é obrigatória"); 16 | } 17 | return true; 18 | }), 19 | ]; 20 | }; 21 | 22 | const photoUpdateValidation = () => { 23 | return [ 24 | body("image") 25 | .optional() 26 | .custom((value, { req }) => { 27 | if (!req.file) { 28 | throw new Error("A imagem é obrigatória"); 29 | } 30 | return true; 31 | }), 32 | body("title") 33 | .optional() 34 | .isString() 35 | .withMessage("O título é obrigatório") 36 | .isLength({ min: 3 }) 37 | .withMessage("O nome precisa ter no mínimo 3 caracteres."), 38 | ]; 39 | }; 40 | 41 | const commentValidation = () => { 42 | return [body("comment").isString().withMessage("O comentário é obrigatório")]; 43 | }; 44 | 45 | module.exports = { 46 | photoInsertValidation, 47 | photoUpdateValidation, 48 | commentValidation, 49 | }; 50 | -------------------------------------------------------------------------------- /12_REACTGRAM/backend/middlewares/userValidations.js: -------------------------------------------------------------------------------- 1 | const { body } = require("express-validator"); 2 | 3 | const userCreateValidation = () => { 4 | return [ 5 | body("name") 6 | .isString() 7 | .withMessage("O nome é obrigatório.") 8 | .isLength({ min: 3 }) 9 | .withMessage("O nome precisa ter no mínimo 3 caracteres."), 10 | body("email") 11 | .isString() 12 | .withMessage("O e-mail é obrigatório.") 13 | .isEmail() 14 | .withMessage("Insira um e-mail válido"), 15 | body("password") 16 | .isString() 17 | .withMessage("A senha é obrigatória.") 18 | .isLength({ min: 5 }) 19 | .withMessage("A senha precisa de no mínimo 5 caracteres."), 20 | body("confirmPassword") 21 | .isString() 22 | .withMessage("A confirmação de senha é obrigatória.") 23 | .custom((value, { req }) => { 24 | if (value != req.body.password) { 25 | throw new Error("As senhas não são iguais."); 26 | } 27 | return true; 28 | }), 29 | ]; 30 | }; 31 | 32 | const loginValidation = () => { 33 | return [ 34 | body("email") 35 | .isString() 36 | .withMessage("O e-mail é obrigatório.") 37 | .isEmail() 38 | .withMessage("Insira um e-mail válido"), 39 | body("password").isString().withMessage("A senha é obrigatória."), 40 | ]; 41 | }; 42 | 43 | const userUpdateValidation = () => { 44 | return [ 45 | body("name") 46 | .optional() 47 | .isLength({ min: 3 }) 48 | .withMessage("O nome precisa ter no mínimo 3 caracteres."), 49 | body("password") 50 | .optional() 51 | .isLength({ min: 5 }) 52 | .withMessage("A senha precisa de no mínimo 5 caracteres."), 53 | ]; 54 | }; 55 | 56 | module.exports = { 57 | userCreateValidation, 58 | loginValidation, 59 | userUpdateValidation, 60 | }; 61 | -------------------------------------------------------------------------------- /12_REACTGRAM/backend/models/Photo.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const { Schema } = mongoose; 3 | 4 | const photoSchema = new Schema( 5 | { 6 | image: String, 7 | title: String, 8 | likes: Array, 9 | comments: Array, 10 | userId: mongoose.ObjectId, 11 | userName: String, 12 | }, 13 | { 14 | timestamps: true, 15 | } 16 | ); 17 | 18 | Photo = mongoose.model("Photo", photoSchema); 19 | 20 | module.exports = Photo; 21 | -------------------------------------------------------------------------------- /12_REACTGRAM/backend/models/User.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const { Schema } = mongoose; 3 | 4 | const userSchema = new Schema( 5 | { 6 | name: String, 7 | email: String, 8 | password: String, 9 | profileImage: String, 10 | bio: String, 11 | }, 12 | { 13 | timestamps: true, 14 | } 15 | ); 16 | 17 | User = mongoose.model("User", userSchema); 18 | 19 | module.exports = User; 20 | -------------------------------------------------------------------------------- /12_REACTGRAM/backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactgram_backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "server": "nodemon ./app.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "nodemon": "^2.0.15" 15 | }, 16 | "dependencies": { 17 | "bcryptjs": "^2.4.3", 18 | "cors": "^2.8.5", 19 | "dotenv": "^16.0.0", 20 | "express": "^4.17.3", 21 | "express-validator": "^6.14.0", 22 | "jsonwebtoken": "^8.5.1", 23 | "mongoose": "^6.2.9", 24 | "multer": "^1.4.4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /12_REACTGRAM/backend/routes/PhotoRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | // Controller 5 | const { 6 | insertPhoto, 7 | deletePhoto, 8 | getAllPhotos, 9 | getUserPhotos, 10 | getPhotoById, 11 | updatePhoto, 12 | likePhoto, 13 | commentPhoto, 14 | searchPhotos, 15 | } = require("../controllers/PhotoController"); 16 | 17 | // Middlewares 18 | const authGuard = require("../middlewares/authGuard"); 19 | const validate = require("../middlewares/handleValidations"); 20 | const { 21 | photoInsertValidation, 22 | photoUpdateValidation, 23 | commentValidation, 24 | } = require("../middlewares/photoValidations"); 25 | const { imageUpload } = require("../middlewares/imageUpload"); 26 | 27 | // Routes 28 | router.post( 29 | "/", 30 | authGuard, 31 | imageUpload.single("image"), 32 | photoInsertValidation(), 33 | validate, 34 | insertPhoto 35 | ); 36 | router.delete("/:id", authGuard, deletePhoto); 37 | router.get("/", getAllPhotos); 38 | router.get("/user/:id", getUserPhotos); 39 | router.get("/search", searchPhotos); 40 | 41 | router.get("/:id", getPhotoById); 42 | router.put( 43 | "/:id", 44 | authGuard, 45 | imageUpload.single("image"), 46 | photoUpdateValidation(), 47 | validate, 48 | updatePhoto 49 | ); 50 | router.put("/like/:id", authGuard, likePhoto); 51 | router.put( 52 | "/comment/:id", 53 | authGuard, 54 | commentValidation(), 55 | validate, 56 | commentPhoto 57 | ); 58 | 59 | module.exports = router; 60 | -------------------------------------------------------------------------------- /12_REACTGRAM/backend/routes/Router.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express(); 3 | 4 | router.use("/api/users", require("./UserRoutes")); 5 | router.use("/api/photos", require("./PhotoRoutes")); 6 | 7 | module.exports = router; 8 | -------------------------------------------------------------------------------- /12_REACTGRAM/backend/routes/UserRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | // Controller 5 | const { 6 | register, 7 | getCurrentUser, 8 | login, 9 | update, 10 | getUserById, 11 | } = require("../controllers/UserController"); 12 | 13 | // Middlewares 14 | const validate = require("../middlewares/handleValidations"); 15 | const { 16 | userCreateValidation, 17 | loginValidation, 18 | userUpdateValidation, 19 | } = require("../middlewares/userValidations"); 20 | const authGuard = require("../middlewares/authGuard"); 21 | const { imageUpload } = require("../middlewares/imageUpload"); 22 | 23 | // Routes 24 | router.post("/register", userCreateValidation(), validate, register); 25 | router.get("/profile", authGuard, getCurrentUser); 26 | router.post("/login", loginValidation(), validate, login); 27 | router.put( 28 | "/", 29 | authGuard, 30 | userUpdateValidation(), 31 | validate, 32 | imageUpload.single("profileImage"), 33 | update 34 | ); 35 | router.get("/:id", getUserById); 36 | 37 | module.exports = router; 38 | -------------------------------------------------------------------------------- /12_REACTGRAM/backend/uploads/photos/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/backend/uploads/photos/.gitkeep -------------------------------------------------------------------------------- /12_REACTGRAM/backend/uploads/users/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/backend/uploads/users/.gitkeep -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/.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 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactgram_frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@reduxjs/toolkit": "^1.8.1", 7 | "@testing-library/jest-dom": "^5.16.3", 8 | "@testing-library/react": "^12.1.4", 9 | "@testing-library/user-event": "^13.5.0", 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2", 12 | "react-icons": "^4.3.1", 13 | "react-redux": "^7.2.7", 14 | "react-router-dom": "^6.2.2", 15 | "react-scripts": "5.0.0", 16 | "web-vitals": "^2.1.4" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 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 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/frontend/public/favicon.ico -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/frontend/public/logo192.png -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/frontend/public/logo512.png -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/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 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/App.css: -------------------------------------------------------------------------------- 1 | .container { 2 | min-height: 70vh; 3 | } 4 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/components/Footer.css: -------------------------------------------------------------------------------- 1 | #footer { 2 | background-color: #121212; 3 | height: 200px; 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | border-top: 1px solid #363636; 8 | } 9 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import "./Footer.css"; 2 | 3 | const Footer = () => { 4 | return ( 5 |
6 |

ReactGram © 2022

7 |
8 | ); 9 | }; 10 | 11 | export default Footer; 12 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/components/LikeContainer.css: -------------------------------------------------------------------------------- 1 | .like { 2 | display: flex; 3 | align-items: center; 4 | border-top: 1px solid #363636; 5 | border-bottom: 1px solid #363636; 6 | } 7 | 8 | #home .like { 9 | border: none; 10 | } 11 | 12 | .like svg { 13 | font-size: 1.5em; 14 | cursor: pointer; 15 | } 16 | 17 | .like p { 18 | margin-left: 1em; 19 | } 20 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/components/LikeContainer.js: -------------------------------------------------------------------------------- 1 | import "./LikeContainer.css"; 2 | 3 | import { BsHeart, BsHeartFill } from "react-icons/bs"; 4 | 5 | const LikeContainer = ({ photo, user, handleLike }) => { 6 | return ( 7 |
8 | {photo.likes && user && ( 9 | <> 10 | {photo.likes.includes(user._id) ? ( 11 | 12 | ) : ( 13 | handleLike(photo)} /> 14 | )} 15 |

{photo.likes.length} like(s)

16 | 17 | )} 18 |
19 | ); 20 | }; 21 | 22 | export default LikeContainer; 23 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/components/Message.css: -------------------------------------------------------------------------------- 1 | .message { 2 | border-radius: 5px; 3 | padding: 5px 10px; 4 | position: relative; 5 | margin: 0; 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | } 10 | 11 | .error { 12 | 13 | color: #721c24; 14 | background-color: #f8d7da; 15 | border-color: #f5c6cb; 16 | } 17 | 18 | .success { 19 | color: #155724; 20 | background-color: #d4edda; 21 | border-color: #c3e6cb; 22 | } -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/components/Message.js: -------------------------------------------------------------------------------- 1 | import "./Message.css"; 2 | 3 | const Message = ({ msg, type }) => { 4 | return ( 5 |
6 |

{msg}

7 |
8 | ); 9 | }; 10 | 11 | export default Message; 12 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/components/Navbar.css: -------------------------------------------------------------------------------- 1 | #nav { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | background-color: #000; 6 | border-bottom: 1px solid #363636; 7 | padding: 0.1em 1em; 8 | } 9 | 10 | #search-form { 11 | position: relative; 12 | width: 20%; 13 | } 14 | 15 | #search-form svg { 16 | position: absolute; 17 | top: 10px; 18 | left: 9px; 19 | } 20 | 21 | #search-form input { 22 | padding-left: 2.5em; 23 | border: none; 24 | border-radius: 5px; 25 | width: 100%; 26 | margin: 0; 27 | } 28 | 29 | #nav-links { 30 | display: flex; 31 | align-items: center; 32 | } 33 | 34 | #nav-links li { 35 | margin-right: 1em; 36 | } 37 | 38 | #nav-links span { 39 | cursor: pointer; 40 | } 41 | 42 | #nav-links svg { 43 | font-size: 1.5em; 44 | } 45 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/components/PhotoItem.css: -------------------------------------------------------------------------------- 1 | .photo-item img { 2 | width: 100%; 3 | } 4 | 5 | #home .photo-item h2 { 6 | margin-bottom: 0.2em; 7 | } 8 | 9 | .photo-author { 10 | text-align: left; 11 | } 12 | 13 | .photo-author a { 14 | font-weight: bold; 15 | } 16 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/components/PhotoItem.js: -------------------------------------------------------------------------------- 1 | import "./PhotoItem.css"; 2 | 3 | import { uploads } from "../utils/config"; 4 | 5 | import { Link } from "react-router-dom"; 6 | 7 | const PhotoItem = ({ photo }) => { 8 | return ( 9 |
10 | {photo.image && ( 11 | {photo.title} 12 | )} 13 |

{photo.title}

14 |

15 | Publicada por:{" "} 16 | {photo.userName} 17 |

18 |
19 | ); 20 | }; 21 | 22 | export default PhotoItem; 23 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/hooks/useAuth.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { useSelector } from "react-redux"; 3 | 4 | export const useAuth = () => { 5 | const { user } = useSelector((state) => state.auth); 6 | 7 | const [auth, setAuth] = useState(false); 8 | const [loading, setLoading] = useState(true); 9 | 10 | useEffect(() => { 11 | if (user) { 12 | setAuth(true); 13 | } else { 14 | setAuth(false); 15 | } 16 | 17 | setLoading(false); 18 | }, [user]); 19 | 20 | return { auth, loading }; 21 | }; 22 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/hooks/useQuery.js: -------------------------------------------------------------------------------- 1 | import { useLocation } from "react-router-dom"; 2 | import { useMemo } from "react"; 3 | 4 | export function useQuery() { 5 | const { search } = useLocation(); 6 | 7 | return useMemo(() => new URLSearchParams(search), [search]); 8 | } 9 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/hooks/useResetComponentMessage.js: -------------------------------------------------------------------------------- 1 | // Redux 2 | import { resetMessage } from "../slices/photoSlice"; 3 | 4 | export const useResetComponentMessage = (dispatch) => { 5 | return () => { 6 | setTimeout(() => { 7 | dispatch(resetMessage()); 8 | }, 2000); 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Roboto", sans-serif; 3 | margin: 0; 4 | padding: 0; 5 | box-sizing: border-box; 6 | background-color: #121212; 7 | color: #fafafa; 8 | } 9 | 10 | a { 11 | color: #fafafa; 12 | text-decoration: none; 13 | } 14 | 15 | .active { 16 | font-weight: bold; 17 | } 18 | 19 | ul { 20 | list-style: none; 21 | } 22 | 23 | form { 24 | display: flex; 25 | flex-direction: column; 26 | } 27 | 28 | label { 29 | display: flex; 30 | flex-direction: column; 31 | text-align: left; 32 | } 33 | 34 | label span { 35 | font-weight: bold; 36 | margin-bottom: 0.5em; 37 | color: #aaa; 38 | font-size: 0.7em; 39 | } 40 | 41 | input { 42 | background-color: #3b3b3b; 43 | color: #aaa; 44 | border: 1px solid #555; 45 | border-radius: 2px; 46 | padding: 10px 8px; 47 | outline: none; 48 | margin-bottom: 0.6em; 49 | } 50 | 51 | input::placeholder { 52 | color: #aaa; 53 | } 54 | 55 | .btn, 56 | button, 57 | input[type="submit"] { 58 | background-color: #0094f6; 59 | color: #fff; 60 | border: none; 61 | border-radius: 4px; 62 | padding: 10px 8px; 63 | font-weight: bold; 64 | cursor: pointer; 65 | opacity: 0.8; 66 | font-size: 1em; 67 | } 68 | 69 | input[type="submit"] { 70 | margin-top: 1em; 71 | } 72 | 73 | input:disabled { 74 | cursor: not-allowed; 75 | background-color: #000; 76 | } 77 | 78 | button:hover, 79 | input[type="submit"]:hover { 80 | opacity: 1; 81 | } 82 | 83 | .cancel-btn { 84 | background-color: #ccc; 85 | } 86 | 87 | .hide { 88 | display: none; 89 | } 90 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/index.js: -------------------------------------------------------------------------------- 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 | // Redux 8 | import { Provider } from "react-redux"; 9 | import { store } from "./store"; 10 | 11 | ReactDOM.render( 12 | 13 | 14 | 15 | 16 | , 17 | document.getElementById("root") 18 | ); 19 | 20 | // If you want to start measuring performance in your app, pass a function 21 | // to log results (for example: reportWebVitals(console.log)) 22 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 23 | reportWebVitals(); 24 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/pages/Auth/Auth.css: -------------------------------------------------------------------------------- 1 | #login, 2 | #register { 3 | border: 1px solid #363636; 4 | background-color: #000; 5 | padding: 1.5em 2em; 6 | max-width: 33%; 7 | margin: 2em auto; 8 | } 9 | 10 | #login h2, 11 | #register h2 { 12 | text-align: center; 13 | font-size: 2.1em; 14 | margin-top: 0; 15 | } 16 | 17 | .subtitle { 18 | font-weight: bold; 19 | color: #999; 20 | margin-bottom: 1.5em; 21 | } 22 | 23 | #login form, 24 | #register form { 25 | padding-bottom: 1.5em; 26 | margin-bottom: 1.5em; 27 | border-bottom: 1px solid #363636; 28 | } 29 | 30 | #login p, 31 | #register p { 32 | text-align: center; 33 | } 34 | 35 | #login p a, 36 | #register p a { 37 | font-weight: bold; 38 | color: #0094f6; 39 | } 40 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/pages/Auth/Login.js: -------------------------------------------------------------------------------- 1 | import "./Auth.css"; 2 | 3 | // Components 4 | import { Link } from "react-router-dom"; 5 | import Message from "../../components/Message"; 6 | 7 | // Hooks 8 | import { useEffect, useState } from "react"; 9 | import { useSelector, useDispatch } from "react-redux"; 10 | 11 | // Redux 12 | import { login, reset } from "../../slices/authSlice"; 13 | 14 | const Login = () => { 15 | const [email, setEmail] = useState(""); 16 | const [password, setPassword] = useState(""); 17 | 18 | const dispatch = useDispatch(); 19 | 20 | const { loading, error } = useSelector((state) => state.auth); 21 | 22 | const handleSubmit = (e) => { 23 | e.preventDefault(); 24 | 25 | const user = { 26 | email, 27 | password, 28 | }; 29 | 30 | console.log(user); 31 | 32 | dispatch(login(user)); 33 | }; 34 | 35 | // Clean all auth states 36 | useEffect(() => { 37 | dispatch(reset()); 38 | }, [dispatch]); 39 | 40 | return ( 41 |
42 |

ReactGram

43 |

Faça o login para ver o que há de novo.

44 |
45 | setEmail(e.target.value)} 49 | value={email} 50 | /> 51 | setPassword(e.target.value)} 55 | value={password} 56 | /> 57 | {!loading && } 58 | {loading && } 59 | {error && } 60 | 61 |

62 | Não tem uma conta? Clique aqui 63 |

64 |
65 | ); 66 | }; 67 | 68 | export default Login; 69 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/pages/EditProfile/EditProfile.css: -------------------------------------------------------------------------------- 1 | #edit-profile { 2 | border: 1px solid #363636; 3 | background-color: #000; 4 | padding: 1.5em 2em; 5 | max-width: 40%; 6 | margin: 2em auto; 7 | text-align: center; 8 | } 9 | 10 | .subtitle { 11 | color: #ccc; 12 | } 13 | 14 | .profile-image { 15 | width: 150px; 16 | height: 150px; 17 | border-radius: 50%; 18 | margin-bottom: 1em; 19 | } -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/pages/Home/Home.css: -------------------------------------------------------------------------------- 1 | #home { 2 | width: 50%; 3 | margin: 0 auto; 4 | padding-top: 2em; 5 | } 6 | 7 | #home .btn { 8 | display: block; 9 | max-width: 80px; 10 | text-align: center; 11 | margin: 0.5em 0 2em 0; 12 | } 13 | 14 | .no-photos { 15 | text-align: center; 16 | } 17 | 18 | .no-photos a { 19 | color: #0094f6; 20 | } 21 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/pages/Home/Home.js: -------------------------------------------------------------------------------- 1 | import "./Home.css"; 2 | 3 | // components 4 | import LikeContainer from "../../components/LikeContainer"; 5 | import PhotoItem from "../../components/PhotoItem"; 6 | import { Link } from "react-router-dom"; 7 | 8 | // hooks 9 | import { useEffect } from "react"; 10 | import { useSelector, useDispatch } from "react-redux"; 11 | import { useResetComponentMessage } from "../../hooks/useResetComponentMessage"; 12 | 13 | // Redux 14 | import { getPhotos, like } from "../../slices/photoSlice"; 15 | 16 | const Home = () => { 17 | const dispatch = useDispatch(); 18 | 19 | const resetMessage = useResetComponentMessage(dispatch); 20 | 21 | const { user } = useSelector((state) => state.auth); 22 | const { photos, loading } = useSelector((state) => state.photo); 23 | 24 | // Load all photos 25 | useEffect(() => { 26 | dispatch(getPhotos()); 27 | }, [dispatch]); 28 | 29 | const handleLike = (photo = null) => { 30 | dispatch(like(photo._id)); 31 | 32 | resetMessage(); 33 | }; 34 | 35 | if (loading) { 36 | return

Carregando...

; 37 | } 38 | 39 | return ( 40 |
41 | {photos && 42 | photos.map((photo) => ( 43 |
44 | 45 | 46 | 47 | Ver mais 48 | 49 |
50 | ))} 51 | {photos && photos.length === 0 && ( 52 |

53 | Ainda não há fotos publicadas,{" "} 54 | clique aqui para começar. 55 |

56 | )} 57 |
58 | ); 59 | }; 60 | 61 | export default Home; 62 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/pages/Photo/Photo.css: -------------------------------------------------------------------------------- 1 | #photo { 2 | width: 50%; 3 | margin: 0 auto; 4 | text-align: center; 5 | margin-top: 2em; 6 | } 7 | 8 | #photo img { 9 | width: 100%; 10 | } 11 | 12 | .message-container { 13 | margin: 1em 0; 14 | } 15 | 16 | .comments { 17 | text-align: left; 18 | } 19 | 20 | .comments form { 21 | margin-bottom: 2em; 22 | padding-bottom: 1em; 23 | border-bottom: 1px solid #363636; 24 | } 25 | 26 | .author { 27 | display: flex; 28 | font-weight: bold; 29 | } 30 | 31 | #photo .author img { 32 | width: 50px; 33 | height: 50px; 34 | border-radius: 50%; 35 | margin-right: 1em; 36 | } 37 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/pages/Profile/Profile.css: -------------------------------------------------------------------------------- 1 | #profile { 2 | width: 50%; 3 | margin: 0 auto; 4 | } 5 | 6 | .profile-header { 7 | display: flex; 8 | align-items: center; 9 | flex-wrap: wrap; 10 | padding: 1em; 11 | border-bottom: 1px solid #363636; 12 | } 13 | 14 | .profile-header img { 15 | width: 100px; 16 | height: 100px; 17 | border-radius: 50%; 18 | margin-right: 2em; 19 | } 20 | 21 | .new-photo { 22 | padding: 1em; 23 | border-bottom: 1px solid #363636; 24 | } 25 | 26 | .photos-container { 27 | display: flex; 28 | flex-wrap: wrap; 29 | } 30 | 31 | .photo { 32 | width: 32%; 33 | margin: 0.3%; 34 | } 35 | 36 | .photo img { 37 | width: 100%; 38 | } 39 | 40 | .actions { 41 | display: flex; 42 | justify-content: space-around; 43 | padding: 10px; 44 | } 45 | 46 | .actions svg { 47 | cursor: pointer; 48 | } 49 | 50 | .edit-photo { 51 | margin-bottom: 1em; 52 | } 53 | 54 | .edit-photo img { 55 | width: 100%; 56 | margin-bottom: 1em; 57 | } 58 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/pages/Search/Search.css: -------------------------------------------------------------------------------- 1 | #search { 2 | width: 50%; 3 | margin: 0 auto; 4 | padding-top: 2em; 5 | } 6 | 7 | #search h2 { 8 | text-align: center; 9 | } 10 | 11 | #search .btn { 12 | display: block; 13 | max-width: 80px; 14 | text-align: center; 15 | margin: 0.5em 0 2em 0; 16 | } 17 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/pages/Search/Search.js: -------------------------------------------------------------------------------- 1 | import "./Search.css"; 2 | 3 | // hooks 4 | import { useQuery } from "../../hooks/useQuery"; 5 | import { useEffect } from "react"; 6 | import { useSelector, useDispatch } from "react-redux"; 7 | import { useResetComponentMessage } from "../../hooks/useResetComponentMessage"; 8 | 9 | // components 10 | import LikeContainer from "../../components/LikeContainer"; 11 | import PhotoItem from "../../components/PhotoItem"; 12 | import { Link } from "react-router-dom"; 13 | 14 | // Redux 15 | import { searchPhotos, like } from "../../slices/photoSlice"; 16 | 17 | const Search = () => { 18 | const query = useQuery(); 19 | const search = query.get("q"); 20 | 21 | const dispatch = useDispatch(); 22 | 23 | const resetMessage = useResetComponentMessage(dispatch); 24 | 25 | const { user } = useSelector((state) => state.auth); 26 | const { photos, loading } = useSelector((state) => state.photo); 27 | 28 | // Load all photos 29 | useEffect(() => { 30 | dispatch(searchPhotos(search)); 31 | }, [dispatch, search]); 32 | 33 | const handleLike = (photo = null) => { 34 | dispatch(like(photo._id)); 35 | 36 | resetMessage(); 37 | }; 38 | 39 | if (loading) { 40 | return

Carregando...

; 41 | } 42 | 43 | return ( 44 | 57 | ); 58 | }; 59 | 60 | export default Search; 61 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/services/authService.js: -------------------------------------------------------------------------------- 1 | import { api, requestConfig } from "../utils/config"; 2 | 3 | // Register a user 4 | const register = async (data) => { 5 | const config = requestConfig("POST", data); 6 | 7 | try { 8 | const res = await fetch(api + "/users/register", config) 9 | .then((res) => res.json()) 10 | .catch((err) => err); 11 | 12 | if (res) { 13 | localStorage.setItem("user", JSON.stringify(res)); 14 | } 15 | 16 | return res; 17 | } catch (error) { 18 | console.log(error); 19 | } 20 | }; 21 | 22 | // Logout a user 23 | const logout = () => { 24 | localStorage.removeItem("user"); 25 | }; 26 | 27 | // Sign in a user 28 | const login = async (data) => { 29 | const config = requestConfig("POST", data); 30 | 31 | try { 32 | const res = await fetch(api + "/users/login", config) 33 | .then((res) => res.json()) 34 | .catch((err) => err); 35 | 36 | if (res) { 37 | localStorage.setItem("user", JSON.stringify(res)); 38 | } 39 | 40 | return res; 41 | } catch (error) { 42 | console.log(error); 43 | } 44 | }; 45 | 46 | const authService = { 47 | register, 48 | logout, 49 | login, 50 | }; 51 | 52 | export default authService; 53 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/services/userService.js: -------------------------------------------------------------------------------- 1 | import { api, requestConfig } from "../utils/config"; 2 | 3 | // Get user details 4 | const profile = async (data, token) => { 5 | const config = requestConfig("GET", data, token); 6 | 7 | try { 8 | const res = await fetch(api + "/users/profile", config) 9 | .then((res) => res.json()) 10 | .catch((err) => err); 11 | 12 | return res; 13 | } catch (error) { 14 | console.log(error); 15 | } 16 | }; 17 | 18 | // Update user details 19 | const updateProfile = async (data, token) => { 20 | const config = requestConfig("PUT", data, token, true); 21 | 22 | try { 23 | const res = await fetch(api + "/users/", config) 24 | .then((res) => res.json()) 25 | .catch((err) => err); 26 | 27 | return res; 28 | } catch (error) { 29 | console.log(error); 30 | } 31 | }; 32 | 33 | // Get user details 34 | const getUserDetails = async (id) => { 35 | const config = requestConfig("GET"); 36 | 37 | try { 38 | const res = await fetch(api + "/users/" + id, config) 39 | .then((res) => res.json()) 40 | .catch((err) => err); 41 | 42 | return res; 43 | } catch (error) { 44 | console.log(error); 45 | } 46 | }; 47 | 48 | const userService = { 49 | profile, 50 | updateProfile, 51 | getUserDetails, 52 | }; 53 | 54 | export default userService; 55 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/setupTests.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import authReducer from "./slices/authSlice"; 3 | import userReducer from "./slices/userSlice"; 4 | import photoReducer from "./slices/photoSlice"; 5 | 6 | export const store = configureStore({ 7 | reducer: { 8 | auth: authReducer, 9 | user: userReducer, 10 | photo: photoReducer, 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /12_REACTGRAM/frontend/src/utils/config.js: -------------------------------------------------------------------------------- 1 | export const api = "http://localhost:5000/api"; 2 | export const uploads = "http://localhost:5000/uploads"; 3 | 4 | export const requestConfig = (method, data, token = null, image = null) => { 5 | let config; 6 | 7 | if (image) { 8 | config = { 9 | method: method, 10 | body: data, 11 | headers: {}, 12 | }; 13 | } else if (method === "DELETE" || data === null) { 14 | config = { 15 | method: method, 16 | headers: {}, 17 | }; 18 | } else { 19 | config = { 20 | method: method, 21 | body: JSON.stringify(data), 22 | headers: { 23 | "Content-Type": "application/json", 24 | }, 25 | }; 26 | } 27 | 28 | if (token) { 29 | config.headers.Authorization = `Bearer ${token}`; 30 | } 31 | 32 | return config; 33 | }; 34 | -------------------------------------------------------------------------------- /12_REACTGRAM/images/f1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f1.jpg -------------------------------------------------------------------------------- /12_REACTGRAM/images/f2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f2.jpg -------------------------------------------------------------------------------- /12_REACTGRAM/images/f3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f3.jpg -------------------------------------------------------------------------------- /12_REACTGRAM/images/f4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f4.jpg -------------------------------------------------------------------------------- /12_REACTGRAM/images/f5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f5.jpg -------------------------------------------------------------------------------- /12_REACTGRAM/images/f6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f6.jpg -------------------------------------------------------------------------------- /12_REACTGRAM/images/f7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f7.jpg -------------------------------------------------------------------------------- /12_REACTGRAM/images/f8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f8.jpg -------------------------------------------------------------------------------- /12_REACTGRAM/images/f9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/f9.jpg -------------------------------------------------------------------------------- /12_REACTGRAM/images/u1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/u1.jpg -------------------------------------------------------------------------------- /12_REACTGRAM/images/u2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/u2.jpg -------------------------------------------------------------------------------- /12_REACTGRAM/images/u3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/12_REACTGRAM/images/u3.jpg -------------------------------------------------------------------------------- /13_CURSO_GRATUITO/todo/.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 | -------------------------------------------------------------------------------- /13_CURSO_GRATUITO/todo/data/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "todos": [ 3 | { 4 | "id": 0.7006336193587834, 5 | "title": "Nova tarefa ", 6 | "time": "2", 7 | "done": false 8 | }, 9 | { 10 | "id": 0.7326144513928106, 11 | "title": "Outra tarefa", 12 | "time": "3", 13 | "done": true 14 | }, 15 | { 16 | "id": 0.42808620877472925, 17 | "title": "Mais uma", 18 | "time": "4", 19 | "done": false 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /13_CURSO_GRATUITO/todo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.4", 7 | "@testing-library/react": "^12.1.4", 8 | "@testing-library/user-event": "^13.5.0", 9 | "json-server": "^0.17.0", 10 | "react": "^18.0.0", 11 | "react-dom": "^18.0.0", 12 | "react-icons": "^4.3.1", 13 | "react-scripts": "5.0.0", 14 | "web-vitals": "^2.1.4" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject", 21 | "server": "json-server --watch data/db.json --port 5000" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /13_CURSO_GRATUITO/todo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/13_CURSO_GRATUITO/todo/public/favicon.ico -------------------------------------------------------------------------------- /13_CURSO_GRATUITO/todo/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /13_CURSO_GRATUITO/todo/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/13_CURSO_GRATUITO/todo/public/logo192.png -------------------------------------------------------------------------------- /13_CURSO_GRATUITO/todo/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/13_CURSO_GRATUITO/todo/public/logo512.png -------------------------------------------------------------------------------- /13_CURSO_GRATUITO/todo/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 | -------------------------------------------------------------------------------- /13_CURSO_GRATUITO/todo/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /13_CURSO_GRATUITO/todo/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /13_CURSO_GRATUITO/todo/src/index.css: -------------------------------------------------------------------------------- 1 | html { 2 | background: linear-gradient( 3 | 195deg, 4 | rgba(240, 98, 146, 1) 0%, 5 | rgba(221, 89, 133, 1) 98% 6 | ); 7 | } 8 | 9 | body { 10 | margin: 0; 11 | padding: 0; 12 | font-family: Helvetica; 13 | } 14 | -------------------------------------------------------------------------------- /13_CURSO_GRATUITO/todo/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById("root")); 8 | 9 | root.render( 10 | 11 | 12 | 13 | ); 14 | 15 | // If you want to start measuring performance in your app, pass a function 16 | // to log results (for example: reportWebVitals(console.log)) 17 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 18 | reportWebVitals(); 19 | -------------------------------------------------------------------------------- /13_CURSO_GRATUITO/todo/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /13_CURSO_GRATUITO/todo/src/setupTests.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/.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 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fundamentos", 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 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-scripts": "5.0.0", 12 | "web-vitals": "^2.1.4" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": [ 22 | "react-app", 23 | "react-app/jest" 24 | ] 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/2_FUNDAMENTOS/fundamentos/public/favicon.ico -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/2_FUNDAMENTOS/fundamentos/public/logo192.png -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/2_FUNDAMENTOS/fundamentos/public/logo512.png -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/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 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/src/App.js: -------------------------------------------------------------------------------- 1 | import logo from "./logo.svg"; 2 | import "./App.css"; 3 | 4 | import FirstComponent from "./components/FirstComponent"; 5 | import TemplateExpressions from "./components/TemplateExpressions"; 6 | import MyComponent from "./components/MyComponent"; 7 | import Events from "./components/Events"; 8 | import Challenge from "./components/Challenge"; 9 | 10 | function App() { 11 | return ( 12 |
13 | 14 | 15 | 16 | 17 | 18 |
19 | ); 20 | } 21 | 22 | export default App; 23 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/src/components/Challenge.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Challenge = () => { 4 | const a = 10; 5 | const b = 15; 6 | 7 | return ( 8 |
9 |

A: {a}

10 |

B: {b}

11 | 12 |
13 | ); 14 | }; 15 | 16 | export default Challenge; 17 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/src/components/Events.js: -------------------------------------------------------------------------------- 1 | const Events = () => { 2 | const handleMyEvent = (e) => { 3 | console.log(e); 4 | console.log("Ativou o evento!"); 5 | }; 6 | 7 | const renderSomething = (x) => { 8 | if (x) { 9 | return

Renderizando isso!

; 10 | } else { 11 | return

Também posso renderizar isso!

; 12 | } 13 | }; 14 | 15 | return ( 16 |
17 | {/* evento */} 18 |
19 | 20 |
21 | {/* lógica no evento */} 22 |
23 | 26 | 35 |
36 | {renderSomething(true)} 37 | {renderSomething(false)} 38 |
39 | ); 40 | }; 41 | 42 | export default Events; 43 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/src/components/FirstComponent.js: -------------------------------------------------------------------------------- 1 | import MyComponent from "./MyComponent"; 2 | 3 | const FirstComponent = () => { 4 | // Um comentário 5 | return ( 6 |
7 | {/* Um comentário no JSX */} 8 |

Título

9 |

Testando alguma classe

10 | 11 |
12 | ); 13 | }; 14 | 15 | export default FirstComponent; 16 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/src/components/MyComponent.js: -------------------------------------------------------------------------------- 1 | const MyComponent = () => { 2 | return ( 3 |
4 |

Estou sendo reaproveitado em vários lugares!

5 |
6 | ); 7 | }; 8 | 9 | export default MyComponent; 10 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/src/components/TemplateExpressions.js: -------------------------------------------------------------------------------- 1 | const TemplateExpressions = () => { 2 | const name = "Matheus"; 3 | const data = { 4 | age: 31, 5 | job: "Programmer", 6 | }; 7 | 8 | return ( 9 |
10 |

Olá {name}, tudo bem?

11 |

Você atua como: {data.job}

12 |

{4 + 4}

13 |
14 | ); 15 | }; 16 | 17 | export default TemplateExpressions; 18 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/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 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/src/index.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /2_FUNDAMENTOS/fundamentos/src/setupTests.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/.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 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "avancando", 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 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-scripts": "5.0.0", 12 | "web-vitals": "^2.1.4" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": [ 22 | "react-app", 23 | "react-app/jest" 24 | ] 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/3_AVANCANDO_NO_REACT/avancando/public/favicon.ico -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/public/img1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/3_AVANCANDO_NO_REACT/avancando/public/img1.jpg -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/3_AVANCANDO_NO_REACT/avancando/public/logo192.png -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/3_AVANCANDO_NO_REACT/avancando/public/logo512.png -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/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 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/assets/city.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/3_AVANCANDO_NO_REACT/avancando/src/assets/city.jpg -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/components/CarDetails.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const CarDetails = ({ brand, km, color }) => { 4 | return ( 5 |
6 |

Detalhes do carro:

7 |
    8 |
  • Marca: {brand}
  • 9 |
  • Kilometragem: {km}
  • 10 |
  • Cor: {color}
  • 11 |
12 |
13 | ); 14 | }; 15 | 16 | export default CarDetails; 17 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/components/ChangeMessageState.js: -------------------------------------------------------------------------------- 1 | const ChangeMessageState = ({ handleMessage }) => { 2 | const messages = ["Oi", "Olá", "Tudo bem?"]; 3 | 4 | return ( 5 |
6 | 7 | 8 | 9 |
10 | ); 11 | }; 12 | 13 | export default ChangeMessageState; 14 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/components/ConditionalRender.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const ConditionalRender = () => { 4 | const x = true; 5 | 6 | const name = "Matheus"; 7 | 8 | return ( 9 |
10 |

Isso será exibido?

11 | {x &&

Se x for true sim!

} 12 |

Render ternário:

13 | {name === "João" ? ( 14 |
15 |

O nome é João

16 |
17 | ) : ( 18 |
19 |

Nome não encontrado!

20 |
21 | )} 22 |
23 | ); 24 | }; 25 | 26 | export default ConditionalRender; 27 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/components/Container.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Container = ({ children }) => { 4 | return ( 5 |
6 |

Conteúdo do componente pai:

7 | {children} 8 |
9 | ); 10 | }; 11 | 12 | export default Container; 13 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/components/ExecuteFunction.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const ExecuteFunction = ({ myFunction }) => { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | }; 10 | 11 | export default ExecuteFunction; 12 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/components/Fragment.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Fragment = () => { 4 | return ( 5 | <> 6 |
7 |

Temos dois elementos pai

8 |
9 |
10 |

Este também é

11 |
12 | 13 | ); 14 | }; 15 | 16 | export default Fragment; 17 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/components/ListRender.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | const ListRender = () => { 4 | const [list] = useState(["Matheus", "Pedro", "Josias"]); 5 | const [users, setUsers] = useState([ 6 | { id: 1, name: "Matheus", age: 31 }, 7 | { id: 2, name: "Jones", age: 19 }, 8 | { id: 3, name: "Scorpion", age: 201 }, 9 | ]); 10 | 11 | const deleteRandom = () => { 12 | const randomNumber = Math.floor(Math.random() * 4); 13 | 14 | setUsers((prevUsers) => { 15 | return prevUsers.filter((user) => randomNumber !== user.id); 16 | }); 17 | }; 18 | 19 | return ( 20 |
21 | {/* render sem key primeiramente */} 22 |
    23 | {list.map((item, i) => ( 24 |
  • {item}
  • 25 | ))} 26 |
27 |
    28 | {users.map((user) => ( 29 |
  • 30 | {user.name} - {user.age} anos 31 |
  • 32 | ))} 33 |
34 | 35 |
36 | ); 37 | }; 38 | 39 | export default ListRender; 40 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/components/ManageData.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | const ManageData = () => { 4 | const someData = 10; 5 | 6 | const [anotherNumber, setAnotherNumber] = useState(15); 7 | 8 | return ( 9 |
10 |
11 |

Valor: {someData}

12 | 13 |
14 |
15 |

Valor: {anotherNumber}

16 | 17 |
18 |
19 | ); 20 | }; 21 | 22 | export default ManageData; 23 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/components/MessageState.js: -------------------------------------------------------------------------------- 1 | const MessageState = ({ msg }) => { 2 | return ( 3 |
4 |

A mensagem é: {msg}

5 |
6 | ); 7 | }; 8 | 9 | export default MessageState; 10 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/components/ShowUserName.js: -------------------------------------------------------------------------------- 1 | const ShowUserName = (props) => { 2 | return ( 3 |
4 |

O nome do usuário é: {props.name}

5 |
6 | ); 7 | }; 8 | 9 | export default ShowUserName; 10 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/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 | padding-bottom: 500px; 10 | } 11 | 12 | code { 13 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 14 | monospace; 15 | } 16 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/index.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /3_AVANCANDO_NO_REACT/avancando/src/setupTests.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/.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 | -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "challengecss", 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 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-scripts": "5.0.0", 12 | "web-vitals": "^2.1.4" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": [ 22 | "react-app", 23 | "react-app/jest" 24 | ] 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/4_CSS_REACT/challengecss/public/favicon.ico -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/4_CSS_REACT/challengecss/public/logo192.png -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/4_CSS_REACT/challengecss/public/logo512.png -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/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 | -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .car-container { 6 | display: flex; 7 | justify-content: center; 8 | } 9 | 10 | .car-container div { 11 | margin: 50px; 12 | } 13 | -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/src/App.js: -------------------------------------------------------------------------------- 1 | import logo from "./logo.svg"; 2 | import "./App.css"; 3 | import Car from "./components/Car"; 4 | 5 | function App() { 6 | const myCars = [ 7 | { name: "Fusca", km: 10000, color: "Branca" }, 8 | { name: "Polo", km: 32000, color: "Cinza" }, 9 | { name: "Onix", km: 0, color: "Preto" }, 10 | ]; 11 | return ( 12 |
13 |

Showroom de carros

14 |
15 | {myCars.map((car) => ( 16 | 17 | ))} 18 |
19 |
20 | ); 21 | } 22 | 23 | export default App; 24 | -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/src/components/Car.js: -------------------------------------------------------------------------------- 1 | import styles from "./Car.module.css"; 2 | 3 | const Car = ({ car }) => { 4 | return ( 5 |
6 |

{car.name}

7 |

KM: {car.km}

8 |

Cor: {car.color}

9 |
10 | ); 11 | }; 12 | 13 | export default Car; 14 | -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/src/components/Car.module.css: -------------------------------------------------------------------------------- 1 | .card { 2 | border: 1px solid purple; 3 | border-radius: 10px; 4 | padding: 10px 50px; 5 | } 6 | -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 0; 3 | margin: 0; 4 | font-family: Verdana; 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/src/index.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /4_CSS_REACT/challengecss/src/setupTests.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/.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 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "css", 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 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-scripts": "5.0.0", 12 | "web-vitals": "^2.1.4" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": [ 22 | "react-app", 23 | "react-app/jest" 24 | ] 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/4_CSS_REACT/css/public/favicon.ico -------------------------------------------------------------------------------- /4_CSS_REACT/css/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/4_CSS_REACT/css/public/logo192.png -------------------------------------------------------------------------------- /4_CSS_REACT/css/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/4_CSS_REACT/css/public/logo512.png -------------------------------------------------------------------------------- /4_CSS_REACT/css/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 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/src/App.js: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | import MyComponent from "./components/MyComponent"; 3 | import Title from "./components/Title"; 4 | 5 | function App() { 6 | const n = 15; 7 | 8 | const redTitle = true; 9 | 10 | return ( 11 |
12 | {/* Global css */} 13 |

React com CSS

14 | {/* Component css */} 15 |

Este parágrafo pegou estilo do componente!

16 | 17 | 18 | {/* Inline css */} 19 |

26 | Este elemento foi estilizado inline 27 |

28 | {/* Dinâmico css */} 29 |

10 ? { color: "purple" } : { color: "magenta" }}> 30 | CSS dinâmico 31 |

32 |

33 | CSS dinâmico 2 34 |

35 | {/* classe dinâmica */} 36 |

37 | Este título vai ter uma classe dinâmica 38 |

39 | {/* CSS modules */} 40 | 41 | <h1 className="title">Este não recebe título do CSS modules</h1> 42 | </div> 43 | ); 44 | } 45 | 46 | export default App; 47 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(<App />); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/src/components/MyComponent.css: -------------------------------------------------------------------------------- 1 | p { 2 | color: green; 3 | border-bottom: 1px solid #000; 4 | } 5 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/src/components/MyComponent.js: -------------------------------------------------------------------------------- 1 | import "./MyComponent.css"; 2 | 3 | const MyComponent = () => { 4 | return ( 5 | <div> 6 | <h1>CSS de componente</h1> 7 | <p>Este parágrafo vai ter um estilo</p> 8 | </div> 9 | ); 10 | }; 11 | 12 | export default MyComponent; 13 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/src/components/Title.js: -------------------------------------------------------------------------------- 1 | import styles from "./Title.module.css"; 2 | 3 | const Title = () => { 4 | return <h1 className={styles.title}>Meu título!</h1>; 5 | }; 6 | 7 | export default Title; 8 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/src/components/Title.module.css: -------------------------------------------------------------------------------- 1 | .title { 2 | color: orange; 3 | background-color: #000; 4 | padding: 10px; 5 | } 6 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: Helvetica; 5 | padding-bottom: 500px; 6 | } 7 | 8 | h1 { 9 | color: red; 10 | } 11 | 12 | /* dynamic class */ 13 | .red-title { 14 | font-size: 50px; 15 | color: red; 16 | } 17 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/src/index.js: -------------------------------------------------------------------------------- 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 | <React.StrictMode> 9 | <App /> 10 | </React.StrictMode>, 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 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /4_CSS_REACT/css/src/setupTests.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /5_FORM_EM_REACT/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 | -------------------------------------------------------------------------------- /5_FORM_EM_REACT/forms/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "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.2", 8 | "@testing-library/user-event": "^13.5.0", 9 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-scripts": "5.0.0", 12 | "web-vitals": "^2.1.4" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": [ 22 | "react-app", 23 | "react-app/jest" 24 | ] 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /5_FORM_EM_REACT/forms/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/5_FORM_EM_REACT/forms/public/favicon.ico -------------------------------------------------------------------------------- /5_FORM_EM_REACT/forms/public/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8" /> 5 | <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> 6 | <meta name="viewport" content="width=device-width, initial-scale=1" /> 7 | <meta name="theme-color" content="#000000" /> 8 | <meta 9 | name="description" 10 | content="Web site created using create-react-app" 11 | /> 12 | <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> 13 | <!-- 14 | manifest.json provides metadata used when your web app is installed on a 15 | user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ 16 | --> 17 | <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> 18 | <!-- 19 | Notice the use of %PUBLIC_URL% in the tags above. 20 | It will be replaced with the URL of the `public` folder during the build. 21 | Only files inside the `public` folder can be referenced from the HTML. 22 | 23 | Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will 24 | work correctly both with client-side routing and a non-root public URL. 25 | Learn how to configure a non-root public URL by running `npm run build`. 26 | --> 27 | <title>React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /5_FORM_EM_REACT/forms/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/5_FORM_EM_REACT/forms/public/logo192.png -------------------------------------------------------------------------------- /5_FORM_EM_REACT/forms/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/5_FORM_EM_REACT/forms/public/logo512.png -------------------------------------------------------------------------------- /5_FORM_EM_REACT/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 | -------------------------------------------------------------------------------- /5_FORM_EM_REACT/forms/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /5_FORM_EM_REACT/forms/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /5_FORM_EM_REACT/forms/src/App.js: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | import MyForm from "./components/MyForm"; 3 | 4 | function App() { 5 | return ( 6 |
7 |

Forms

8 | 9 |
10 | ); 11 | } 12 | 13 | export default App; 14 | -------------------------------------------------------------------------------- /5_FORM_EM_REACT/forms/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /5_FORM_EM_REACT/forms/src/components/MyForm.css: -------------------------------------------------------------------------------- 1 | form { 2 | width: 500px; 3 | margin: 0 auto; 4 | text-align: left; 5 | } 6 | 7 | input, 8 | textarea, 9 | select { 10 | display: block; 11 | margin-top: 10px; 12 | } 13 | -------------------------------------------------------------------------------- /5_FORM_EM_REACT/forms/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 | -------------------------------------------------------------------------------- /5_FORM_EM_REACT/forms/src/index.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /5_FORM_EM_REACT/forms/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /5_FORM_EM_REACT/forms/src/setupTests.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/.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 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secretword", 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 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-scripts": "5.0.0", 12 | "web-vitals": "^2.1.4" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": [ 22 | "react-app", 23 | "react-app/jest" 24 | ] 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/6_PROJETO_SECRET_WORD/secretword/public/favicon.ico -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/6_PROJETO_SECRET_WORD/secretword/public/logo192.png -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/6_PROJETO_SECRET_WORD/secretword/public/logo512.png -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/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 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/src/components/Game.css: -------------------------------------------------------------------------------- 1 | .game h1 { 2 | font-size: 2.5em; 3 | } 4 | 5 | .points span { 6 | font-weight: bold; 7 | } 8 | 9 | .tip span { 10 | color: #ecfa00; 11 | } 12 | 13 | .wordContainer { 14 | margin: 1.5em; 15 | padding: 1.5em; 16 | border: 20px solid #ecfa00; 17 | display: flex; 18 | } 19 | 20 | .letter, 21 | .blankSquare { 22 | font-size: 70px; 23 | line-height: 1.5; 24 | border: 3px solid #000; 25 | height: 100px; 26 | width: 100px; 27 | text-transform: uppercase; 28 | background-color: #fff; 29 | color: #000; 30 | font-weight: bold; 31 | } 32 | 33 | .letterContainer p { 34 | margin-bottom: 1.2em; 35 | } 36 | 37 | .letterContainer form { 38 | display: flex; 39 | align-items: center; 40 | justify-content: center; 41 | } 42 | 43 | .letterContainer input { 44 | height: 50px; 45 | width: 50px; 46 | font-size: 2em; 47 | text-align: center; 48 | margin-right: 1em; 49 | } 50 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/src/components/GameOver.css: -------------------------------------------------------------------------------- 1 | h2 span { 2 | color: #ecfa00; 3 | font-size: 1.5em; 4 | } 5 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/src/components/GameOver.js: -------------------------------------------------------------------------------- 1 | import "./GameOver.css"; 2 | 3 | const GameOver = ({ retry, score }) => { 4 | return ( 5 |
6 |

Fim de jogo!

7 |

8 | A sua pontuação foi: {score}! 9 |

10 | 11 |
12 | ); 13 | }; 14 | 15 | export default GameOver; 16 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/src/components/StartScreen.css: -------------------------------------------------------------------------------- 1 | .start h1 { 2 | font-size: 3.5em; 3 | } 4 | 5 | .start p { 6 | margin-bottom: 2em; 7 | color: #ecfa00; 8 | } 9 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/src/components/StartScreen.js: -------------------------------------------------------------------------------- 1 | import "./StartScreen.css"; 2 | 3 | const GameStart = ({ startGame }) => { 4 | return ( 5 |
6 |

Secret Word

7 |

Clique no botão abaixo para começar a jogar

8 | 9 |
10 | ); 11 | }; 12 | 13 | export default GameStart; 14 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/src/data/words.js: -------------------------------------------------------------------------------- 1 | export const wordsList = { 2 | carro: ["Motor", "Porta", "Capô", "Pneu", "Antena"], 3 | fruta: ["Banana", "Maçã", "Pêra", "Mamão", "Laranja"], 4 | corpo: ["Braço", "Perna", "Cérebro", "Pescoço", "Olhos"], 5 | computador: ["Mouse", "Teclado", "Monitor", "Gabinete"], 6 | programação: ["Linguagem", "Framework", "JavaScript", "React"], 7 | alimento: ["Arroz", "Feijão", "Carne", "Leite", "Ovo"], 8 | }; 9 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/src/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | font-family: Helvetica; 8 | margin: 0; 9 | padding: 0; 10 | background: rgb(9, 35, 175); 11 | background: linear-gradient( 12 | 180deg, 13 | rgba(9, 35, 175, 1) 0%, 14 | rgba(0, 0, 0, 1) 100% 15 | ); 16 | color: #fff; 17 | } 18 | 19 | button { 20 | background-color: #1646a0; 21 | color: #fff; 22 | padding: 0 45px; 23 | border: 2px solid #fff; 24 | border-radius: 25px; 25 | height: 50px; 26 | text-transform: uppercase; 27 | font-weight: bold; 28 | font-size: 1.2em; 29 | cursor: pointer; 30 | transition: 0.4s; 31 | } 32 | 33 | button:hover { 34 | background: #0923af; 35 | } 36 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/src/index.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /6_PROJETO_SECRET_WORD/secretword/src/setupTests.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/.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 | -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/data/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "products": [ 3 | { 4 | "id": 1, 5 | "name": "Camisa", 6 | "price": 59.9 7 | }, 8 | { 9 | "id": 2, 10 | "name": "Calça vermelha", 11 | "price": 90 12 | }, 13 | { 14 | "id": 3, 15 | "name": "Boné aba reta", 16 | "price": 29.9 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/data/db.json.backup: -------------------------------------------------------------------------------- 1 | { 2 | "products": [ 3 | { 4 | "id": 1, 5 | "name": "Camisa", 6 | "price": 59.9 7 | }, 8 | { 9 | "id": 2, 10 | "name": "Calça vermelha", 11 | "price": 90 12 | }, 13 | { 14 | "id": 3, 15 | "name": "Boné aba reta", 16 | "price": 29.9 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "httpreact", 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 | "json-server": "^0.17.0", 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2", 12 | "react-scripts": "5.0.0", 13 | "web-vitals": "^2.1.4" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject", 20 | "server": "json-server --watch data/db.json" 21 | }, 22 | "eslintConfig": { 23 | "extends": [ 24 | "react-app", 25 | "react-app/jest" 26 | ] 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/7_REQ_HTTP_REACT/httpreact/public/favicon.ico -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/7_REQ_HTTP_REACT/httpreact/public/logo192.png -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/7_REQ_HTTP_REACT/httpreact/public/logo512.png -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/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 | -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | 40 | .add-product { 41 | border-top: 1px solid #000; 42 | } 43 | 44 | form { 45 | display: flex; 46 | flex-direction: column; 47 | align-items: center; 48 | justify-content: center; 49 | } 50 | 51 | form input { 52 | display: flex; 53 | flex-direction: column; 54 | margin-bottom: 15px; 55 | } 56 | -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/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 | -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/src/index.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /7_REQ_HTTP_REACT/httpreact/src/setupTests.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/.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 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/data/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "products": [ 3 | { 4 | "id": 1, 5 | "name": "Camisa", 6 | "price": 59.9 7 | }, 8 | { 9 | "id": 2, 10 | "name": "Calça vermelha", 11 | "price": 90 12 | }, 13 | { 14 | "id": 3, 15 | "name": "Boné aba reta", 16 | "price": 29.9 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/data/db.json.backup: -------------------------------------------------------------------------------- 1 | { 2 | "products": [ 3 | { 4 | "id": 1, 5 | "name": "Camisa", 6 | "price": 59.9 7 | }, 8 | { 9 | "id": 2, 10 | "name": "Calça vermelha", 11 | "price": 90 12 | }, 13 | { 14 | "id": 3, 15 | "name": "Boné aba reta", 16 | "price": 29.9 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactrouter", 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 | "json-server": "^0.17.0", 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2", 12 | "react-router-dom": "^6.2.1", 13 | "react-scripts": "5.0.0", 14 | "web-vitals": "^2.1.4" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject", 21 | "server": "json-server --watch data/db.json" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/8_REACT_ROUTER/reactrouter/public/favicon.ico -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/8_REACT_ROUTER/reactrouter/public/logo192.png -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/8_REACT_ROUTER/reactrouter/public/logo512.png -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/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 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/App.js: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | 3 | // 1 - config react router, sem links 4 | import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; 5 | 6 | // pages 7 | import Home from "./pages/Home"; 8 | import About from "./pages/About"; 9 | 10 | // 2 - adicionando links 11 | // components 12 | import Navbar from "./components/Navbar"; 13 | import Product from "./pages/Product"; 14 | import Info from "./pages/Info"; 15 | import NotFound from "./pages/NotFound"; 16 | import Search from "./pages/Search"; 17 | import { SearchForm } from "./components/SearchForm"; 18 | 19 | function App() { 20 | return ( 21 |
22 |

React Router

23 | 24 | 25 | {/* 9 - search */} 26 | 27 | 28 | } /> 29 | } /> 30 | {/* 6 - nested route */} 31 | } /> 32 | {/* 4 - rota dinamica */} 33 | } /> 34 | 35 | {/* 9 search params */} 36 | } /> 37 | {/* 10 - redirect */} 38 | } /> 39 | {/* 7 - no match route */} 40 | } /> 41 | 42 | 43 |
44 | ); 45 | } 46 | 47 | export default App; 48 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/components/Navbar.css: -------------------------------------------------------------------------------- 1 | nav { 2 | display: flex; 3 | justify-content: center; 4 | } 5 | 6 | nav a { 7 | margin: 0 10px; 8 | padding: 5px; 9 | text-decoration: none; 10 | color: #000; 11 | } 12 | 13 | nav a:hover { 14 | color: #ccc; 15 | } 16 | 17 | .active { 18 | background-color: #000; 19 | color: #fff; 20 | } 21 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import { Link, NavLink } from "react-router-dom"; 2 | 3 | import "./Navbar.css"; 4 | 5 | const Navbar = () => { 6 | return ( 7 | 24 | ); 25 | }; 26 | 27 | export default Navbar; 28 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/components/SearchForm.js: -------------------------------------------------------------------------------- 1 | import { useNavigate } from "react-router-dom"; 2 | 3 | import { useState } from "react"; 4 | 5 | export const SearchForm = () => { 6 | const navigate = useNavigate(); 7 | const [query, setQuery] = useState(); 8 | 9 | const handleSubmit = (e) => { 10 | e.preventDefault(); 11 | 12 | navigate("/search?q=" + query); 13 | }; 14 | return ( 15 |
16 | setQuery(e.target.value)} /> 17 | 18 |
19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/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 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/index.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/pages/About.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const About = () => { 4 | return ( 5 |
6 |

About

7 |
8 | ); 9 | }; 10 | 11 | export default About; 12 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/pages/Home.css: -------------------------------------------------------------------------------- 1 | .products { 2 | display: flex; 3 | justify-content: center; 4 | flex-wrap: wrap; 5 | } 6 | 7 | .products li { 8 | border: 1px solid #efefef; 9 | border-radius: 5px; 10 | padding: 10px; 11 | text-align: center; 12 | list-style: none; 13 | margin: 0 10px; 14 | width: 25%; 15 | } 16 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom"; 2 | import { useFetch } from "../hooks/useFetch"; 3 | 4 | import "./Home.css"; 5 | 6 | const Home = () => { 7 | // 3 - carregamento de dados 8 | const url = "http://localhost:3000/products"; 9 | 10 | const { data: items, loading, error } = useFetch(url); 11 | 12 | return ( 13 |
14 |

Produtos

15 | {loading &&

Carregando dados...

} 16 | {error &&

{error}

} 17 |
    18 | {items && 19 | items.map((product) => ( 20 |
  • 21 |

    {product.name}

    22 |

    R$: {product.price}

    23 | {/* 4 - rota dinamica */} 24 | Detalhes 25 |
  • 26 | ))} 27 |
28 |
29 | ); 30 | }; 31 | 32 | export default Home; 33 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/pages/Info.js: -------------------------------------------------------------------------------- 1 | import { useParams } from "react-router-dom"; 2 | 3 | const Info = () => { 4 | const { id } = useParams(); 5 | 6 | return ( 7 |
8 |

Informações sobre o produto: {id}

9 |
10 | ); 11 | }; 12 | 13 | export default Info; 14 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/pages/NotFound.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const NotFound = () => { 4 | return ( 5 |
6 |

404

7 |

Não há nada aqui =(

8 |
9 | ); 10 | }; 11 | 12 | export default NotFound; 13 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/pages/Product.js: -------------------------------------------------------------------------------- 1 | import { useFetch } from "../hooks/useFetch"; 2 | 3 | import { useParams, Link } from "react-router-dom"; 4 | 5 | const Product = () => { 6 | //4 - rota dinamica 7 | const { id } = useParams(); 8 | 9 | //5 - carregamento de dados 10 | const url = "http://localhost:3000/products/" + id; 11 | 12 | const { data: product, loading, error } = useFetch(url); 13 | 14 | return ( 15 | <> 16 |

ID do produto: {id}

17 | 18 | {error &&

Ocorreu um erro...

} 19 | {loading &&

Carregando produto...

} 20 | {product && ( 21 |
22 |

{product.name}

23 |

R${product.price}

24 | {/* 6 - nested routes */} 25 | Mais informações 26 |
27 | )} 28 | 29 | ); 30 | }; 31 | 32 | export default Product; 33 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/pages/Search.js: -------------------------------------------------------------------------------- 1 | import { useSearchParams, Link } from "react-router-dom"; 2 | 3 | import { useFetch } from "../hooks/useFetch"; 4 | 5 | const Search = () => { 6 | let [searchParams] = useSearchParams(); 7 | 8 | const url = "http://localhost:3000/products?" + searchParams; 9 | 10 | const { data: items, loading, error } = useFetch(url); 11 | 12 | return ( 13 |
14 |

Resultados disponíveis:

15 |
    16 | {items && 17 | items.map((product) => ( 18 |
  • 19 |

    {product.name}

    20 |

    R$: {product.price}

    21 | Detalhes 22 |
  • 23 | ))} 24 |
25 |
26 | ); 27 | }; 28 | 29 | export default Search; 30 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /8_REACT_ROUTER/reactrouter/src/setupTests.js: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /9_CONTEXT/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 | -------------------------------------------------------------------------------- /9_CONTEXT/context/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "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.3", 8 | "@testing-library/user-event": "^13.5.0", 9 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-router-dom": "^6.2.1", 12 | "react-scripts": "5.0.0", 13 | "web-vitals": "^2.1.4" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /9_CONTEXT/context/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/9_CONTEXT/context/public/favicon.ico -------------------------------------------------------------------------------- /9_CONTEXT/context/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /9_CONTEXT/context/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/9_CONTEXT/context/public/logo192.png -------------------------------------------------------------------------------- /9_CONTEXT/context/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/curso_react/223e5537a1562c6e22c68550e85ed258d6a12f88/9_CONTEXT/context/public/logo512.png -------------------------------------------------------------------------------- /9_CONTEXT/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 | -------------------------------------------------------------------------------- /9_CONTEXT/context/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/App.js: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | 3 | // 1 - config react router, sem links 4 | import { BrowserRouter, Routes, Route } from "react-router-dom"; 5 | 6 | // pages 7 | import Home from "./pages/Home"; 8 | import About from "./pages/About"; 9 | import Products from "./pages/Products"; 10 | 11 | // 2 - adicionando links 12 | // components 13 | import Navbar from "./components/Navbar"; 14 | 15 | function App() { 16 | return ( 17 |
18 |

Context

19 | 20 | 21 | 22 | } /> 23 | } /> 24 | } /> 25 | 26 | 27 |
28 | ); 29 | } 30 | 31 | export default App; 32 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/components/ChangeCounter.js: -------------------------------------------------------------------------------- 1 | // 3 - alterando valor do contador 2 | import { useContext } from "react"; 3 | 4 | import { CounterContext } from "../context/CounterContext"; 5 | import { useCounterContext } from "../hooks/useCounterContext"; 6 | 7 | const ChangeCounter = () => { 8 | // const { counter, setCounter } = useContext(CounterContext); 9 | // 4 - refatorando para hook 10 | const { counter, setCounter } = useCounterContext(); 11 | 12 | return ( 13 |
14 | 17 |
18 | ); 19 | }; 20 | 21 | export default ChangeCounter; 22 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/components/Navbar.css: -------------------------------------------------------------------------------- 1 | nav { 2 | display: flex; 3 | justify-content: center; 4 | } 5 | 6 | nav a { 7 | margin: 0 10px; 8 | padding: 5px; 9 | text-decoration: none; 10 | color: #000; 11 | } 12 | 13 | nav a:hover { 14 | color: #ccc; 15 | } 16 | 17 | .active { 18 | background-color: #000; 19 | color: #fff; 20 | } 21 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import { NavLink } from "react-router-dom"; 2 | 3 | import "./Navbar.css"; 4 | 5 | const Navbar = () => { 6 | return ( 7 | 27 | ); 28 | }; 29 | 30 | export default Navbar; 31 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/context/CounterContext.js: -------------------------------------------------------------------------------- 1 | // 1 - criar contexto 2 | import { createContext, useState } from "react"; 3 | 4 | export const CounterContext = createContext(); 5 | 6 | // 2 - criar provider 7 | export const CounterContextProvider = ({ children }) => { 8 | const [counter, setCounter] = useState(0); 9 | 10 | return ( 11 | 12 | {children} 13 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/context/TitleColorContext.js: -------------------------------------------------------------------------------- 1 | // contexto mais complexo 2 | import { createContext, useReducer, useState } from "react"; 3 | 4 | export const TitleColorContext = createContext(); 5 | 6 | export const titleColorReducer = (state, action) => { 7 | switch (action.type) { 8 | case "RED": 9 | return { ...state, color: "red" }; 10 | case "BLUE": 11 | return { ...state, color: "blue" }; 12 | default: 13 | return state; 14 | } 15 | }; 16 | 17 | export const TitleColorContextProvider = ({ children }) => { 18 | const [state, dispatch] = useReducer(titleColorReducer, { 19 | color: "purple", 20 | }); 21 | 22 | console.log("Title Color Context:", state); 23 | 24 | return ( 25 | 26 | {children} 27 | 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/hooks/useCounterContext.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { CounterContext } from "../context/CounterContext"; 3 | 4 | export const useCounterContext = () => { 5 | const context = useContext(CounterContext); 6 | 7 | if (!context) { 8 | console.log("Contexto não encontrado."); 9 | } 10 | 11 | return context; 12 | }; 13 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/hooks/useTitleColorContext.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { TitleColorContext } from "../context/TitleColorContext"; 3 | 4 | export const useTitleColorContext = () => { 5 | const context = useContext(TitleColorContext); 6 | 7 | if (!context) { 8 | console.log("Contexto não encontrado."); 9 | } 10 | 11 | return context; 12 | }; 13 | -------------------------------------------------------------------------------- /9_CONTEXT/context/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 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/index.js: -------------------------------------------------------------------------------- 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 | import { CounterContextProvider } from "./context/CounterContext"; 7 | import { TitleColorContextProvider } from "./context/TitleColorContext"; 8 | 9 | ReactDOM.render( 10 | 11 | {/* 2 - criando provider */} 12 | 13 | 14 | 15 | 16 | 17 | , 18 | document.getElementById("root") 19 | ); 20 | 21 | // If you want to start measuring performance in your app, pass a function 22 | // to log results (for example: reportWebVitals(console.log)) 23 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 24 | reportWebVitals(); 25 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/pages/About.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | 3 | import { CounterContext } from "../context/CounterContext"; 4 | 5 | import { useTitleColorContext } from "../hooks/useTitleColorContext"; 6 | 7 | const About = () => { 8 | const { counter } = useContext(CounterContext); 9 | 10 | // 5 - contexto mais complexo 11 | const { color } = useTitleColorContext(); 12 | 13 | return ( 14 |
15 |

About

16 |

Valor contador: {counter}

17 |
18 | ); 19 | }; 20 | 21 | export default About; 22 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/pages/Home.css: -------------------------------------------------------------------------------- 1 | .products { 2 | display: flex; 3 | justify-content: center; 4 | flex-wrap: wrap; 5 | } 6 | 7 | .products li { 8 | border: 1px solid #efefef; 9 | border-radius: 5px; 10 | padding: 10px; 11 | text-align: center; 12 | list-style: none; 13 | margin: 0 10px; 14 | width: 25%; 15 | } 16 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import "./Home.css"; 2 | 3 | import { useContext } from "react"; 4 | 5 | import { CounterContext } from "../context/CounterContext"; 6 | import ChangeCounter from "../components/ChangeCounter"; 7 | 8 | // 4 - refatorando com hook 9 | import { useCounterContext } from "../hooks/useCounterContext"; 10 | import { useTitleColorContext } from "../hooks/useTitleColorContext"; 11 | 12 | const Home = () => { 13 | // 2 - criar provider 14 | // const { counter } = useContext(CounterContext); 15 | const { counter } = useCounterContext(); 16 | 17 | // 5 - contexto mais complexo 18 | const { color, dispatch } = useTitleColorContext(); 19 | 20 | // 6 - alterando contexto complexo 21 | const setTitleColor = (color) => { 22 | dispatch({ type: color }); 23 | }; 24 | 25 | return ( 26 |
27 |

Home

28 |

Valor contador: {counter}

29 | {/* 3 - alterar o valor do contexto */} 30 | 31 |
32 | 33 | 34 |
35 |
36 | ); 37 | }; 38 | 39 | export default Home; 40 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/pages/Products.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | 3 | import { CounterContext } from "../context/CounterContext"; 4 | 5 | const Products = () => { 6 | const { counter } = useContext(CounterContext); 7 | 8 | return ( 9 |
10 |

Lista de produtos

11 |

Valor contador: {counter}

12 |
13 | ); 14 | }; 15 | 16 | export default Products; 17 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /9_CONTEXT/context/src/setupTests.js: -------------------------------------------------------------------------------- 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 | --------------------------------------------------------------------------------