├── 75-76-basic-blog-project ├── after │ ├── api │ │ ├── .gitignore │ │ └── package.json │ └── client │ │ ├── .env.development │ │ ├── src │ │ ├── api │ │ │ ├── base.js │ │ │ ├── todos.js │ │ │ ├── comments.js │ │ │ ├── posts.js │ │ │ └── users.js │ │ ├── components │ │ │ ├── TodoItem.jsx │ │ │ └── PostCard.jsx │ │ ├── main.jsx │ │ ├── pages │ │ │ ├── TodoList.jsx │ │ │ ├── PostList.jsx │ │ │ ├── UserList.jsx │ │ │ ├── Post.jsx │ │ │ └── User.jsx │ │ ├── layouts │ │ │ └── RootLayout.jsx │ │ └── router.jsx │ │ ├── vite.config.js │ │ ├── .gitignore │ │ ├── index.html │ │ ├── package.json │ │ └── public │ │ └── vite.svg └── before │ ├── api │ ├── .gitignore │ └── package.json │ └── client │ ├── todos.html │ └── post.html ├── 78-79-advanced-blog-project ├── after │ ├── api │ │ ├── .gitignore │ │ └── package.json │ └── client │ │ ├── .env.development │ │ ├── src │ │ ├── api │ │ │ ├── base.js │ │ │ ├── todos.js │ │ │ ├── comments.js │ │ │ ├── users.js │ │ │ └── posts.js │ │ ├── components │ │ │ ├── TodoItem.jsx │ │ │ ├── FormGroup.jsx │ │ │ ├── PostCard.jsx │ │ │ └── PostForm.jsx │ │ ├── main.jsx │ │ ├── pages │ │ │ ├── TodoList.jsx │ │ │ ├── UserList.jsx │ │ │ ├── NewPost.jsx │ │ │ ├── Post.jsx │ │ │ ├── User.jsx │ │ │ ├── EditPost.jsx │ │ │ └── PostList.jsx │ │ ├── layouts │ │ │ └── RootLayout.jsx │ │ └── router.jsx │ │ ├── vite.config.js │ │ ├── .gitignore │ │ ├── index.html │ │ ├── package.json │ │ └── public │ │ └── vite.svg └── before │ ├── api │ ├── .gitignore │ └── package.json │ ├── client │ ├── .env.development │ ├── src │ │ ├── api │ │ │ ├── base.js │ │ │ ├── todos.js │ │ │ ├── comments.js │ │ │ ├── posts.js │ │ │ └── users.js │ │ ├── components │ │ │ ├── TodoItem.jsx │ │ │ └── PostCard.jsx │ │ ├── main.jsx │ │ ├── pages │ │ │ ├── TodoList.jsx │ │ │ ├── PostList.jsx │ │ │ ├── UserList.jsx │ │ │ ├── Post.jsx │ │ │ └── User.jsx │ │ ├── layouts │ │ │ └── RootLayout.jsx │ │ └── router.jsx │ ├── vite.config.js │ ├── .gitignore │ ├── index.html │ ├── package.json │ └── public │ │ └── vite.svg │ ├── README.md │ └── html │ ├── new-post.html │ └── edit-post.html ├── 37-38-user-list ├── after │ ├── src │ │ ├── User.jsx │ │ ├── main.jsx │ │ └── App.jsx │ ├── vite.config.js │ ├── .gitignore │ ├── index.html │ └── package.json └── before │ ├── loading.html │ ├── app.html │ └── README.md ├── 15-16-user-card ├── before │ ├── user.json │ ├── user.css │ ├── user.html │ └── README.md └── after │ ├── src │ ├── user.json │ ├── main.jsx │ ├── UserCard.jsx │ ├── user.css │ ├── UserCardClass.jsx │ └── App.jsx │ ├── vite.config.js │ ├── .gitignore │ ├── index.html │ └── package.json ├── 21-22-name-counter ├── after │ ├── src │ │ ├── App.jsx │ │ ├── main.jsx │ │ ├── FunctionComponent.jsx │ │ └── ClassComponent.jsx │ ├── vite.config.js │ ├── .gitignore │ ├── index.html │ ├── package.json │ └── public │ │ └── vite.svg └── before │ └── README.md ├── 58-59-basic-form-validation-project ├── after │ ├── src │ │ ├── App.jsx │ │ ├── main.jsx │ │ ├── validators.js │ │ ├── styles.css │ │ ├── StateForm.jsx │ │ └── RefForm.jsx │ ├── vite.config.js │ ├── .gitignore │ ├── index.html │ └── package.json └── before │ ├── app.html │ ├── styles.css │ └── README.md ├── 20-array-component ├── src │ ├── main.jsx │ └── App.jsx ├── vite.config.js ├── .gitignore ├── index.html ├── package.json ├── README.md └── public │ └── vite.svg ├── 27-28-use-effect-exercises ├── after │ ├── src │ │ ├── main.jsx │ │ ├── App.jsx │ │ └── Child.jsx │ ├── vite.config.js │ ├── .gitignore │ ├── index.html │ ├── package.json │ └── public │ │ └── vite.svg └── before │ ├── src │ ├── main.jsx │ ├── App.jsx │ └── Child.jsx │ ├── vite.config.js │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── public │ └── vite.svg │ └── README.md ├── 30-31-lifecycle-method-exercises ├── after │ ├── src │ │ ├── main.jsx │ │ ├── App.jsx │ │ └── Child.jsx │ ├── vite.config.js │ ├── .gitignore │ ├── index.html │ ├── package.json │ └── public │ │ └── vite.svg └── before │ ├── src │ ├── main.jsx │ ├── App.jsx │ └── Child.jsx │ ├── vite.config.js │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── README.md │ └── public │ └── vite.svg ├── 41-42-simple-todo-list ├── after │ ├── vite.config.js │ ├── src │ │ ├── main.jsx │ │ ├── TodoItem.jsx │ │ ├── styles.css │ │ └── App.jsx │ ├── .gitignore │ ├── index.html │ └── package.json └── before │ ├── styles.css │ ├── app.html │ └── README.md ├── 49-50-use-fetch-hook ├── after │ ├── vite.config.js │ ├── src │ │ ├── main.jsx │ │ ├── useFetch.js │ │ └── App.jsx │ ├── index.html │ ├── .gitignore │ └── package.json └── before │ ├── vite.config.js │ ├── src │ ├── main.jsx │ └── App.jsx │ ├── index.html │ ├── .gitignore │ ├── package.json │ └── README.md ├── 51-52-use-array-hook ├── after │ ├── vite.config.js │ ├── src │ │ ├── main.jsx │ │ ├── useArray.js │ │ └── App.jsx │ ├── .gitignore │ ├── index.html │ └── package.json └── before │ ├── vite.config.js │ ├── src │ ├── main.jsx │ └── App.jsx │ ├── .gitignore │ ├── index.html │ ├── package.json │ └── README.md ├── 61-react-hook-form-project ├── after │ ├── vite.config.js │ ├── src │ │ ├── main.jsx │ │ ├── FormGroup.jsx │ │ └── styles.css │ ├── .gitignore │ ├── index.html │ └── package.json └── before │ ├── vite.config.js │ ├── src │ ├── main.jsx │ ├── FormGroup.jsx │ ├── validators.js │ ├── styles.css │ └── App.jsx │ ├── .gitignore │ ├── index.html │ └── package.json ├── 68-69-advanced-todo-list ├── after │ ├── vite.config.js │ ├── src │ │ ├── main.jsx │ │ ├── TodoList.jsx │ │ ├── NewTodoForm.jsx │ │ ├── TodoFilterForm.jsx │ │ ├── styles.css │ │ ├── TodoItem.jsx │ │ └── App.jsx │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── README.md │ └── app.html └── before │ ├── vite.config.js │ ├── src │ ├── main.jsx │ ├── TodoItem.jsx │ ├── styles.css │ └── App.jsx │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── README.md │ └── app.html └── 53-54-use-local-storage-hook ├── after ├── vite.config.js ├── src │ ├── main.jsx │ ├── useLocalStorage.js │ └── App.jsx ├── index.html ├── .gitignore └── package.json └── before ├── vite.config.js ├── src ├── main.jsx └── App.jsx ├── index.html ├── .gitignore ├── package.json └── README.md /75-76-basic-blog-project/after/api/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /75-76-basic-blog-project/before/api/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/api/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/api/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/.env.development: -------------------------------------------------------------------------------- 1 | VITE_API_URL=http://localhost:3000 -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/.env.development: -------------------------------------------------------------------------------- 1 | VITE_API_URL=http://localhost:3000 -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/.env.development: -------------------------------------------------------------------------------- 1 | VITE_API_URL=http://localhost:3000 -------------------------------------------------------------------------------- /37-38-user-list/after/src/User.jsx: -------------------------------------------------------------------------------- 1 | export function User({ name }) { 2 | return
  • {name}
  • 3 | } 4 | -------------------------------------------------------------------------------- /15-16-user-card/before/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Kyle Cook", 3 | "age": 27, 4 | "phoneNumber": "123-456-7890", 5 | "address": "123 Main St." 6 | } -------------------------------------------------------------------------------- /15-16-user-card/after/src/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Kyle Cook", 3 | "age": 48, 4 | "phoneNumber": "123-456-7890", 5 | "address": "123 Main St." 6 | } 7 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/api/base.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | 3 | export const baseApi = axios.create({ baseURL: import.meta.env.VITE_API_URL }) 4 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/api/base.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | 3 | export const baseApi = axios.create({ baseURL: import.meta.env.VITE_API_URL }) 4 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/api/base.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | 3 | export const baseApi = axios.create({ baseURL: import.meta.env.VITE_API_URL }) 4 | -------------------------------------------------------------------------------- /21-22-name-counter/after/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { ClassComponent } from "./ClassComponent" 2 | 3 | function App() { 4 | return 5 | } 6 | 7 | export default App 8 | -------------------------------------------------------------------------------- /58-59-basic-form-validation-project/after/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { StateForm } from "./StateForm" 2 | import "./styles.css" 3 | 4 | export default function App() { 5 | return 6 | } 7 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/api/todos.js: -------------------------------------------------------------------------------- 1 | import { baseApi } from "./base" 2 | 3 | export function getTodos(options) { 4 | return baseApi.get("todos", options).then(res => res.data) 5 | } 6 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/components/TodoItem.jsx: -------------------------------------------------------------------------------- 1 | export function TodoItem({ completed, title }) { 2 | return
  • {title}
  • 3 | } 4 | -------------------------------------------------------------------------------- /20-array-component/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render() 6 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/api/todos.js: -------------------------------------------------------------------------------- 1 | import { baseApi } from "./base" 2 | 3 | export function getTodos(options) { 4 | return baseApi.get("todos", options).then(res => res.data) 5 | } 6 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/components/TodoItem.jsx: -------------------------------------------------------------------------------- 1 | export function TodoItem({ completed, title }) { 2 | return
  • {title}
  • 3 | } 4 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/api/todos.js: -------------------------------------------------------------------------------- 1 | import { baseApi } from "./base" 2 | 3 | export function getTodos(options) { 4 | return baseApi.get("todos", options).then(res => res.data) 5 | } 6 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/components/TodoItem.jsx: -------------------------------------------------------------------------------- 1 | export function TodoItem({ completed, title }) { 2 | return
  • {title}
  • 3 | } 4 | -------------------------------------------------------------------------------- /15-16-user-card/after/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render() 6 | -------------------------------------------------------------------------------- /21-22-name-counter/after/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render() 6 | -------------------------------------------------------------------------------- /20-array-component/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /27-28-use-effect-exercises/after/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render() 6 | -------------------------------------------------------------------------------- /27-28-use-effect-exercises/before/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render() 6 | -------------------------------------------------------------------------------- /15-16-user-card/after/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite" 2 | import react from "@vitejs/plugin-react-swc" 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/after/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render() 6 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/before/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render() 6 | -------------------------------------------------------------------------------- /37-38-user-list/after/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /21-22-name-counter/after/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /27-28-use-effect-exercises/after/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /41-42-simple-todo-list/after/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /49-50-use-fetch-hook/after/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /49-50-use-fetch-hook/before/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /51-52-use-array-hook/after/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /51-52-use-array-hook/before/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /27-28-use-effect-exercises/before/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/after/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /61-react-hook-form-project/after/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /61-react-hook-form-project/before/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/after/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/before/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/api/comments.js: -------------------------------------------------------------------------------- 1 | import { baseApi } from "./base" 2 | 3 | export function getComments(postId, options) { 4 | return baseApi.get(`posts/${postId}/comments`, options).then(res => res.data) 5 | } 6 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/api/comments.js: -------------------------------------------------------------------------------- 1 | import { baseApi } from "./base" 2 | 3 | export function getComments(postId, options) { 4 | return baseApi.get(`posts/${postId}/comments`, options).then(res => res.data) 5 | } 6 | -------------------------------------------------------------------------------- /53-54-use-local-storage-hook/after/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /53-54-use-local-storage-hook/before/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/api/comments.js: -------------------------------------------------------------------------------- 1 | import { baseApi } from "./base" 2 | 3 | export function getComments(postId, options) { 4 | return baseApi.get(`posts/${postId}/comments`, options).then(res => res.data) 5 | } 6 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/before/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /58-59-basic-form-validation-project/after/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /37-38-user-list/after/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /41-42-simple-todo-list/after/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /49-50-use-fetch-hook/after/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /49-50-use-fetch-hook/before/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /51-52-use-array-hook/after/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /51-52-use-array-hook/before/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/after/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /53-54-use-local-storage-hook/after/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /53-54-use-local-storage-hook/before/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /61-react-hook-form-project/after/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /61-react-hook-form-project/before/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/before/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /58-59-basic-form-validation-project/after/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /61-react-hook-form-project/before/src/FormGroup.jsx: -------------------------------------------------------------------------------- 1 | export function FormGroup({ errors = [], children }) { 2 | return ( 3 |
    0 ? "error" : ""}`}> 4 | {children} 5 | {errors.length > 0 &&
    {errors.join(", ")}
    } 6 |
    7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /61-react-hook-form-project/after/src/FormGroup.jsx: -------------------------------------------------------------------------------- 1 | export function FormGroup({ errorMessage = "", children }) { 2 | return ( 3 |
    0 ? "error" : ""}`}> 4 | {children} 5 | {errorMessage.length > 0 &&
    {errorMessage}
    } 6 |
    7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/api/posts.js: -------------------------------------------------------------------------------- 1 | import { baseApi } from "./base" 2 | 3 | export function getPosts(options) { 4 | return baseApi.get("posts", options).then(res => res.data) 5 | } 6 | 7 | export function getPost(postId, options) { 8 | return baseApi.get(`posts/${postId}`, options).then(res => res.data) 9 | } 10 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/api/users.js: -------------------------------------------------------------------------------- 1 | import { baseApi } from "./base" 2 | 3 | export function getUsers(options) { 4 | return baseApi.get("users", options).then(res => res.data) 5 | } 6 | 7 | export function getUser(userId, options) { 8 | return baseApi.get(`users/${userId}`, options).then(res => res.data) 9 | } 10 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/api/users.js: -------------------------------------------------------------------------------- 1 | import { baseApi } from "./base" 2 | 3 | export function getUsers(options) { 4 | return baseApi.get("users", options).then(res => res.data) 5 | } 6 | 7 | export function getUser(userId, options) { 8 | return baseApi.get(`users/${userId}`, options).then(res => res.data) 9 | } 10 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/api/posts.js: -------------------------------------------------------------------------------- 1 | import { baseApi } from "./base" 2 | 3 | export function getPosts(options) { 4 | return baseApi.get("posts", options).then(res => res.data) 5 | } 6 | 7 | export function getPost(postId, options) { 8 | return baseApi.get(`posts/${postId}`, options).then(res => res.data) 9 | } 10 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/api/users.js: -------------------------------------------------------------------------------- 1 | import { baseApi } from "./base" 2 | 3 | export function getUsers(options) { 4 | return baseApi.get("users", options).then(res => res.data) 5 | } 6 | 7 | export function getUser(userId, options) { 8 | return baseApi.get(`users/${userId}`, options).then(res => res.data) 9 | } 10 | -------------------------------------------------------------------------------- /49-50-use-fetch-hook/after/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite + React 7 | 8 | 9 |
    10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /49-50-use-fetch-hook/before/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite + React 7 | 8 | 9 |
    10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/components/FormGroup.jsx: -------------------------------------------------------------------------------- 1 | export function FormGroup({ children, errorMessage }) { 2 | return ( 3 |
    4 | {children} 5 | {errorMessage != null && ( 6 |
    {errorMessage}
    7 | )} 8 |
    9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /20-array-component/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /15-16-user-card/after/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /37-38-user-list/after/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /53-54-use-local-storage-hook/after/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite + React 7 | 8 | 9 |
    10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /53-54-use-local-storage-hook/before/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite + React 7 | 8 | 9 |
    10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "json-server --watch db.json --port 3000 --host 127.0.0.1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "json-server": "^0.17.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/before/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "json-server --watch db.json --port 3000 --host 127.0.0.1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "json-server": "^0.17.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /21-22-name-counter/after/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /41-42-simple-todo-list/after/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /49-50-use-fetch-hook/after/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /49-50-use-fetch-hook/before/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /51-52-use-array-hook/after/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /51-52-use-array-hook/before/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "json-server --watch db.json --port 3000 --host 127.0.0.1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "json-server": "^0.17.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "json-server --watch db.json --port 3000 --host 127.0.0.1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "json-server": "^0.17.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /27-28-use-effect-exercises/after/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /27-28-use-effect-exercises/before/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /61-react-hook-form-project/after/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /61-react-hook-form-project/before/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/after/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/before/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/after/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /37-38-user-list/before/loading.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

    User List

    11 |

    Loading...

    12 | 13 | 14 | -------------------------------------------------------------------------------- /53-54-use-local-storage-hook/after/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /53-54-use-local-storage-hook/before/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/before/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /58-59-basic-form-validation-project/after/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import "./styles.css" 4 | import { RouterProvider } from "react-router-dom" 5 | import { router } from "./router" 6 | 7 | ReactDOM.createRoot(document.getElementById("root")).render( 8 | 9 | 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import "./styles.css" 4 | import { RouterProvider } from "react-router-dom" 5 | import { router } from "./router" 6 | 7 | ReactDOM.createRoot(document.getElementById("root")).render( 8 | 9 | 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import "./styles.css" 4 | import { RouterProvider } from "react-router-dom" 5 | import { router } from "./router" 6 | 7 | ReactDOM.createRoot(document.getElementById("root")).render( 8 | 9 | 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/after/src/TodoList.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from "react" 2 | import { TodoContext } from "./App" 3 | import { TodoItem } from "./TodoItem" 4 | 5 | export function TodoList() { 6 | const { todos } = useContext(TodoContext) 7 | 8 | return ( 9 |
      10 | {todos.map(todo => { 11 | return 12 | })} 13 |
    14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /20-array-component/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /15-16-user-card/after/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /21-22-name-counter/after/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /37-38-user-list/after/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /41-42-simple-todo-list/after/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /51-52-use-array-hook/after/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /51-52-use-array-hook/before/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/after/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /27-28-use-effect-exercises/after/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /27-28-use-effect-exercises/before/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /61-react-hook-form-project/after/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /61-react-hook-form-project/before/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/before/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/after/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/before/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /27-28-use-effect-exercises/after/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | import { Child } from "./Child" 3 | 4 | export default function App() { 5 | const [show, setShow] = useState(true) 6 | 7 | const childComponent = show ? : null 8 | 9 | return ( 10 |
    11 | 14 | {childComponent} 15 |
    16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /58-59-basic-form-validation-project/after/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
    11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /27-28-use-effect-exercises/before/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | import { Child } from "./Child" 3 | 4 | export default function App() { 5 | const [show, setShow] = useState(true) 6 | 7 | const childComponent = show ? : null 8 | 9 | return ( 10 |
    11 | 14 | {childComponent} 15 |
    16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/after/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | import { Child } from "./Child" 3 | 4 | export default function App() { 5 | const [show, setShow] = useState(true) 6 | 7 | const childComponent = show ? : null 8 | 9 | return ( 10 |
    11 | 14 | {childComponent} 15 |
    16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/before/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | import { Child } from "./Child" 3 | 4 | export default function App() { 5 | const [show, setShow] = useState(true) 6 | 7 | const childComponent = show ? : null 8 | 9 | return ( 10 |
    11 | 14 | {childComponent} 15 |
    16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /15-16-user-card/after/src/UserCard.jsx: -------------------------------------------------------------------------------- 1 | export function UserCard({ name, age, phoneNumber, address }) { 2 | return ( 3 |
    4 |

    {name}

    5 |
    6 |
    Age:
    7 |
    {age}
    8 |
    Phone:
    9 |
    {phoneNumber}
    10 |
    Address:
    11 |
    {address}
    12 |
    13 |
    14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /37-38-user-list/after/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "after", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.27", 17 | "@types/react-dom": "^18.0.10", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.1.0" 20 | } 21 | } -------------------------------------------------------------------------------- /15-16-user-card/after/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "after", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.26", 17 | "@types/react-dom": "^18.0.9", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.0.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /41-42-simple-todo-list/after/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "after", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.27", 17 | "@types/react-dom": "^18.0.10", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.1.0" 20 | } 21 | } -------------------------------------------------------------------------------- /49-50-use-fetch-hook/after/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "before", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.27", 17 | "@types/react-dom": "^18.0.10", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.1.0" 20 | } 21 | } -------------------------------------------------------------------------------- /49-50-use-fetch-hook/before/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "before", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.27", 17 | "@types/react-dom": "^18.0.10", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.1.0" 20 | } 21 | } -------------------------------------------------------------------------------- /51-52-use-array-hook/before/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "before", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.27", 17 | "@types/react-dom": "^18.0.10", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.1.0" 20 | } 21 | } -------------------------------------------------------------------------------- /68-69-advanced-todo-list/after/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "after", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.27", 17 | "@types/react-dom": "^18.0.10", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.1.0" 20 | } 21 | } -------------------------------------------------------------------------------- /68-69-advanced-todo-list/before/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "after", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.27", 17 | "@types/react-dom": "^18.0.10", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.1.0" 20 | } 21 | } -------------------------------------------------------------------------------- /20-array-component/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "current-course-project", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.26", 17 | "@types/react-dom": "^18.0.9", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.0.0" 20 | } 21 | } -------------------------------------------------------------------------------- /27-28-use-effect-exercises/before/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "before", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.26", 17 | "@types/react-dom": "^18.0.9", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.0.0" 20 | } 21 | } -------------------------------------------------------------------------------- /51-52-use-array-hook/after/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "before", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.27", 17 | "@types/react-dom": "^18.0.10", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.1.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /53-54-use-local-storage-hook/after/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "before", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.27", 17 | "@types/react-dom": "^18.0.10", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.1.0" 20 | } 21 | } -------------------------------------------------------------------------------- /53-54-use-local-storage-hook/before/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "before", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.27", 17 | "@types/react-dom": "^18.0.10", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.1.0" 20 | } 21 | } -------------------------------------------------------------------------------- /21-22-name-counter/after/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "current-course-project", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.26", 17 | "@types/react-dom": "^18.0.9", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.0.0" 20 | } 21 | } -------------------------------------------------------------------------------- /27-28-use-effect-exercises/after/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "21-22-name-counter", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.22", 17 | "@types/react-dom": "^18.0.7", 18 | "@vitejs/plugin-react": "^2.2.0", 19 | "vite": "^3.2.0" 20 | } 21 | } -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/before/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "before", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.26", 17 | "@types/react-dom": "^18.0.9", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.0.0" 20 | } 21 | } -------------------------------------------------------------------------------- /58-59-basic-form-validation-project/after/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "after", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.27", 17 | "@types/react-dom": "^18.0.10", 18 | "@vitejs/plugin-react-swc": "^3.0.0", 19 | "vite": "^4.1.0" 20 | } 21 | } -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/after/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "21-22-name-counter", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.22", 17 | "@types/react-dom": "^18.0.7", 18 | "@vitejs/plugin-react": "^2.2.0", 19 | "vite": "^3.2.0" 20 | } 21 | } -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/components/PostCard.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom" 2 | 3 | export function PostCard({ id, title, body }) { 4 | return ( 5 |
    6 |
    {title}
    7 |
    8 |
    {body}
    9 |
    10 |
    11 | 12 | View 13 | 14 |
    15 |
    16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /15-16-user-card/after/src/user.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #F3F3F3; 3 | } 4 | 5 | .card { 6 | background: white; 7 | border: 1px solid #777; 8 | border-radius: .5rem; 9 | box-shadow: 0 5px 10px 0 rgba(0, 0, 0, .15); 10 | max-width: 400px; 11 | padding: 1.5rem; 12 | font-family: Arial; 13 | } 14 | 15 | .name { 16 | margin: 0; 17 | margin-bottom: 2rem; 18 | } 19 | 20 | 21 | .body { 22 | display: grid; 23 | grid-template-columns: auto 1fr; 24 | gap: 1rem .5rem; 25 | padding: 0 1rem; 26 | } 27 | 28 | .label { 29 | font-weight: bold; 30 | } -------------------------------------------------------------------------------- /15-16-user-card/before/user.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #F3F3F3; 3 | } 4 | 5 | .card { 6 | background: white; 7 | border: 1px solid #777; 8 | border-radius: .5rem; 9 | box-shadow: 0 5px 10px 0 rgba(0, 0, 0, .15); 10 | max-width: 400px; 11 | padding: 1.5rem; 12 | font-family: Arial; 13 | } 14 | 15 | .name { 16 | margin: 0; 17 | margin-bottom: 2rem; 18 | } 19 | 20 | 21 | .body { 22 | display: grid; 23 | grid-template-columns: auto 1fr; 24 | gap: 1rem .5rem; 25 | padding: 0 1rem; 26 | } 27 | 28 | .label { 29 | font-weight: bold; 30 | } -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/components/PostCard.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom" 2 | 3 | export function PostCard({ id, title, body }) { 4 | return ( 5 |
    6 |
    {title}
    7 |
    8 |
    {body}
    9 |
    10 |
    11 | 12 | View 13 | 14 |
    15 |
    16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/components/PostCard.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom" 2 | 3 | export function PostCard({ id, title, body }) { 4 | return ( 5 |
    6 |
    {title}
    7 |
    8 |
    {body}
    9 |
    10 |
    11 | 12 | View 13 | 14 |
    15 |
    16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /61-react-hook-form-project/before/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "before", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0", 14 | "react-select": "^5.7.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.27", 18 | "@types/react-dom": "^18.0.10", 19 | "@vitejs/plugin-react-swc": "^3.0.0", 20 | "vite": "^4.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /27-28-use-effect-exercises/before/src/Child.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | 3 | export function Child() { 4 | const [age, setAge] = useState(0) 5 | const [name, setName] = useState("") 6 | 7 | return ( 8 |
    9 | setName(e.target.value)} /> 10 |
    11 |
    12 | 13 | {age} 14 | 15 |
    16 |
    17 | My name is {name} and I am {age} years old. 18 |
    19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "axios": "^1.3.4", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "react-router-dom": "^6.9.0" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.0.27", 19 | "@types/react-dom": "^18.0.10", 20 | "@vitejs/plugin-react-swc": "^3.0.0", 21 | "vite": "^4.1.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /21-22-name-counter/before/README.md: -------------------------------------------------------------------------------- 1 | # Before Getting Started 2 | 3 | There is no starting code for this project since the styling/HTML are not important. You can use whatever HTML you want. 4 | 5 | # Instructions 6 | 7 | 1. Create a new function component that has state for name and age 8 | 2. Create a text input that when updated will update the name state 9 | 3. Create a plus and minus button that will update the age state and display the state between the two buttons 10 | 4. Display the string `My name is {name} and I am {age} years old` in your JSX 11 | 5. Repeat but for a class component instead of a function component -------------------------------------------------------------------------------- /61-react-hook-form-project/after/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "before", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0", 14 | "react-hook-form": "^7.43.3", 15 | "react-select": "^5.7.0" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.0.27", 19 | "@types/react-dom": "^18.0.10", 20 | "@vitejs/plugin-react-swc": "^3.0.0", 21 | "vite": "^4.1.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "axios": "^1.3.4", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "react-router-dom": "^6.9.0" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.0.27", 19 | "@types/react-dom": "^18.0.10", 20 | "@vitejs/plugin-react-swc": "^3.0.0", 21 | "vite": "^4.1.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/api/posts.js: -------------------------------------------------------------------------------- 1 | import { baseApi } from "./base" 2 | 3 | export function getPosts(options) { 4 | return baseApi.get("posts", options).then(res => res.data) 5 | } 6 | 7 | export function getPost(postId, options) { 8 | return baseApi.get(`posts/${postId}`, options).then(res => res.data) 9 | } 10 | 11 | export function createPost(data, options) { 12 | return baseApi.post("posts", data, options).then(res => res.data) 13 | } 14 | 15 | export function updatePost(postId, data, options) { 16 | return baseApi.put(`posts/${postId}`, data, options).then(res => res.data) 17 | } 18 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "axios": "^1.3.4", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "react-router-dom": "^6.9.0" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.0.27", 19 | "@types/react-dom": "^18.0.10", 20 | "@vitejs/plugin-react-swc": "^3.0.0", 21 | "vite": "^4.1.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /41-42-simple-todo-list/after/src/TodoItem.jsx: -------------------------------------------------------------------------------- 1 | export function TodoItem({ id, name, completed, toggleTodo, deleteTodo }) { 2 | return ( 3 |
  • 4 | 13 | 16 |
  • 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/before/src/TodoItem.jsx: -------------------------------------------------------------------------------- 1 | export function TodoItem({ id, name, completed, toggleTodo, deleteTodo }) { 2 | return ( 3 |
  • 4 | 13 | 16 |
  • 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /15-16-user-card/after/src/UserCardClass.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | export class UserCardClass extends React.Component { 4 | render() { 5 | return ( 6 |
    7 |

    {this.props.name}

    8 |
    9 |
    Age:
    10 |
    {this.props.age}
    11 |
    Phone:
    12 |
    {this.props.phoneNumber}
    13 |
    Address:
    14 |
    {this.props.address}
    15 |
    16 |
    17 | ) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /15-16-user-card/after/src/App.jsx: -------------------------------------------------------------------------------- 1 | import "./user.css" 2 | import user from "./user.json" 3 | import { UserCard } from "./UserCard" 4 | import { UserCardClass } from "./UserCardClass" 5 | 6 | export default function App() { 7 | return ( 8 |
    9 | 15 |
    16 | 22 |
    23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /21-22-name-counter/after/src/FunctionComponent.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | 3 | export function FunctionComponent() { 4 | const [name, setName] = useState("") 5 | const [age, setAge] = useState(0) 6 | 7 | return ( 8 |
    9 | setName(e.target.value)} /> 10 |
    11 |
    12 | 13 | {age} 14 | 15 |
    16 |
    17 | My name is {name} and I am {age} years old. 18 |
    19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/pages/TodoList.jsx: -------------------------------------------------------------------------------- 1 | import { useLoaderData } from "react-router-dom" 2 | import { getTodos } from "../api/todos" 3 | import { TodoItem } from "../components/TodoItem" 4 | 5 | function TodoList() { 6 | const todos = useLoaderData() 7 | 8 | return ( 9 | <> 10 |

    Todos

    11 |
      12 | {todos.map(todo => ( 13 | 14 | ))} 15 |
    16 | 17 | ) 18 | } 19 | 20 | function loader({ request: { signal } }) { 21 | return getTodos({ signal }) 22 | } 23 | 24 | export const todoListRoute = { 25 | loader, 26 | element: , 27 | } 28 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/pages/TodoList.jsx: -------------------------------------------------------------------------------- 1 | import { useLoaderData } from "react-router-dom" 2 | import { getTodos } from "../api/todos" 3 | import { TodoItem } from "../components/TodoItem" 4 | 5 | function TodoList() { 6 | const todos = useLoaderData() 7 | 8 | return ( 9 | <> 10 |

    Todos

    11 |
      12 | {todos.map(todo => ( 13 | 14 | ))} 15 |
    16 | 17 | ) 18 | } 19 | 20 | function loader({ request: { signal } }) { 21 | return getTodos({ signal }) 22 | } 23 | 24 | export const todoListRoute = { 25 | loader, 26 | element: , 27 | } 28 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/pages/TodoList.jsx: -------------------------------------------------------------------------------- 1 | import { useLoaderData } from "react-router-dom" 2 | import { getTodos } from "../api/todos" 3 | import { TodoItem } from "../components/TodoItem" 4 | 5 | function TodoList() { 6 | const todos = useLoaderData() 7 | 8 | return ( 9 | <> 10 |

    Todos

    11 |
      12 | {todos.map(todo => ( 13 | 14 | ))} 15 |
    16 | 17 | ) 18 | } 19 | 20 | function loader({ request: { signal } }) { 21 | return getTodos({ signal }) 22 | } 23 | 24 | export const todoListRoute = { 25 | loader, 26 | element: , 27 | } 28 | -------------------------------------------------------------------------------- /15-16-user-card/before/user.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 |
    12 |

    Kyle Cook

    13 |
    14 |
    Age:
    15 |
    27
    16 |
    Phone:
    17 |
    123-456-7890
    18 |
    Address:
    19 |
    123 Main St.
    20 |
    21 |
    22 | 23 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/pages/PostList.jsx: -------------------------------------------------------------------------------- 1 | import { useLoaderData } from "react-router-dom" 2 | import { getPosts } from "../api/posts" 3 | import { PostCard } from "../components/PostCard" 4 | 5 | function PostList() { 6 | const posts = useLoaderData() 7 | 8 | return ( 9 | <> 10 |

    Posts

    11 |
    12 | {posts.map(post => ( 13 | 14 | ))} 15 |
    16 | 17 | ) 18 | } 19 | 20 | function loader({ request: { signal } }) { 21 | return getPosts({ signal }) 22 | } 23 | 24 | export const postListRoute = { 25 | loader, 26 | element: , 27 | } 28 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/pages/PostList.jsx: -------------------------------------------------------------------------------- 1 | import { useLoaderData } from "react-router-dom" 2 | import { getPosts } from "../api/posts" 3 | import { PostCard } from "../components/PostCard" 4 | 5 | function PostList() { 6 | const posts = useLoaderData() 7 | 8 | return ( 9 | <> 10 |

    Posts

    11 |
    12 | {posts.map(post => ( 13 | 14 | ))} 15 |
    16 | 17 | ) 18 | } 19 | 20 | function loader({ request: { signal } }) { 21 | return getPosts({ signal }) 22 | } 23 | 24 | export const postListRoute = { 25 | loader, 26 | element: , 27 | } 28 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/after/src/NewTodoForm.jsx: -------------------------------------------------------------------------------- 1 | import { useContext, useRef } from "react" 2 | import { TodoContext } from "./App" 3 | 4 | export function NewTodoForm() { 5 | const nameRef = useRef() 6 | const { addNewTodo } = useContext(TodoContext) 7 | 8 | function handleSubmit(e) { 9 | e.preventDefault() 10 | if (nameRef.current.value === "") return 11 | 12 | addNewTodo(nameRef.current.value) 13 | 14 | nameRef.current.value = "" 15 | } 16 | 17 | return ( 18 |
    19 | 20 | 21 | 22 |
    23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/after/src/TodoFilterForm.jsx: -------------------------------------------------------------------------------- 1 | export function TodoFilterForm({ 2 | name, 3 | setName, 4 | hideCompleted, 5 | setHideCompleted, 6 | }) { 7 | return ( 8 |
    9 |
    10 | 11 | setName(e.target.value)} 16 | /> 17 |
    18 | 26 |
    27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /37-38-user-list/before/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

    User List

    11 |
      12 |
    • Leanne Graham
    • 13 |
    • Ervin Howell
    • 14 |
    • Clementine Bauch
    • 15 |
    • Patricia Lebsack
    • 16 |
    • Chelsey Dietrich
    • 17 |
    • Mrs. Dennis Schulist
    • 18 |
    • Kurtis Weissnat
    • 19 |
    • Nicholas Runolfsdottir V
    • 20 |
    • Glenna Reichert
    • 21 |
    • Clementina DuBuque
    • 22 |
    23 | 24 | 25 | -------------------------------------------------------------------------------- /53-54-use-local-storage-hook/after/src/useLocalStorage.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react" 2 | 3 | export function useLocalStorage(key, initialValue) { 4 | const [value, setValue] = useState(() => { 5 | const localValue = localStorage.getItem(key) 6 | if (localValue == null) { 7 | if (typeof initialValue === "function") { 8 | return initialValue() 9 | } else { 10 | return initialValue 11 | } 12 | } else { 13 | return JSON.parse(localValue) 14 | } 15 | }) 16 | 17 | useEffect(() => { 18 | if (value === undefined) { 19 | localStorage.removeItem(key) 20 | } else { 21 | localStorage.setItem(key, JSON.stringify(value)) 22 | } 23 | }, [value, key]) 24 | 25 | return [value, setValue] 26 | } 27 | -------------------------------------------------------------------------------- /58-59-basic-form-validation-project/after/src/validators.js: -------------------------------------------------------------------------------- 1 | export function checkEmail(email) { 2 | const errors = [] 3 | 4 | if (email.length === 0) { 5 | errors.push("Required") 6 | } 7 | 8 | if (!email.endsWith("@webdevsimplified.com")) { 9 | errors.push("Must end with @webdevsimplified.com") 10 | } 11 | 12 | return errors 13 | } 14 | 15 | export function checkPassword(password) { 16 | const errors = [] 17 | 18 | if (password.length < 10) { 19 | errors.push("Must be at least 10 characters") 20 | } 21 | 22 | if (!password.match(/[a-z]/)) { 23 | errors.push("Must include at least 1 lowercase letter") 24 | } 25 | 26 | if (!password.match(/[A-Z]/)) { 27 | errors.push("Must include at least 1 uppercase letter") 28 | } 29 | 30 | if (!password.match(/[0-9]/)) { 31 | errors.push("Must include at least 1 number") 32 | } 33 | 34 | return errors 35 | } 36 | -------------------------------------------------------------------------------- /58-59-basic-form-validation-project/before/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 9 |
    10 |
    11 | 12 | 13 |
    Must end in @webdevsimplified.com
    14 |
    15 |
    16 | 17 | 23 |
    24 | 25 |
    26 | 27 | 28 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/layouts/RootLayout.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | Link, 3 | Outlet, 4 | ScrollRestoration, 5 | useNavigation, 6 | } from "react-router-dom" 7 | 8 | export function RootLayout() { 9 | const { state } = useNavigation() 10 | const isLoading = state === "loading" 11 | 12 | return ( 13 | <> 14 | 28 | 29 | {isLoading &&
    } 30 |
    31 | 32 |
    33 | 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/layouts/RootLayout.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | Link, 3 | Outlet, 4 | ScrollRestoration, 5 | useNavigation, 6 | } from "react-router-dom" 7 | 8 | export function RootLayout() { 9 | const { state } = useNavigation() 10 | const isLoading = state === "loading" 11 | 12 | return ( 13 | <> 14 | 28 | 29 | {isLoading &&
    } 30 |
    31 | 32 |
    33 | 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/layouts/RootLayout.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | Link, 3 | Outlet, 4 | ScrollRestoration, 5 | useNavigation, 6 | } from "react-router-dom" 7 | 8 | export function RootLayout() { 9 | const { state } = useNavigation() 10 | const isLoading = state === "loading" 11 | 12 | return ( 13 | <> 14 | 28 | 29 | {isLoading &&
    } 30 |
    31 | 32 |
    33 | 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /37-38-user-list/after/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react" 2 | import { User } from "./User" 3 | 4 | function App() { 5 | const [isLoading, setIsLoading] = useState(true) 6 | const [users, setUsers] = useState([]) 7 | 8 | useEffect(() => { 9 | setIsLoading(true) 10 | 11 | const controller = new AbortController() 12 | fetch("https://jsonplaceholder.typicode.com/users", { 13 | signal: controller.signal, 14 | }) 15 | .then(res => res.json()) 16 | .then(setUsers) 17 | .finally(() => { 18 | setIsLoading(false) 19 | }) 20 | 21 | return () => { 22 | controller.abort() 23 | } 24 | }, []) 25 | 26 | return ( 27 | <> 28 |

    User List

    29 | {isLoading ? ( 30 |

    Loading...

    31 | ) : ( 32 |
      33 | {users.map(user => { 34 | return 35 | })} 36 |
    37 | )} 38 | 39 | ) 40 | } 41 | 42 | export default App 43 | -------------------------------------------------------------------------------- /15-16-user-card/before/README.md: -------------------------------------------------------------------------------- 1 | # Before Getting Started 2 | 3 | If you want you can open the `user.html` file in your browser to see what the final version of this application should look like. Your application should match the output exactly to what the `user.html` file renders. 4 | 5 | All of the CSS is already provided for you along with the appropriate class names in the HTML so you shouldn't need to write any CSS or move any of the class names around. 6 | 7 | 8 | # Instructions 9 | 10 | 1. Initialize a Vite app or use Create React App 11 | 2. Remove all unnecessary code 12 | 3. Import the CSS/JSON files into your App.jsx 13 | 4. Create a `UserCard` function component that takes in `name`, `phoneNumber`, `age`, and `address` props and has the same HTML as the `user.html` file. 14 | 5. Pass the user information into the `UserCard` component from the `App.jsx` file 15 | 6. Try manually changing the data in the `user.json` file to test that everything is hooked up properly 16 | 7. Repeat steps 4-6 but with a class component instead of a function component -------------------------------------------------------------------------------- /51-52-use-array-hook/after/src/useArray.js: -------------------------------------------------------------------------------- 1 | import { useState, useCallback } from "react" 2 | 3 | export function useArray(initialValue) { 4 | const [array, setArray] = useState(initialValue) 5 | 6 | const push = useCallback(element => { 7 | setArray(a => [...a, element]) 8 | }, []) 9 | 10 | const replace = useCallback((index, newElement) => { 11 | setArray(a => { 12 | return [...a.slice(0, index), newElement, ...a.slice(index + 1)] 13 | }) 14 | }, []) 15 | 16 | const filter = useCallback(callback => { 17 | setArray(a => { 18 | return a.filter(callback) 19 | }) 20 | }, []) 21 | 22 | const remove = useCallback(index => { 23 | setArray(a => { 24 | return [...a.slice(0, index), ...a.slice(index + 1)] 25 | }) 26 | }, []) 27 | 28 | const clear = useCallback(() => { 29 | setArray([]) 30 | }, []) 31 | 32 | const reset = useCallback(() => { 33 | setArray(initialValue) 34 | }, [initialValue]) 35 | 36 | return { array, set: setArray, push, replace, filter, remove, clear, reset } 37 | } 38 | -------------------------------------------------------------------------------- /41-42-simple-todo-list/before/styles.css: -------------------------------------------------------------------------------- 1 | #new-todo-form { 2 | display: flex; 3 | flex-direction: column; 4 | max-width: 300px; 5 | } 6 | 7 | #new-todo-form > * { 8 | margin: 0.15rem; 9 | } 10 | 11 | #list { 12 | padding: 0; 13 | } 14 | 15 | .list-item { 16 | list-style: none; 17 | } 18 | 19 | .list-item-label:hover, 20 | [data-list-item-checkbox]:hover { 21 | cursor: pointer; 22 | } 23 | 24 | .list-item-label:hover > [data-list-item-text] { 25 | color: #333; 26 | text-decoration: line-through; 27 | } 28 | 29 | [data-list-item-checkbox]:checked ~ [data-list-item-text] { 30 | text-decoration: line-through; 31 | color: #aaa; 32 | } 33 | 34 | [data-button-delete] { 35 | margin-left: 0.5rem; 36 | } 37 | 38 | button { 39 | cursor: pointer; 40 | } 41 | 42 | [data-button-edit] { 43 | margin-left: 0.5rem; 44 | } 45 | 46 | .filter-form { 47 | display: flex; 48 | gap: 1rem; 49 | align-items: center; 50 | border-bottom: 1px solid black; 51 | padding-bottom: 0.5rem; 52 | } 53 | 54 | .filter-form-group { 55 | display: flex; 56 | gap: 0.25rem; 57 | } 58 | -------------------------------------------------------------------------------- /41-42-simple-todo-list/after/src/styles.css: -------------------------------------------------------------------------------- 1 | #new-todo-form { 2 | display: flex; 3 | flex-direction: column; 4 | max-width: 300px; 5 | } 6 | 7 | #new-todo-form > * { 8 | margin: 0.15rem; 9 | } 10 | 11 | #list { 12 | padding: 0; 13 | } 14 | 15 | .list-item { 16 | list-style: none; 17 | } 18 | 19 | .list-item-label:hover, 20 | [data-list-item-checkbox]:hover { 21 | cursor: pointer; 22 | } 23 | 24 | .list-item-label:hover > [data-list-item-text] { 25 | color: #333; 26 | text-decoration: line-through; 27 | } 28 | 29 | [data-list-item-checkbox]:checked ~ [data-list-item-text] { 30 | text-decoration: line-through; 31 | color: #aaa; 32 | } 33 | 34 | [data-button-delete] { 35 | margin-left: 0.5rem; 36 | } 37 | 38 | button { 39 | cursor: pointer; 40 | } 41 | 42 | [data-button-edit] { 43 | margin-left: 0.5rem; 44 | } 45 | 46 | .filter-form { 47 | display: flex; 48 | gap: 1rem; 49 | align-items: center; 50 | border-bottom: 1px solid black; 51 | padding-bottom: 0.5rem; 52 | } 53 | 54 | .filter-form-group { 55 | display: flex; 56 | gap: 0.25rem; 57 | } 58 | -------------------------------------------------------------------------------- /61-react-hook-form-project/before/src/validators.js: -------------------------------------------------------------------------------- 1 | export function checkEmail(email) { 2 | const errors = [] 3 | 4 | if (email.length === 0) { 5 | errors.push("Required") 6 | } 7 | 8 | if (!email.endsWith("@webdevsimplified.com")) { 9 | errors.push("Must end with @webdevsimplified.com") 10 | } 11 | 12 | return errors 13 | } 14 | 15 | export function checkPassword(password) { 16 | const errors = [] 17 | 18 | if (password.length < 10) { 19 | errors.push("Must be at least 10 characters") 20 | } 21 | 22 | if (!password.match(/[a-z]/)) { 23 | errors.push("Must include at least 1 lowercase letter") 24 | } 25 | 26 | if (!password.match(/[A-Z]/)) { 27 | errors.push("Must include at least 1 uppercase letter") 28 | } 29 | 30 | if (!password.match(/[0-9]/)) { 31 | errors.push("Must include at least 1 number") 32 | } 33 | 34 | return errors 35 | } 36 | 37 | export function checkCountry(country = "") { 38 | const errors = [] 39 | 40 | if (country === "") { 41 | errors.push("Required") 42 | } 43 | 44 | return errors 45 | } 46 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/after/src/styles.css: -------------------------------------------------------------------------------- 1 | #new-todo-form { 2 | display: flex; 3 | flex-direction: column; 4 | max-width: 300px; 5 | } 6 | 7 | #new-todo-form > * { 8 | margin: 0.15rem; 9 | } 10 | 11 | #list { 12 | padding: 0; 13 | } 14 | 15 | .list-item { 16 | list-style: none; 17 | } 18 | 19 | .list-item-label:hover, 20 | [data-list-item-checkbox]:hover { 21 | cursor: pointer; 22 | } 23 | 24 | .list-item-label:hover > [data-list-item-text] { 25 | color: #333; 26 | text-decoration: line-through; 27 | } 28 | 29 | [data-list-item-checkbox]:checked ~ [data-list-item-text] { 30 | text-decoration: line-through; 31 | color: #aaa; 32 | } 33 | 34 | [data-button-delete] { 35 | margin-left: 0.5rem; 36 | } 37 | 38 | button { 39 | cursor: pointer; 40 | } 41 | 42 | [data-button-edit] { 43 | margin-left: 0.5rem; 44 | } 45 | 46 | .filter-form { 47 | display: flex; 48 | gap: 1rem; 49 | align-items: center; 50 | border-bottom: 1px solid black; 51 | padding-bottom: 0.5rem; 52 | } 53 | 54 | .filter-form-group { 55 | display: flex; 56 | gap: 0.25rem; 57 | } 58 | -------------------------------------------------------------------------------- /49-50-use-fetch-hook/after/src/useFetch.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react" 2 | 3 | export function useFetch(url, options = {}) { 4 | const [data, setData] = useState() 5 | const [isError, setIsError] = useState(false) 6 | const [isLoading, setIsLoading] = useState(true) 7 | 8 | useEffect(() => { 9 | setData(undefined) 10 | setIsError(false) 11 | setIsLoading(true) 12 | 13 | const controller = new AbortController() 14 | 15 | fetch(url, { signal: controller.signal, ...options }) 16 | .then(res => { 17 | if (res.status === 200) { 18 | return res.json() 19 | } 20 | return Promise.reject(res) 21 | }) 22 | .then(setData) 23 | .catch(e => { 24 | if (e.name === "AbortError") return 25 | 26 | setIsError(true) 27 | }) 28 | .finally(() => { 29 | if (controller.signal.aborted) return 30 | setIsLoading(false) 31 | }) 32 | 33 | return () => { 34 | controller.abort() 35 | } 36 | }, [url]) 37 | 38 | return { data, isError, isLoading } 39 | } 40 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/before/src/styles.css: -------------------------------------------------------------------------------- 1 | #new-todo-form { 2 | display: flex; 3 | flex-direction: column; 4 | max-width: 300px; 5 | } 6 | 7 | #new-todo-form > * { 8 | margin: 0.15rem; 9 | } 10 | 11 | #list { 12 | padding: 0; 13 | } 14 | 15 | .list-item { 16 | list-style: none; 17 | } 18 | 19 | .list-item-label:hover, 20 | [data-list-item-checkbox]:hover { 21 | cursor: pointer; 22 | } 23 | 24 | .list-item-label:hover > [data-list-item-text] { 25 | color: #333; 26 | text-decoration: line-through; 27 | } 28 | 29 | [data-list-item-checkbox]:checked ~ [data-list-item-text] { 30 | text-decoration: line-through; 31 | color: #aaa; 32 | } 33 | 34 | [data-button-delete] { 35 | margin-left: 0.5rem; 36 | } 37 | 38 | button { 39 | cursor: pointer; 40 | } 41 | 42 | [data-button-edit] { 43 | margin-left: 0.5rem; 44 | } 45 | 46 | .filter-form { 47 | display: flex; 48 | gap: 1rem; 49 | align-items: center; 50 | border-bottom: 1px solid black; 51 | padding-bottom: 0.5rem; 52 | } 53 | 54 | .filter-form-group { 55 | display: flex; 56 | gap: 0.25rem; 57 | } 58 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/pages/UserList.jsx: -------------------------------------------------------------------------------- 1 | import { Link, useLoaderData } from "react-router-dom" 2 | import { getUsers } from "../api/users" 3 | 4 | function UserList() { 5 | const users = useLoaderData() 6 | 7 | return ( 8 | <> 9 |

    Users

    10 |
    11 | {users.map(user => ( 12 |
    13 |
    {user.name}
    14 |
    15 |
    {user.company.name}
    16 |
    {user.website}
    17 |
    {user.email}
    18 |
    19 |
    20 | 21 | View 22 | 23 |
    24 |
    25 | ))} 26 |
    27 | 28 | ) 29 | } 30 | 31 | function loader({ request: { signal } }) { 32 | return getUsers({ signal }) 33 | } 34 | 35 | export const userListRoute = { 36 | loader, 37 | element: , 38 | } 39 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/pages/UserList.jsx: -------------------------------------------------------------------------------- 1 | import { Link, useLoaderData } from "react-router-dom" 2 | import { getUsers } from "../api/users" 3 | 4 | function UserList() { 5 | const users = useLoaderData() 6 | 7 | return ( 8 | <> 9 |

    Users

    10 |
    11 | {users.map(user => ( 12 |
    13 |
    {user.name}
    14 |
    15 |
    {user.company.name}
    16 |
    {user.website}
    17 |
    {user.email}
    18 |
    19 |
    20 | 21 | View 22 | 23 |
    24 |
    25 | ))} 26 |
    27 | 28 | ) 29 | } 30 | 31 | function loader({ request: { signal } }) { 32 | return getUsers({ signal }) 33 | } 34 | 35 | export const userListRoute = { 36 | loader, 37 | element: , 38 | } 39 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/pages/UserList.jsx: -------------------------------------------------------------------------------- 1 | import { Link, useLoaderData } from "react-router-dom" 2 | import { getUsers } from "../api/users" 3 | 4 | function UserList() { 5 | const users = useLoaderData() 6 | 7 | return ( 8 | <> 9 |

    Users

    10 |
    11 | {users.map(user => ( 12 |
    13 |
    {user.name}
    14 |
    15 |
    {user.company.name}
    16 |
    {user.website}
    17 |
    {user.email}
    18 |
    19 |
    20 | 21 | View 22 | 23 |
    24 |
    25 | ))} 26 |
    27 | 28 | ) 29 | } 30 | 31 | function loader({ request: { signal } }) { 32 | return getUsers({ signal }) 33 | } 34 | 35 | export const userListRoute = { 36 | loader, 37 | element: , 38 | } 39 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/before/src/Child.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | export class Child extends React.Component { 4 | constructor() { 5 | super() 6 | this.state = { 7 | age: 0, 8 | name: "", 9 | } 10 | } 11 | 12 | render() { 13 | return ( 14 |
    15 | this.setState({ name: e.target.value })} 19 | /> 20 |
    21 |
    22 | 31 | {this.state.age} 32 | 41 |
    42 |
    43 | My name is {this.state.name} and I am {this.state.age} years old. 44 |
    45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /51-52-use-array-hook/before/src/App.jsx: -------------------------------------------------------------------------------- 1 | const INITIAL_ARRAY = [1, 2, 3] 2 | // const INITIAL_ARRAY = () => [1, 2, 3] 3 | 4 | function App() { 5 | const { array, set, push, replace, filter, remove, clear, reset } = 6 | useArray(INITIAL_ARRAY) 7 | 8 | return ( 9 | <> 10 |
    {array.join(", ")}
    11 |
    20 | 21 | 22 | 25 | 28 | 29 | 30 | 31 |
    32 | 33 | ) 34 | } 35 | 36 | export default App 37 | -------------------------------------------------------------------------------- /41-42-simple-todo-list/before/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 |
      12 |
    • 13 | 17 | 18 |
    • 19 |
    • 20 | 24 | 25 |
    • 26 |
    27 | 28 |
    29 | 30 | 31 | 32 |
    33 | 34 | 35 | -------------------------------------------------------------------------------- /27-28-use-effect-exercises/after/src/Child.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react" 2 | 3 | export function Child() { 4 | const [age, setAge] = useState(0) 5 | const [name, setName] = useState("") 6 | 7 | useEffect(() => { 8 | console.log("Re-Render") 9 | }) 10 | 11 | useEffect(() => { 12 | console.log("Hi") 13 | 14 | return () => { 15 | console.log("Bye") 16 | } 17 | }, []) 18 | 19 | useEffect(() => { 20 | console.log(`My name is ${name} and I am ${age} years old`) 21 | }, [name, age]) 22 | 23 | useEffect(() => { 24 | document.title = name 25 | 26 | const timeout = setTimeout(() => { 27 | console.log(`My name is ${name}`) 28 | }, 1000) 29 | 30 | return () => { 31 | clearTimeout(timeout) 32 | } 33 | }, [name]) 34 | 35 | return ( 36 |
    37 | setName(e.target.value)} /> 38 |
    39 |
    40 | 41 | {age} 42 | 43 |
    44 |
    45 | My name is {name} and I am {age} years old. 46 |
    47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /58-59-basic-form-validation-project/after/src/styles.css: -------------------------------------------------------------------------------- 1 | .form { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 1rem; 5 | max-width: 600px; 6 | } 7 | 8 | .form-group { 9 | display: flex; 10 | flex-direction: column; 11 | gap: 0.25rem; 12 | } 13 | 14 | .label { 15 | font-weight: bold; 16 | font-size: 0.8em; 17 | } 18 | 19 | .input { 20 | all: unset; 21 | border: 0.05em solid black; 22 | border-radius: 0.25em; 23 | padding: 0.25em 0.5em; 24 | } 25 | 26 | .form-group.error .input { 27 | border-color: hsl(0, 100%, 70%); 28 | color: hsl(0, 100%, 70%); 29 | background: hsl(0, 100%, 95%); 30 | } 31 | 32 | .form-group.error .label { 33 | color: hsl(0, 100%, 70%); 34 | } 35 | 36 | .input:focus { 37 | border-color: hsl(200, 100%, 50%); 38 | } 39 | 40 | .btn { 41 | all: unset; 42 | border: 0.05em solid #888; 43 | background-color: #bbb; 44 | border-radius: 0.25em; 45 | padding: 0.5em 1em; 46 | text-align: center; 47 | cursor: pointer; 48 | } 49 | 50 | .btn:hover, 51 | .btn:focus { 52 | background-color: #aaa; 53 | } 54 | 55 | .btn:focus { 56 | outline: auto; 57 | } 58 | 59 | .form-group.error .msg { 60 | font-size: 0.8em; 61 | color: hsl(0, 100%, 70%); 62 | } 63 | -------------------------------------------------------------------------------- /58-59-basic-form-validation-project/before/styles.css: -------------------------------------------------------------------------------- 1 | .form { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 1rem; 5 | max-width: 600px; 6 | } 7 | 8 | .form-group { 9 | display: flex; 10 | flex-direction: column; 11 | gap: 0.25rem; 12 | } 13 | 14 | .label { 15 | font-weight: bold; 16 | font-size: 0.8em; 17 | } 18 | 19 | .input { 20 | all: unset; 21 | border: 0.05em solid black; 22 | border-radius: 0.25em; 23 | padding: 0.25em 0.5em; 24 | } 25 | 26 | .form-group.error .input { 27 | border-color: hsl(0, 100%, 70%); 28 | color: hsl(0, 100%, 70%); 29 | background: hsl(0, 100%, 95%); 30 | } 31 | 32 | .form-group.error .label { 33 | color: hsl(0, 100%, 70%); 34 | } 35 | 36 | .input:focus { 37 | border-color: hsl(200, 100%, 50%); 38 | } 39 | 40 | .btn { 41 | all: unset; 42 | border: 0.05em solid #888; 43 | background-color: #bbb; 44 | border-radius: 0.25em; 45 | padding: 0.5em 1em; 46 | text-align: center; 47 | cursor: pointer; 48 | } 49 | 50 | .btn:hover, 51 | .btn:focus { 52 | background-color: #aaa; 53 | } 54 | 55 | .btn:focus { 56 | outline: auto; 57 | } 58 | 59 | .form-group.error .msg { 60 | font-size: 0.8em; 61 | color: hsl(0, 100%, 70%); 62 | } 63 | -------------------------------------------------------------------------------- /51-52-use-array-hook/after/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useArray } from "./useArray" 2 | 3 | const INITIAL_ARRAY = [1, 2, 3] 4 | // const INITIAL_ARRAY = () => [1, 2, 3] 5 | 6 | function App() { 7 | const { array, set, push, replace, filter, remove, clear, reset } = 8 | useArray(INITIAL_ARRAY) 9 | 10 | return ( 11 | <> 12 |
    {array.join(", ")}
    13 |
    22 | 23 | 24 | 27 | 30 | 31 | 32 | 33 |
    34 | 35 | ) 36 | } 37 | 38 | export default App 39 | -------------------------------------------------------------------------------- /61-react-hook-form-project/after/src/styles.css: -------------------------------------------------------------------------------- 1 | .form { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 1rem; 5 | max-width: 600px; 6 | } 7 | 8 | .form-group { 9 | display: flex; 10 | flex-direction: column; 11 | gap: 0.25rem; 12 | } 13 | 14 | .label { 15 | font-weight: bold; 16 | font-size: 0.8em; 17 | } 18 | 19 | .input { 20 | all: unset; 21 | border: 0.05em solid black; 22 | border-radius: 0.25em; 23 | padding: 0.25em 0.5em; 24 | } 25 | 26 | .form-group.error .input, 27 | .form-group.error .react-select__control { 28 | border-color: hsl(0, 100%, 70%); 29 | color: hsl(0, 100%, 70%); 30 | background: hsl(0, 100%, 95%); 31 | } 32 | 33 | .form-group.error .label { 34 | color: hsl(0, 100%, 70%); 35 | } 36 | 37 | .input:focus { 38 | border-color: hsl(200, 100%, 50%); 39 | } 40 | 41 | .btn { 42 | all: unset; 43 | border: 0.05em solid #888; 44 | background-color: #bbb; 45 | border-radius: 0.25em; 46 | padding: 0.5em 1em; 47 | text-align: center; 48 | cursor: pointer; 49 | } 50 | 51 | .btn:hover, 52 | .btn:focus { 53 | background-color: #aaa; 54 | } 55 | 56 | .btn:focus { 57 | outline: auto; 58 | } 59 | 60 | .form-group.error .msg { 61 | font-size: 0.8em; 62 | color: hsl(0, 100%, 70%); 63 | } 64 | -------------------------------------------------------------------------------- /61-react-hook-form-project/before/src/styles.css: -------------------------------------------------------------------------------- 1 | .form { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 1rem; 5 | max-width: 600px; 6 | } 7 | 8 | .form-group { 9 | display: flex; 10 | flex-direction: column; 11 | gap: 0.25rem; 12 | } 13 | 14 | .label { 15 | font-weight: bold; 16 | font-size: 0.8em; 17 | } 18 | 19 | .input { 20 | all: unset; 21 | border: 0.05em solid black; 22 | border-radius: 0.25em; 23 | padding: 0.25em 0.5em; 24 | } 25 | 26 | .form-group.error .input, 27 | .form-group.error .react-select__control { 28 | border-color: hsl(0, 100%, 70%); 29 | color: hsl(0, 100%, 70%); 30 | background: hsl(0, 100%, 95%); 31 | } 32 | 33 | .form-group.error .label { 34 | color: hsl(0, 100%, 70%); 35 | } 36 | 37 | .input:focus { 38 | border-color: hsl(200, 100%, 50%); 39 | } 40 | 41 | .btn { 42 | all: unset; 43 | border: 0.05em solid #888; 44 | background-color: #bbb; 45 | border-radius: 0.25em; 46 | padding: 0.5em 1em; 47 | text-align: center; 48 | cursor: pointer; 49 | } 50 | 51 | .btn:hover, 52 | .btn:focus { 53 | background-color: #aaa; 54 | } 55 | 56 | .btn:focus { 57 | outline: auto; 58 | } 59 | 60 | .form-group.error .msg { 61 | font-size: 0.8em; 62 | color: hsl(0, 100%, 70%); 63 | } 64 | -------------------------------------------------------------------------------- /21-22-name-counter/after/src/ClassComponent.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | export class ClassComponent extends React.Component { 4 | constructor(props) { 5 | super(props) 6 | 7 | this.state = { 8 | name: "", 9 | age: 0, 10 | } 11 | } 12 | 13 | render() { 14 | return ( 15 |
    16 | this.setState({ name: e.target.value })} 19 | /> 20 |
    21 |
    22 | 33 | {this.state.age} 34 | 45 |
    46 |
    47 | My name is {this.state.name} and I am {this.state.age} years old. 48 |
    49 | ) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/after/README.md: -------------------------------------------------------------------------------- 1 | # Before Getting Started 2 | 3 | The starting code for this project is the exact same code as the ending of the simple todo list project we completed earlier in the course. The only change is I added an `app.html` file which shows what the final HTML/CSS for this project should look like. Also, the CSS file you used in the first project already contains all the styles needed for this updated version of the project so you do not need to add any additional styles. 4 | 5 | The goal of this project is to expand upon our simple todo list by adding more complex user interactions which will lead to more complex state management. 6 | 7 | # Instructions 8 | 9 | 1. The state for our todos should be stored in local storage so when we come back to the page at a later time all our data is still there 10 | 2. Convert all the state in the application to use `useReducer` and `Context` to pass the state between components 11 | 3. Add a form that lets you filter todos by their name and hide completed todos 12 | 13 | ## Bonus 14 | 15 | 1. Add the ability to edit existing todos 16 | - This is in the bonus section not because the editing portion is tricky, but because handling the proper UI state of swapping between text and an input is something we haven't really done before 17 | -------------------------------------------------------------------------------- /51-52-use-array-hook/before/README.md: -------------------------------------------------------------------------------- 1 | # Before Getting Started 2 | 3 | The starting code for this project includes all the JSX/logic of using the hook already. The only thing you need to do is implement the `useArray` hook and import it into the `App.jsx` file. 4 | 5 | # Instructions 6 | 7 | 1. Create a custom `useArray` hook that takes an array (or a function that returns an array) as its only argument, stores that array in state, and returns an object with the following properties: 8 | * `array` - This is the array stored in state 9 | * `set` - This is just the set state function returned from `useState` 10 | * `push` - A function that takes one argument and adds that argument to the end of the array 11 | * `replace` - A function that takes two arguments (the index of the element to replace, and the new element to replace the old element with) and replaces the element at the specified index with the new element 12 | * `filter` - A function that takes one argument (a callback function) and filters the array just like the `array.filter` method 13 | * `remove` - A function that takes one argument (the index of the element to remove) and removes the element at the index specified 14 | * `clear` - A function that will remove all elements from the array 15 | * `reset` - A function that will reset the array to its initial value -------------------------------------------------------------------------------- /68-69-advanced-todo-list/before/README.md: -------------------------------------------------------------------------------- 1 | # Before Getting Started 2 | 3 | The starting code for this project is the exact same code as the ending of the simple todo list project we completed earlier in the course. The only change is I added an `app.html` file which shows what the final HTML/CSS for this project should look like. Also, the CSS file you used in the first project already contains all the styles needed for this updated version of the project so you do not need to add any additional styles. 4 | 5 | The goal of this project is to expand upon our simple todo list by adding more complex user interactions which will lead to more complex state management. 6 | 7 | # Instructions 8 | 9 | 1. The state for our todos should be stored in local storage so when we come back to the page at a later time all our data is still there 10 | 2. Convert all the state in the application to use `useReducer` and `Context` to pass the state between components 11 | 3. Add the ability to delete existing todos 12 | 4. Add a form that lets you filter todos by their name and hide completed todos 13 | 14 | ## Bonus 15 | 16 | 1. Add the ability to edit existing todos 17 | - This is in the bonus section not because the editing portion is tricky, but because handling the proper UI state of swapping between text and an input is something we haven't really done before 18 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/pages/Post.jsx: -------------------------------------------------------------------------------- 1 | import { Link, useLoaderData } from "react-router-dom" 2 | import { getComments } from "../api/comments" 3 | import { getPost } from "../api/posts" 4 | import { getUser } from "../api/users" 5 | 6 | function Post() { 7 | const { comments, post, user } = useLoaderData() 8 | 9 | return ( 10 | <> 11 |

    {post.title}

    12 | 13 | By: {user.name} 14 | 15 |
    {post.body}
    16 | 17 |

    Comments

    18 |
    19 | {comments.map(comment => ( 20 |
    21 |
    22 |
    {comment.email}
    23 | {comment.body} 24 |
    25 |
    26 | ))} 27 |
    28 | 29 | ) 30 | } 31 | 32 | async function loader({ request: { signal }, params: { postId } }) { 33 | const comments = getComments(postId, { signal }) 34 | const post = await getPost(postId, { signal }) 35 | const user = getUser(post.userId, { signal }) 36 | 37 | return { comments: await comments, post, user: await user } 38 | } 39 | 40 | export const postRoute = { 41 | loader, 42 | element: , 43 | } 44 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/pages/Post.jsx: -------------------------------------------------------------------------------- 1 | import { Link, useLoaderData } from "react-router-dom" 2 | import { getComments } from "../api/comments" 3 | import { getPost } from "../api/posts" 4 | import { getUser } from "../api/users" 5 | 6 | function Post() { 7 | const { comments, post, user } = useLoaderData() 8 | 9 | return ( 10 | <> 11 |

    {post.title}

    12 | 13 | By: {user.name} 14 | 15 |
    {post.body}
    16 | 17 |

    Comments

    18 |
    19 | {comments.map(comment => ( 20 |
    21 |
    22 |
    {comment.email}
    23 | {comment.body} 24 |
    25 |
    26 | ))} 27 |
    28 | 29 | ) 30 | } 31 | 32 | async function loader({ request: { signal }, params: { postId } }) { 33 | const comments = getComments(postId, { signal }) 34 | const post = await getPost(postId, { signal }) 35 | const user = getUser(post.userId, { signal }) 36 | 37 | return { comments: await comments, post, user: await user } 38 | } 39 | 40 | export const postRoute = { 41 | loader, 42 | element: , 43 | } 44 | -------------------------------------------------------------------------------- /20-array-component/README.md: -------------------------------------------------------------------------------- 1 | # Before Getting Started 2 | 3 | There is no starting code for this project. This project is used purely as a way to test your knowledge of working with arrays and state. Each of the functions that I ask you to create should have a button rendered in the JSX that executes that function and the JSX should also render the array as a comma separated string. 4 | 5 | You can do this with either class components or function components (I recommend function components), but there is no need to do both as they are very similar. 6 | 7 | 8 | # Functionality To Create 9 | 10 | 1. Create state that stores an array with the initial value of `["A", "B", "C"]` 11 | 2. Add the ability to remove the first element from the array 12 | 3. Add the ability to remove a specific letter from the array 13 | 4. Add the ability to add a new element to the start of the array 14 | 5. Add the ability to add a new element to the end of the array 15 | 6. Add the ability to clear the array 16 | 7. Add the ability to reset the array to the initial value 17 | 18 | ## Bonus Functionality 19 | 20 | These are optional challenges that are more difficult and will really test your skills. 21 | 22 | 1. Add the ability to update all `A` elements in the array to `H` 23 | 2. Add an input that is connected to state and a button that will add the input value to the start of the array 24 | 3. Add the ability to add a new element at any index in the array -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/pages/NewPost.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | redirect, 3 | useActionData, 4 | useLoaderData, 5 | useNavigation, 6 | } from "react-router-dom" 7 | import { createPost } from "../api/posts" 8 | import { getUsers } from "../api/users" 9 | import { PostForm, postFormValidator } from "../components/PostForm" 10 | 11 | function NewPost() { 12 | const users = useLoaderData() 13 | const { state } = useNavigation() 14 | const errors = useActionData() 15 | const isSubmitting = state === "submitting" 16 | 17 | return ( 18 | <> 19 |

    New Post

    20 | 21 | 22 | ) 23 | } 24 | 25 | async function action({ request }) { 26 | const formData = await request.formData() 27 | const title = formData.get("title") 28 | const body = formData.get("body") 29 | const userId = formData.get("userId") 30 | 31 | const errors = postFormValidator({ title, userId, body }) 32 | 33 | if (Object.keys(errors).length > 0) { 34 | return errors 35 | } 36 | 37 | const post = await createPost( 38 | { title, body, userId }, 39 | { signal: request.signal } 40 | ) 41 | 42 | return redirect(`/posts/${post.id}`) 43 | } 44 | 45 | function loader({ request: { signal } }) { 46 | return getUsers({ signal }) 47 | } 48 | 49 | export const newPostRoute = { 50 | loader, 51 | action, 52 | element: , 53 | } 54 | -------------------------------------------------------------------------------- /53-54-use-local-storage-hook/before/README.md: -------------------------------------------------------------------------------- 1 | # Before Getting Started 2 | 3 | The starting code for this project includes all the JSX/logic of using the hook already. The only thing you need to do is implement the `useLocalStorage` hook and import it into the `App.jsx` file. Also, the code for the bonus section of the course is included but just commented out so you can uncomment those lines whenever you tackle the bonus sections. 4 | 5 | This project is a step up in difficulty from the last few (especially the bonus sections) so don't worry if this is a bit of a struggle. 6 | 7 | # Instructions 8 | 9 | 1. Create a custom `useLocalStorage` hook that functions identically to `useState` by returning an array where the first element is the value and the second element is the function to set the value. This hook should take two arguments. The first is a string which is the key for `localStorage` and the second is the initial value of the state. 10 | 2. Whenever the state changes it should be synced with `localStorage` so that if you were to refresh your page nothing would change as all values are pulled from `localStorage` on initial load and stored in `localStorage` when changed. 11 | 12 | ## Bonus 13 | 14 | 1. Ensure that the `useLocalStorage` hook works just like `useState` in that you can pass it a value or function as the initial value. 15 | 2. Use JSON to serialize and deserialize the values stored in `localStorage` so that it will work with any value (such as arrays or objects). -------------------------------------------------------------------------------- /20-array-component/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /21-22-name-counter/after/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/before/README.md: -------------------------------------------------------------------------------- 1 | # Before Getting Started 2 | 3 | This project is identical to the previous `useEffect` project, but will be class based instead of function based. The starting code is the same, but the `Child` component is a class instead of a function. 4 | 5 | 6 | # Instructions 7 | 8 | The following exercises should all be performed within the `Child` component. They are exactly the same as the previous `useEffect` project instructions. 9 | 10 | 1. `console.log` the text **Render** each time the component re-renders 11 | 2. `console.log` the text **Hi** when the component mounts 12 | 3. `console.log` the text **My name is {name} and I am {age} years old** whenever the `name` or `age` changes 13 | 4. Update the `document.title` to be equal to `name` whenever the `name` changes 14 | 15 | ## Bonus 16 | 17 | 1. `console.log` the text **Bye** when the component unmounts 18 | 2. Create a timeout that `console.log`s the text **My name is {name}** only after there has been a 1 second delay since the last time the name was changed 19 | * For example, if I change the name from **Kyle** to **Kyl** and then to **Ky** without having more than 1 second between each name change the console should log nothing until 1 second after I finishing changing the name to **Ky** and then it will log **Ky**. If instead there as greater than 1 second delay between each change, it should log **Kyl** and then **Ky**. Each of those logs would happen exactly 1 second after the name was changed. -------------------------------------------------------------------------------- /27-28-use-effect-exercises/after/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /27-28-use-effect-exercises/before/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/after/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/before/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/after/src/TodoItem.jsx: -------------------------------------------------------------------------------- 1 | import { useContext, useRef, useState } from "react" 2 | import { TodoContext } from "./App" 3 | 4 | export function TodoItem({ id, name, completed }) { 5 | const { toggleTodo, deleteTodo, updateTodoName } = useContext(TodoContext) 6 | const [isEditing, setIsEditing] = useState(false) 7 | const nameRef = useRef() 8 | 9 | function handleSubmit(e) { 10 | e.preventDefault() 11 | 12 | if (nameRef.current.value === "") return 13 | 14 | updateTodoName(id, nameRef.current.value) 15 | setIsEditing(false) 16 | } 17 | 18 | return ( 19 |
  • 20 | {isEditing ? ( 21 |
    22 | 23 | 24 |
    25 | ) : ( 26 | <> 27 | 36 | 39 | 42 | 43 | )} 44 |
  • 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /49-50-use-fetch-hook/before/README.md: -------------------------------------------------------------------------------- 1 | # Before Getting Started 2 | 3 | The starting code for this project includes all the JSX/logic of using the hook already. The only thing you need to do is implement the `useFetch` hook and import it into the `App.jsx` file. Also, the code for the bonus section of the course is included but just commented out so you can uncomment those lines whenever you tackle the bonus sections. 4 | 5 | If the API used in this video is no longer available or does not work just uncomment the `URLS` object at the top of the file to use the local JSON files in the `public` folder. 6 | 7 | This project should hopefully be fairly easy for you as it is just taking everything we have done with fetch and putting it into a custom hook. 8 | 9 | # Instructions 10 | 11 | 1. Create a custom `useFetch` hook that returns an object with the following data: 12 | * `isLoading` - Will be true while the fetch request is loading 13 | * `isError` - Will be true if the fetch request failed 14 | * `data` - Will contain the data from the fetch request 15 | 16 | ## Bonus 17 | 18 | 1. Add the ability to pass down an `options` object to the `useFetch` hook that will set the options for the fetch request. 19 | * If you are using the local files instead of the API you will not see any difference in the output when passing these custom options just because of the limitations of not actually calling an API on a server. 20 | 2. Add in the proper cleanup functionality for aborting a request if a new request is triggered before the old one finishes. -------------------------------------------------------------------------------- /37-38-user-list/before/README.md: -------------------------------------------------------------------------------- 1 | # Before Getting Started 2 | 3 | If you want you can open the `app.html` file in your browser to see what the final version of this application should look like. Your application should match the output exactly to what the `app.html` file renders. 4 | 5 | **BONUS:** If you decide to do the bonus loading section you can view the `loading.html` file for an example of what the page should look like while loading. 6 | 7 | If the API we use in this video is unavailable, or different in anyway to what I show in the video you can use the `users.json` file as an alternative to the API. If you place the `users.json` file in the `public` folder of your project you can fetch it by running `fetch("users.json")` in your code. 8 | 9 | 10 | # Instructions 11 | 12 | 1. Fetch all users from the API (https://jsonplaceholder.typicode.com/users) in your App.jsx file using `useEffect`. 13 | 2. Render an `h1` that says **User List** and below that a `ul` containing a list of all users. This is a perfect use case for fragments since we don't want to wrap it in an extra div. 14 | 3. The users in the list should be in their own component and that component should take a `name` prop and return the `name` inside an `li` element. 15 | 16 | ## Bonus 17 | 18 | 1. Add a loading state that will display the text `Loading...` instead of the user list while it is being downloaded from the API. 19 | * You can use your dev tools to throttle your network speed to more easily test the loading. Go to the **Network** tab, click the **No Throttling** drop down and choose **Slow 3G**. -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/pages/Post.jsx: -------------------------------------------------------------------------------- 1 | import { Link, useLoaderData } from "react-router-dom" 2 | import { getComments } from "../api/comments" 3 | import { getPost } from "../api/posts" 4 | import { getUser } from "../api/users" 5 | 6 | function Post() { 7 | const { comments, post, user } = useLoaderData() 8 | 9 | return ( 10 | <> 11 |

    12 | {post.title} 13 |
    14 | 15 | Edit 16 | 17 |
    18 |

    19 | 20 | By: {user.name} 21 | 22 |
    {post.body}
    23 | 24 |

    Comments

    25 |
    26 | {comments.map(comment => ( 27 |
    28 |
    29 |
    {comment.email}
    30 | {comment.body} 31 |
    32 |
    33 | ))} 34 |
    35 | 36 | ) 37 | } 38 | 39 | async function loader({ request: { signal }, params: { postId } }) { 40 | const comments = getComments(postId, { signal }) 41 | const post = await getPost(postId, { signal }) 42 | const user = getUser(post.userId, { signal }) 43 | 44 | return { comments: await comments, post, user: await user } 45 | } 46 | 47 | export const postRoute = { 48 | loader, 49 | element: , 50 | } 51 | -------------------------------------------------------------------------------- /53-54-use-local-storage-hook/after/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useLocalStorage } from "./useLocalStorage" 2 | 3 | function App() { 4 | const [firstName, setFirstName] = useLocalStorage("FIRST_NAME", "") 5 | 6 | const [lastName, setLastName] = useLocalStorage("LAST_NAME", () => { 7 | return "Default" 8 | }) 9 | 10 | const [hobbies, setHobbies] = useLocalStorage("HOBBIES", [ 11 | "Programming", 12 | "Weight Lifting", 13 | ]) 14 | 15 | return ( 16 | <> 17 |
    25 | 26 | setFirstName(e.target.value)} 30 | /> 31 |
    32 | 33 |
    41 | 42 | setLastName(e.target.value)} 46 | /> 47 |
    48 | 49 |
    {hobbies.join(", ")}
    50 | 57 | 58 | ) 59 | } 60 | 61 | export default App 62 | -------------------------------------------------------------------------------- /49-50-use-fetch-hook/after/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | import { useFetch } from "./useFetch" 3 | 4 | // If the API does not work use these local URLs 5 | // const URLS = { 6 | // USERS: "users.json", 7 | // POSTS: "posts.json", 8 | // COMMENTS: "comments.json", 9 | // } 10 | 11 | const URLS = { 12 | USERS: "https://jsonplaceholder.typicode.com/users", 13 | POSTS: "https://jsonplaceholder.typicode.com/posts", 14 | COMMENTS: "https://jsonplaceholder.typicode.com/comments", 15 | } 16 | 17 | function App() { 18 | const [url, setUrl] = useState(URLS.USERS) 19 | 20 | const { data, isLoading, isError } = useFetch(url) 21 | 22 | return ( 23 | <> 24 |
    25 | 33 | 41 | 49 |
    50 | {isLoading ? ( 51 |

    Loading...

    52 | ) : isError ? ( 53 |

    Error

    54 | ) : ( 55 |
    {JSON.stringify(data, null, 2)}
    56 | )} 57 | 58 | ) 59 | } 60 | 61 | export default App 62 | -------------------------------------------------------------------------------- /53-54-use-local-storage-hook/before/src/App.jsx: -------------------------------------------------------------------------------- 1 | function App() { 2 | const [firstName, setFirstName] = useLocalStorage("FIRST_NAME", "") 3 | 4 | // Bonus: 5 | // const [lastName, setLastName] = useLocalStorage("LAST_NAME", () => { 6 | // return "Default" 7 | // }) 8 | 9 | // Bonus: 10 | // const [hobbies, setHobbies] = useLocalStorage("HOBBIES", [ 11 | // "Programming", 12 | // "Weight Lifting", 13 | // ]) 14 | 15 | return ( 16 | <> 17 |
    25 | 26 | setFirstName(e.target.value)} 30 | /> 31 |
    32 | 33 | {/* Bonus: */} 34 | {/*
    42 | 43 | setLastName(e.target.value)} 47 | /> 48 |
    */} 49 | 50 | {/* Bonus: */} 51 | {/*
    {hobbies.join(", ")}
    52 | */} 59 | 60 | ) 61 | } 62 | 63 | export default App 64 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/pages/User.jsx: -------------------------------------------------------------------------------- 1 | import { useLoaderData } from "react-router-dom" 2 | import { getPosts } from "../api/posts" 3 | import { getTodos } from "../api/todos" 4 | import { getUser } from "../api/users" 5 | import { PostCard } from "../components/PostCard" 6 | import { TodoItem } from "../components/TodoItem" 7 | 8 | function User() { 9 | const { user, posts, todos } = useLoaderData() 10 | 11 | return ( 12 | <> 13 |

    {user.name}

    14 |
    {user.email}
    15 |
    16 | Company: {user.company.name} 17 |
    18 |
    19 | Website: {user.website} 20 |
    21 |
    22 | Address: {user.address.street} {user.address.suite}{" "} 23 | {user.address.city} {user.address.zipcode} 24 |
    25 | 26 |

    Posts

    27 |
    28 | {posts.map(post => ( 29 | 30 | ))} 31 |
    32 |

    Todos

    33 |
      34 | {todos.map(todo => ( 35 | 36 | ))} 37 |
    38 | 39 | ) 40 | } 41 | 42 | async function loader({ request: { signal }, params: { userId } }) { 43 | const posts = getPosts({ signal, params: { userId } }) 44 | const todos = getTodos({ signal, params: { userId } }) 45 | const user = getUser(userId, { signal }) 46 | 47 | return { posts: await posts, todos: await todos, user: await user } 48 | } 49 | 50 | export const userRoute = { 51 | loader, 52 | element: , 53 | } 54 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/pages/User.jsx: -------------------------------------------------------------------------------- 1 | import { useLoaderData } from "react-router-dom" 2 | import { getPosts } from "../api/posts" 3 | import { getTodos } from "../api/todos" 4 | import { getUser } from "../api/users" 5 | import { PostCard } from "../components/PostCard" 6 | import { TodoItem } from "../components/TodoItem" 7 | 8 | function User() { 9 | const { user, posts, todos } = useLoaderData() 10 | 11 | return ( 12 | <> 13 |

    {user.name}

    14 |
    {user.email}
    15 |
    16 | Company: {user.company.name} 17 |
    18 |
    19 | Website: {user.website} 20 |
    21 |
    22 | Address: {user.address.street} {user.address.suite}{" "} 23 | {user.address.city} {user.address.zipcode} 24 |
    25 | 26 |

    Posts

    27 |
    28 | {posts.map(post => ( 29 | 30 | ))} 31 |
    32 |

    Todos

    33 |
      34 | {todos.map(todo => ( 35 | 36 | ))} 37 |
    38 | 39 | ) 40 | } 41 | 42 | async function loader({ request: { signal }, params: { userId } }) { 43 | const posts = getPosts({ signal, params: { userId } }) 44 | const todos = getTodos({ signal, params: { userId } }) 45 | const user = getUser(userId, { signal }) 46 | 47 | return { posts: await posts, todos: await todos, user: await user } 48 | } 49 | 50 | export const userRoute = { 51 | loader, 52 | element: , 53 | } 54 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/pages/User.jsx: -------------------------------------------------------------------------------- 1 | import { useLoaderData } from "react-router-dom" 2 | import { getPosts } from "../api/posts" 3 | import { getTodos } from "../api/todos" 4 | import { getUser } from "../api/users" 5 | import { PostCard } from "../components/PostCard" 6 | import { TodoItem } from "../components/TodoItem" 7 | 8 | function User() { 9 | const { user, posts, todos } = useLoaderData() 10 | 11 | return ( 12 | <> 13 |

    {user.name}

    14 |
    {user.email}
    15 |
    16 | Company: {user.company.name} 17 |
    18 |
    19 | Website: {user.website} 20 |
    21 |
    22 | Address: {user.address.street} {user.address.suite}{" "} 23 | {user.address.city} {user.address.zipcode} 24 |
    25 | 26 |

    Posts

    27 |
    28 | {posts.map(post => ( 29 | 30 | ))} 31 |
    32 |

    Todos

    33 |
      34 | {todos.map(todo => ( 35 | 36 | ))} 37 |
    38 | 39 | ) 40 | } 41 | 42 | async function loader({ request: { signal }, params: { userId } }) { 43 | const posts = getPosts({ signal, params: { userId } }) 44 | const todos = getTodos({ signal, params: { userId } }) 45 | const user = getUser(userId, { signal }) 46 | 47 | return { posts: await posts, todos: await todos, user: await user } 48 | } 49 | 50 | export const userRoute = { 51 | loader, 52 | element: , 53 | } 54 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/after/client/src/router.jsx: -------------------------------------------------------------------------------- 1 | import { createBrowserRouter, Navigate, useRouteError } from "react-router-dom" 2 | import { RootLayout } from "./layouts/RootLayout" 3 | import { postRoute } from "./pages/Post" 4 | import { postListRoute } from "./pages/PostList" 5 | import { todoListRoute } from "./pages/TodoList" 6 | import { userRoute } from "./pages/User" 7 | import { userListRoute } from "./pages/UserList" 8 | 9 | export const router = createBrowserRouter([ 10 | { 11 | path: "/", 12 | element: , 13 | children: [ 14 | { 15 | errorElement: , 16 | children: [ 17 | { index: true, element: }, 18 | { 19 | path: "posts", 20 | children: [ 21 | { 22 | index: true, 23 | ...postListRoute, 24 | }, 25 | { path: ":postId", ...postRoute }, 26 | ], 27 | }, 28 | { 29 | path: "users", 30 | children: [ 31 | { index: true, ...userListRoute }, 32 | { path: ":userId", ...userRoute }, 33 | ], 34 | }, 35 | { path: "todos", ...todoListRoute }, 36 | { path: "*", element:

    404 - Page Not Found

    }, 37 | ], 38 | }, 39 | ], 40 | }, 41 | ]) 42 | 43 | function ErrorPage() { 44 | const error = useRouteError() 45 | 46 | return ( 47 | <> 48 |

    Error - Something went wrong

    49 | {import.meta.env.MODE !== "production" && ( 50 | <> 51 |
    {error.message}
    52 |
    {error.stack}
    53 | 54 | )} 55 | 56 | ) 57 | } 58 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/client/src/router.jsx: -------------------------------------------------------------------------------- 1 | import { createBrowserRouter, Navigate, useRouteError } from "react-router-dom" 2 | import { RootLayout } from "./layouts/RootLayout" 3 | import { postRoute } from "./pages/Post" 4 | import { postListRoute } from "./pages/PostList" 5 | import { todoListRoute } from "./pages/TodoList" 6 | import { userRoute } from "./pages/User" 7 | import { userListRoute } from "./pages/UserList" 8 | 9 | export const router = createBrowserRouter([ 10 | { 11 | path: "/", 12 | element: , 13 | children: [ 14 | { 15 | errorElement: , 16 | children: [ 17 | { index: true, element: }, 18 | { 19 | path: "posts", 20 | children: [ 21 | { 22 | index: true, 23 | ...postListRoute, 24 | }, 25 | { path: ":postId", ...postRoute }, 26 | ], 27 | }, 28 | { 29 | path: "users", 30 | children: [ 31 | { index: true, ...userListRoute }, 32 | { path: ":userId", ...userRoute }, 33 | ], 34 | }, 35 | { path: "todos", ...todoListRoute }, 36 | { path: "*", element:

    404 - Page Not Found

    }, 37 | ], 38 | }, 39 | ], 40 | }, 41 | ]) 42 | 43 | function ErrorPage() { 44 | const error = useRouteError() 45 | 46 | return ( 47 | <> 48 |

    Error - Something went wrong

    49 | {import.meta.env.MODE !== "production" && ( 50 | <> 51 |
    {error.message}
    52 |
    {error.stack}
    53 | 54 | )} 55 | 56 | ) 57 | } 58 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/pages/EditPost.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | redirect, 3 | useActionData, 4 | useLoaderData, 5 | useNavigation, 6 | } from "react-router" 7 | import { getPost, updatePost } from "../api/posts" 8 | import { getUsers } from "../api/users" 9 | import { PostForm, postFormValidator } from "../components/PostForm" 10 | 11 | function EditPost() { 12 | const { users, post } = useLoaderData() 13 | const { state } = useNavigation() 14 | const errors = useActionData() 15 | const isSubmitting = state === "submitting" 16 | 17 | return ( 18 | <> 19 |

    Edit Post

    20 | 26 | 27 | ) 28 | } 29 | 30 | async function loader({ request: { signal }, params: { postId } }) { 31 | const post = getPost(postId, { signal }) 32 | const users = getUsers({ signal }) 33 | 34 | return { post: await post, users: await users } 35 | } 36 | 37 | async function action({ request, params: { postId } }) { 38 | const formData = await request.formData() 39 | const title = formData.get("title") 40 | const body = formData.get("body") 41 | const userId = formData.get("userId") 42 | 43 | const errors = postFormValidator({ title, userId, body }) 44 | 45 | if (Object.keys(errors).length > 0) { 46 | return errors 47 | } 48 | 49 | const post = await updatePost( 50 | postId, 51 | { title, body, userId }, 52 | { signal: request.signal } 53 | ) 54 | 55 | return redirect(`/posts/${post.id}`) 56 | } 57 | 58 | export const editPostRoute = { 59 | loader, 60 | action, 61 | element: , 62 | } 63 | -------------------------------------------------------------------------------- /41-42-simple-todo-list/before/README.md: -------------------------------------------------------------------------------- 1 | # Before Getting Started 2 | 3 | If you want you can open the `app.html` file in your browser to see what the final version of this application should look like. Your application should match the output exactly to what the `app.html` file renders. 4 | 5 | You may notice I included things like ids, data-attributes, class selectors, etc in the HTML. All these should be included in the final version of this project as the CSS uses them all. This is there to ensure you understand how to set HTML attributes in React. 6 | 7 | This project will also be one of the first projects where the instructions I give you will be very vague on implementation details. It is up to you to figure out the best way to implement each task. This will also more closely resemble working on projects in the real world. 8 | 9 | # Instructions 10 | 11 | 1. Add the ability to add todos with an input and a button which adds the todo when clicked. These todos should show up in a list above the input. 12 | * You can use a form for this if you want, but there are some quirks with forms in React that we cover later in the course so it is probably best just to not use a form for now. 13 | 2. Add the ability to mark a todo as complete by clicking on the checkbox or label for the todo. 14 | 3. Add a delete button next to each todo item in the list which will remove the todo when clicked. 15 | 16 | ## Bonus 17 | 18 | 1. The easiest way to create this project is to just put all the HTML/data into one single component. This works fine for this project since it is so small, but with larger projects is not ideal. The bonus for this project is to break up your project so that each todo item in the list is rendered as a separate component from your `App.jsx` component. -------------------------------------------------------------------------------- /41-42-simple-todo-list/after/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | import "./styles.css" 3 | import { TodoItem } from "./TodoItem" 4 | 5 | function App() { 6 | const [newTodoName, setNewTodoName] = useState("") 7 | const [todos, setTodos] = useState([]) 8 | 9 | function addNewTodo() { 10 | if (newTodoName === "") return 11 | 12 | setTodos(currentTodos => { 13 | return [ 14 | ...currentTodos, 15 | { name: newTodoName, completed: false, id: crypto.randomUUID() }, 16 | ] 17 | }) 18 | setNewTodoName("") 19 | } 20 | 21 | function toggleTodo(todoId, completed) { 22 | setTodos(currentTodos => { 23 | return currentTodos.map(todo => { 24 | if (todo.id === todoId) return { ...todo, completed } 25 | 26 | return todo 27 | }) 28 | }) 29 | } 30 | 31 | function deleteTodo(todoId) { 32 | setTodos(currentTodos => { 33 | return currentTodos.filter(todo => todo.id !== todoId) 34 | }) 35 | } 36 | 37 | return ( 38 | <> 39 |
      40 | {todos.map(todo => { 41 | return ( 42 | 48 | ) 49 | })} 50 |
    51 | 52 |
    53 | 54 | setNewTodoName(e.target.value)} 59 | /> 60 | 61 |
    62 | 63 | ) 64 | } 65 | 66 | export default App 67 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/before/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | import "./styles.css" 3 | import { TodoItem } from "./TodoItem" 4 | 5 | function App() { 6 | const [newTodoName, setNewTodoName] = useState("") 7 | const [todos, setTodos] = useState([]) 8 | 9 | function addNewTodo() { 10 | if (newTodoName === "") return 11 | 12 | setTodos(currentTodos => { 13 | return [ 14 | ...currentTodos, 15 | { name: newTodoName, completed: false, id: crypto.randomUUID() }, 16 | ] 17 | }) 18 | setNewTodoName("") 19 | } 20 | 21 | function toggleTodo(todoId, completed) { 22 | setTodos(currentTodos => { 23 | return currentTodos.map(todo => { 24 | if (todo.id === todoId) return { ...todo, completed } 25 | 26 | return todo 27 | }) 28 | }) 29 | } 30 | 31 | function deleteTodo(todoId) { 32 | setTodos(currentTodos => { 33 | return currentTodos.filter(todo => todo.id !== todoId) 34 | }) 35 | } 36 | 37 | return ( 38 | <> 39 |
      40 | {todos.map(todo => { 41 | return ( 42 | 48 | ) 49 | })} 50 |
    51 | 52 |
    53 | 54 | setNewTodoName(e.target.value)} 59 | /> 60 | 61 |
    62 | 63 | ) 64 | } 65 | 66 | export default App 67 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/after/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 9 |
    10 |
    11 | 12 | 13 |
    14 | 18 |
    19 |
      20 |
    • 21 | 25 | 26 | 27 |
    • 28 |
    • 29 | 33 | 34 | 35 |
    • 36 |
    • 37 | 41 | 42 | 43 |
    • 44 |
    45 |
    46 | 47 | 48 | 49 |
    50 | 51 | 52 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/before/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 9 |
    10 |
    11 | 12 | 13 |
    14 | 18 |
    19 |
      20 |
    • 21 | 25 | 26 | 27 |
    • 28 |
    • 29 | 33 | 34 | 35 |
    • 36 |
    • 37 | 41 | 42 | 43 |
    • 44 |
    45 |
    46 | 47 | 48 | 49 |
    50 | 51 | 52 | -------------------------------------------------------------------------------- /58-59-basic-form-validation-project/before/README.md: -------------------------------------------------------------------------------- 1 | # Before Getting Started 2 | 3 | If you want you can open the `app.html` file in your browser to see what the final version of this application should look like. Your application should match the output exactly to what the `app.html` file renders. This includes all the class names you will need to work with this project. 4 | 5 | This project is a bit of a complex one (especially the bonus section) since it combines together many concepts from all across this course. 6 | 7 | # Instructions 8 | 9 | 1. Create a form with an email and password input that check for the following validations: 10 | * Email: 11 | * Required (Cannot be blank) 12 | * Must end in `@webdevsimplified.com` 13 | * Password: 14 | * Required (Cannot be blank) 15 | * Must Be 10 characters or longer 16 | * Must include a lowercase letter 17 | * Must include an uppercase letter 18 | * Must include a number 19 | 2. Show error messages next to the inputs every time the form is submitted if there are any. If there are no errors alert the message `Success`. 20 | 3. If you did the first 2 steps using refs, repeat the same thing with state instead. If you used state for the first 2 steps instead repeat the same thing with refs. 21 | 22 | ## Bonus 23 | 24 | 1. Make it so that the error messages show up when you submit the form (just like step 2), but also make it so the error messages will automatically update when you change the value in each input but only after the first time you submit the form. 25 | * For example if you type in an email that is incorrect and submit the form it should show an error message. Then when you go back and start making changes to the email input, the error message should update with the current errors as you change the input and disappear when the input is valid. -------------------------------------------------------------------------------- /27-28-use-effect-exercises/before/README.md: -------------------------------------------------------------------------------- 1 | # Before Getting Started 2 | 3 | This project is a bit different since the starting code is a fully working React application. All you need to do is run `npm i` and `npm run dev` to install all dependencies and run the application. You will notice that this code looks nearly identical to the previous project. The only change is I added a button that will toggle between hiding and showing the `Child` component. This is just there to help us test mounting and unmounting of our component. 4 | 5 | You may also notice I have removed `StrictMode` from the `main.jsx` file. I did this to make understanding and deciphering this exercise easier. We will cover what this does in just a few videos. 6 | 7 | 8 | # Instructions 9 | 10 | The following exercises should all be performed within the `Child` component. 11 | 12 | 1. `console.log` the text **Render** each time the component re-renders 13 | 2. `console.log` the text **Hi** when the component mounts 14 | 3. `console.log` the text **My name is {name} and I am {age} years old** whenever the `name` or `age` changes 15 | 4. Update the `document.title` to be equal to `name` whenever the `name` changes 16 | 17 | ## Bonus 18 | 19 | 1. `console.log` the text **Bye** when the component unmounts 20 | 2. Create a timeout that `console.log`s the text **My name is {name}** only after there has been a 1 second delay since the last time the name was changed. 21 | * For example, if I change the name from **Kyle** to **Kyl** and then to **Ky** without having more than 1 second between each name change the console should log nothing until 1 second after I finishing changing the name to **Ky** and then it will log **Ky**. If instead there as greater than 1 second delay between each change, it should log **Kyl** and then **Ky**. Each of those logs would happen exactly 1 second after the name was changed. -------------------------------------------------------------------------------- /49-50-use-fetch-hook/before/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | 3 | // If the API does not work use these local URLs 4 | // const URLS = { 5 | // USERS: "users.json", 6 | // POSTS: "posts.json", 7 | // COMMENTS: "comments.json", 8 | // } 9 | 10 | const URLS = { 11 | USERS: "https://jsonplaceholder.typicode.com/users", 12 | POSTS: "https://jsonplaceholder.typicode.com/posts", 13 | COMMENTS: "https://jsonplaceholder.typicode.com/comments", 14 | } 15 | 16 | // BONUS: 17 | // const OPTIONS = { 18 | // method: "POST", 19 | // body: JSON.stringify({ name: "Kyle" }), 20 | // headers: { 21 | // "Content-type": "application/json", 22 | // }, 23 | // } 24 | 25 | function App() { 26 | const [url, setUrl] = useState(URLS.USERS) 27 | 28 | const { data, isLoading, isError } = useFetch(url) 29 | // BONUS: 30 | // const { data, isLoading, isError } = useFetch(url, OPTIONS) 31 | 32 | return ( 33 | <> 34 |
    35 | 43 | 51 | 59 |
    60 | {isLoading ? ( 61 |

    Loading...

    62 | ) : isError ? ( 63 |

    Error

    64 | ) : ( 65 |
    {JSON.stringify(data, null, 2)}
    66 | )} 67 | 68 | ) 69 | } 70 | 71 | export default App 72 | -------------------------------------------------------------------------------- /30-31-lifecycle-method-exercises/after/src/Child.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | export class Child extends React.Component { 4 | constructor() { 5 | super() 6 | this.state = { 7 | age: 0, 8 | name: "", 9 | } 10 | } 11 | 12 | componentDidMount() { 13 | console.log("Hi") 14 | console.log("Render") 15 | } 16 | 17 | componentDidUpdate(prevProps, prevState) { 18 | console.log("Render") 19 | 20 | if ( 21 | prevState.name !== this.state.name || 22 | prevState.age !== this.state.age 23 | ) { 24 | console.log( 25 | `My name is ${this.state.name} and I am ${this.state.age} years old` 26 | ) 27 | } 28 | 29 | if (prevState.name !== this.state.name) { 30 | document.title = this.state.name 31 | 32 | if (this.nameTimeout != null) clearTimeout(this.nameTimeout) 33 | 34 | this.nameTimeout = setTimeout(() => { 35 | console.log(`My name is ${this.state.name}`) 36 | }, 1000) 37 | } 38 | } 39 | 40 | componentWillUnmount() { 41 | if (this.nameTimeout != null) clearTimeout(this.nameTimeout) 42 | console.log("Bye") 43 | } 44 | 45 | render() { 46 | return ( 47 |
    48 | this.setState({ name: e.target.value })} 52 | /> 53 |
    54 |
    55 | 64 | {this.state.age} 65 | 74 |
    75 |
    76 | My name is {this.state.name} and I am {this.state.age} years old. 77 |
    78 | ) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/router.jsx: -------------------------------------------------------------------------------- 1 | import { createBrowserRouter, Navigate, useRouteError } from "react-router-dom" 2 | import { RootLayout } from "./layouts/RootLayout" 3 | import { editPostRoute } from "./pages/EditPost" 4 | import { newPostRoute } from "./pages/NewPost" 5 | import { postRoute } from "./pages/Post" 6 | import { postListRoute } from "./pages/PostList" 7 | import { todoListRoute } from "./pages/TodoList" 8 | import { userRoute } from "./pages/User" 9 | import { userListRoute } from "./pages/UserList" 10 | 11 | export const router = createBrowserRouter([ 12 | { 13 | path: "/", 14 | element: , 15 | children: [ 16 | { 17 | errorElement: , 18 | children: [ 19 | { index: true, element: }, 20 | { 21 | path: "posts", 22 | children: [ 23 | { 24 | index: true, 25 | ...postListRoute, 26 | }, 27 | { 28 | path: ":postId", 29 | children: [ 30 | { index: true, ...postRoute }, 31 | { path: "edit", ...editPostRoute }, 32 | ], 33 | }, 34 | { path: "new", ...newPostRoute }, 35 | ], 36 | }, 37 | { 38 | path: "users", 39 | children: [ 40 | { index: true, ...userListRoute }, 41 | { path: ":userId", ...userRoute }, 42 | ], 43 | }, 44 | { path: "todos", ...todoListRoute }, 45 | { path: "*", element:

    404 - Page Not Found

    }, 46 | ], 47 | }, 48 | ], 49 | }, 50 | ]) 51 | 52 | function ErrorPage() { 53 | const error = useRouteError() 54 | 55 | return ( 56 | <> 57 |

    Error - Something went wrong

    58 | {import.meta.env.MODE !== "production" && ( 59 | <> 60 |
    {error.message}
    61 |
    {error.stack}
    62 | 63 | )} 64 | 65 | ) 66 | } 67 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/README.md: -------------------------------------------------------------------------------- 1 | # Before Getting Started 2 | 3 | This is going to be another large project, but luckily it won't be as large as the last project. The goal of this project is to utilize actions and forms to allow users to filter, create, and edit posts. The starting code for this project is exactly the same as the ending code from the previous project. You also do not need to worry about updating your CSS file since all the new styles needed for this project were already included in the CSS file from the previous project. 4 | 5 | You will also notice the `html` folder. This just contains the HTML for the pages that need to be changed/added for this project. You can use these files to help you figure out what needs to be changed on the existing pages as well as what to add for the new pages. 6 | 7 | # API Information 8 | 9 | The API has the following _new_ endpoints: 10 | 11 | - `POST /posts` - Create a new post 12 | - `PUT /posts/:id` - Update a post 13 | - `GET /posts?q=&userId=` - Returns all of the posts that match the query and userId 14 | 15 | # Instructions 16 | 17 | 1. Create a New Post page that renders out a form that allows the user to create a new post. Don't forget to add a button to the Posts page linking to the New Post page. The form should have the following fields: 18 | - Title 19 | - Body 20 | - Author (User) 21 | 2. Create an Edit Post page that renders out a form that allows the user to edit an existing post. The form should be identical to the new post form. Don't forget to add a button to the Post page linking to the Edit Post page. 22 | 3. Add a filter to the Posts page that allows the user to filter the posts by a query. 23 | 24 | ## Bonus: 25 | 26 | 1. Add a filter to the Posts page that allows the user to filter the posts by the user that wrote the post. 27 | 2. Disable the submit button on the New Post and Edit Post pages if the form is in the process of submitting. 28 | 3. Handle the following validations on the New Post and Edit Post pages: 29 | - Title is required 30 | - Body is required 31 | - User is required 32 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/components/PostForm.jsx: -------------------------------------------------------------------------------- 1 | import { Form, Link } from "react-router-dom" 2 | import { FormGroup } from "./FormGroup" 3 | 4 | export function PostForm({ 5 | users, 6 | isSubmitting, 7 | errors = {}, 8 | defaultValues = {}, 9 | }) { 10 | return ( 11 |
    12 |
    13 | 14 | 15 | 21 | 22 | 23 | 24 | 31 | 32 |
    33 |
    34 | 35 | 36 | 41 | 42 |
    43 |
    44 | 45 | Cancel 46 | 47 | 50 |
    51 |
    52 | ) 53 | } 54 | 55 | export function postFormValidator({ title, body, userId }) { 56 | const errors = {} 57 | 58 | if (title === "") { 59 | errors.title = "Required" 60 | } 61 | 62 | if (body === "") { 63 | errors.body = "Required" 64 | } 65 | 66 | if (userId === "") { 67 | errors.userId = "Required" 68 | } 69 | 70 | return errors 71 | } 72 | -------------------------------------------------------------------------------- /58-59-basic-form-validation-project/after/src/StateForm.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useMemo } from "react" 2 | import { checkEmail, checkPassword } from "./validators" 3 | 4 | export function StateForm() { 5 | const [email, setEmail] = useState("") 6 | const [password, setPassword] = useState("") 7 | const [isAfterFirstSubmit, setIsAfterFirstSubmit] = useState(false) 8 | 9 | const emailErrors = useMemo(() => { 10 | return isAfterFirstSubmit ? checkEmail(email) : [] 11 | }, [isAfterFirstSubmit, email]) 12 | 13 | const passwordErrors = useMemo(() => { 14 | return isAfterFirstSubmit ? checkPassword(password) : [] 15 | }, [isAfterFirstSubmit, password]) 16 | 17 | function onSubmit(e) { 18 | e.preventDefault() 19 | setIsAfterFirstSubmit(true) 20 | 21 | const emailResults = checkEmail(email) 22 | const passwordResults = checkPassword(password) 23 | 24 | if (emailResults.length === 0 && passwordResults.length === 0) { 25 | alert("Success") 26 | } 27 | } 28 | 29 | return ( 30 |
    31 |
    0 ? "error" : ""}`}> 32 | 35 | setEmail(e.target.value)} 41 | /> 42 | {emailErrors.length > 0 && ( 43 |
    {emailErrors.join(", ")}
    44 | )} 45 |
    46 |
    0 ? "error" : ""}`}> 47 | 50 | setPassword(e.target.value)} 56 | /> 57 | {passwordErrors.length > 0 && ( 58 |
    {passwordErrors.join(", ")}
    59 | )} 60 |
    61 | 64 |
    65 | ) 66 | } 67 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/html/new-post.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 17 |
    18 |

    New Post

    19 |
    20 |
    21 |
    22 | 23 | 24 |
    Required
    25 |
    26 |
    27 | 40 |
    41 |
    42 |
    43 |
    44 | 45 | 46 |
    47 |
    48 |
    49 | Cancel 50 | 51 |
    52 |
    53 |
    54 | 55 | 56 | -------------------------------------------------------------------------------- /58-59-basic-form-validation-project/after/src/RefForm.jsx: -------------------------------------------------------------------------------- 1 | import { useRef, useState } from "react" 2 | import { checkEmail, checkPassword } from "./validators" 3 | 4 | export function RefForm() { 5 | const emailRef = useRef() 6 | const passwordRef = useRef() 7 | 8 | const [emailErrors, setEmailErrors] = useState([]) 9 | const [passwordErrors, setPasswordErrors] = useState([]) 10 | const [isAfterFirstSubmit, setIsAfterFirstSubmit] = useState(false) 11 | 12 | function onSubmit(e) { 13 | e.preventDefault() 14 | setIsAfterFirstSubmit(true) 15 | 16 | const emailResults = checkEmail(emailRef.current.value) 17 | const passwordResults = checkPassword(passwordRef.current.value) 18 | 19 | setEmailErrors(emailResults) 20 | setPasswordErrors(passwordResults) 21 | 22 | if (emailResults.length === 0 && passwordResults.length === 0) { 23 | alert("Success") 24 | } 25 | } 26 | 27 | return ( 28 |
    29 |
    0 ? "error" : ""}`}> 30 | 33 | setEmailErrors(checkEmail(e.target.value)) 37 | : undefined 38 | } 39 | className="input" 40 | type="email" 41 | id="email" 42 | ref={emailRef} 43 | /> 44 | {emailErrors.length > 0 && ( 45 |
    {emailErrors.join(", ")}
    46 | )} 47 |
    48 |
    0 ? "error" : ""}`}> 49 | 52 | setPasswordErrors(checkPassword(e.target.value)) 60 | : undefined 61 | } 62 | /> 63 | {passwordErrors.length > 0 && ( 64 |
    {passwordErrors.join(", ")}
    65 | )} 66 |
    67 | 70 |
    71 | ) 72 | } 73 | -------------------------------------------------------------------------------- /20-array-component/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | 3 | const INITIAL_VALUE = ["A", "B", "C"] 4 | 5 | function App() { 6 | const [array, setArray] = useState(INITIAL_VALUE) 7 | const [value, setValue] = useState("") 8 | 9 | function removeFirstElement() { 10 | setArray(currentArray => { 11 | return currentArray.slice(1) 12 | }) 13 | } 14 | 15 | function removeSpecificLetter(letter) { 16 | setArray(currentArray => { 17 | return currentArray.filter(element => element !== letter) 18 | }) 19 | } 20 | 21 | function addLetterToStart(letter) { 22 | setArray(currentArray => { 23 | return [letter, ...currentArray] 24 | }) 25 | } 26 | 27 | function addLetterToEnd(letter) { 28 | setArray(currentArray => { 29 | return [...currentArray, letter] 30 | }) 31 | } 32 | 33 | function clear() { 34 | setArray([]) 35 | } 36 | 37 | function reset() { 38 | setArray(INITIAL_VALUE) 39 | } 40 | 41 | function updateAToH() { 42 | setArray(currentArray => { 43 | return currentArray.map(element => { 44 | if (element === "A") return "H" 45 | return element 46 | }) 47 | }) 48 | } 49 | 50 | function addLetterAtIndex(letter, index) { 51 | setArray(currentArray => { 52 | return [ 53 | ...currentArray.slice(0, index), 54 | letter, 55 | ...currentArray.slice(index), 56 | ] 57 | }) 58 | } 59 | 60 | return ( 61 |
    62 | 63 |
    64 | 65 |
    66 | 67 |
    68 | 69 |
    70 | 71 |
    72 | 73 |
    74 | 75 |
    76 | 77 |
    78 | setValue(e.target.value)} /> 79 |
    80 | 83 |
    84 | {array.join(", ")} 85 |
    86 | ) 87 | } 88 | 89 | export default App 90 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/before/html/edit-post.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 17 |
    18 |

    Edit Post

    19 |
    20 |
    21 |
    22 | 23 | 24 |
    25 |
    26 | 39 |
    40 |
    41 |
    42 |
    43 | 44 | 50 |
    51 |
    52 |
    53 | Cancel 54 | 55 |
    56 |
    57 |
    58 | 59 | 60 | -------------------------------------------------------------------------------- /61-react-hook-form-project/before/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { FormGroup } from "./FormGroup" 2 | import ReactSelect from "react-select" 3 | import { useRef, useState } from "react" 4 | import "./styles.css" 5 | import { checkCountry, checkEmail, checkPassword } from "./validators" 6 | 7 | const COUNTRY_OPTIONS = [ 8 | { label: "United States", value: "US" }, 9 | { label: "India", value: "IN" }, 10 | { label: "Mexico", value: "MX" }, 11 | ] 12 | 13 | function App() { 14 | const emailRef = useRef() 15 | const passwordRef = useRef() 16 | const countryRef = useRef() 17 | 18 | const [emailErrors, setEmailErrors] = useState([]) 19 | const [passwordErrors, setPasswordErrors] = useState([]) 20 | const [countryErrors, setCountryErrors] = useState([]) 21 | 22 | function onSubmit(e) { 23 | e.preventDefault() 24 | 25 | const emailResults = checkEmail(emailRef.current.value) 26 | const passwordResults = checkPassword(passwordRef.current.value) 27 | const countryResults = checkCountry(countryRef.current.getValue()[0]) 28 | 29 | setEmailErrors(emailResults) 30 | setPasswordErrors(passwordResults) 31 | setCountryErrors(countryResults) 32 | 33 | if ( 34 | emailResults.length === 0 && 35 | passwordResults.length === 0 && 36 | countryResults.length === 0 37 | ) { 38 | alert("Success") 39 | } 40 | } 41 | 42 | return ( 43 |
    44 | 45 | 48 | 49 | 50 | 51 | 54 | 60 | 61 | 62 | 65 | 72 | 73 | 76 |
    77 | ) 78 | } 79 | 80 | export default App 81 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/before/client/todos.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 17 |
    18 |

    Todos

    19 |
      20 |
    • delectus aut autem
    • 21 |
    • quis ut nam facilis et officia qui
    • 22 |
    • fugiat veniam minus
    • 23 |
    • et porro tempora
    • 24 |
    • laboriosam mollitia et enim quasi adipisci quia provident illum
    • 25 |
    • qui ullam ratione quibusdam voluptatem quia omnis
    • 26 |
    • illo expedita consequatur quia in
    • 27 |
    • quo adipisci enim quam ut ab
    • 28 |
    • molestiae perspiciatis ipsa
    • 29 |
    • 30 | illo est ratione doloremque quia maiores aut 31 |
    • 32 |
    • vero rerum temporibus dolor
    • 33 |
    • ipsa repellendus fugit nisi
    • 34 |
    • et doloremque nulla
    • 35 |
    • 36 | repellendus sunt dolores architecto voluptatum 37 |
    • 38 |
    • ab voluptatum amet voluptas
    • 39 |
    • 40 | accusamus eos facilis sint et aut voluptatem 41 |
    • 42 |
    • quo laboriosam deleniti aut qui
    • 43 |
    • dolorum est consequatur ea mollitia in culpa
    • 44 |
    • 45 | molestiae ipsa aut voluptatibus pariatur dolor nihil 46 |
    • 47 |
    • 48 | ullam nobis libero sapiente ad optio sint 49 |
    • 50 |
    • suscipit repellat esse quibusdam voluptatem incidunt
    • 51 |
    • 52 | distinctio vitae autem nihil ut molestias quo 53 |
    • 54 |
    • et itaque necessitatibus maxime molestiae qui quas velit
    • 55 |
    • adipisci non ad dicta qui amet quaerat doloribus ea
    • 56 |
    57 |
    58 | 59 | 60 | -------------------------------------------------------------------------------- /78-79-advanced-blog-project/after/client/src/pages/PostList.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from "react" 2 | import { Form, Link, useLoaderData } from "react-router-dom" 3 | import { getPosts } from "../api/posts" 4 | import { getUsers } from "../api/users" 5 | import { FormGroup } from "../components/FormGroup" 6 | import { PostCard } from "../components/PostCard" 7 | 8 | function PostList() { 9 | const { 10 | posts, 11 | users, 12 | searchParams: { query, userId }, 13 | } = useLoaderData() 14 | const queryRef = useRef() 15 | const userIdRef = useRef() 16 | 17 | useEffect(() => { 18 | queryRef.current.value = query || "" 19 | }, [query]) 20 | 21 | useEffect(() => { 22 | userIdRef.current.value = userId || "" 23 | }, [userId]) 24 | 25 | return ( 26 | <> 27 |

    28 | Posts 29 |
    30 | 31 | New 32 | 33 |
    34 |

    35 | 36 |
    37 |
    38 | 39 | 40 | 41 | 42 | 43 | 44 | 52 | 53 | 54 |
    55 |
    56 | 57 |
    58 | {posts.map(post => ( 59 | 60 | ))} 61 |
    62 | 63 | ) 64 | } 65 | 66 | async function loader({ request: { signal, url } }) { 67 | const searchParams = new URL(url).searchParams 68 | const query = searchParams.get("query") 69 | const userId = searchParams.get("userId") 70 | const filterParams = { q: query } 71 | if (userId !== "") filterParams.userId = userId 72 | 73 | const posts = getPosts({ signal, params: filterParams }) 74 | const users = getUsers({ signal }) 75 | 76 | return { 77 | posts: await posts, 78 | users: await users, 79 | searchParams: { query, userId }, 80 | } 81 | } 82 | 83 | export const postListRoute = { 84 | loader, 85 | element: , 86 | } 87 | -------------------------------------------------------------------------------- /75-76-basic-blog-project/before/client/post.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 17 |
    18 |

    19 | sunt aut facere repellat provident occaecati excepturi optio 20 | reprehenderit 21 |

    22 | By: Leanne Graham 25 |
    26 | quia et suscipit suscipit recusandae consequuntur expedita et cum 27 | reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt 28 | rem eveniet architecto 29 |
    30 |

    Comments

    31 |
    32 |
    33 |
    34 |
    Eliseo@gardner.biz
    35 | laudantium enim quasi est quidem magnam voluptate ipsam eos tempora 36 | quo necessitatibus dolor quam autem quasi reiciendis et nam sapiente 37 | accusantium 38 |
    39 |
    40 |
    41 |
    42 |
    Jayne_Kuhic@sydney.com
    43 | est natus enim nihil est dolore omnis voluptatem numquam et omnis 44 | occaecati quod ullam at voluptatem error expedita pariatur nihil 45 | sint nostrum voluptatem reiciendis et 46 |
    47 |
    48 |
    49 |
    50 |
    Nikita@garfield.biz
    51 | quia molestiae reprehenderit quasi aspernatur aut expedita occaecati 52 | aliquam eveniet laudantium omnis quibusdam delectus saepe quia 53 | accusamus maiores nam est cum et ducimus et vero voluptates 54 | excepturi deleniti ratione 55 |
    56 |
    57 |
    58 |
    59 |
    Lew@alysha.tv
    60 | non et atque occaecati deserunt quas accusantium unde odit nobis qui 61 | voluptatem quia voluptas consequuntur itaque dolor et qui rerum 62 | deleniti ut occaecati 63 |
    64 |
    65 |
    66 |
    67 |
    Hayden@althea.biz
    68 | harum non quasi et ratione tempore iure ex voluptates in ratione 69 | harum architecto fugit inventore cupiditate voluptates magni quo et 70 |
    71 |
    72 |
    73 |
    74 | 75 | 76 | -------------------------------------------------------------------------------- /68-69-advanced-todo-list/after/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { createContext, useEffect, useReducer, useState } from "react" 2 | import { NewTodoForm } from "./NewTodoForm" 3 | import "./styles.css" 4 | import { TodoFilterForm } from "./TodoFilterForm" 5 | import { TodoList } from "./TodoList" 6 | 7 | const LOCAL_STORAGE_KEY = "TODOS" 8 | const ACTIONS = { 9 | ADD: "ADD", 10 | UPDATE: "UPDATE", 11 | TOGGLE: "TOGGLE", 12 | DELETE: "DELETE", 13 | } 14 | 15 | function reducer(todos, { type, payload }) { 16 | switch (type) { 17 | case ACTIONS.ADD: 18 | return [ 19 | ...todos, 20 | { name: payload.name, completed: false, id: crypto.randomUUID() }, 21 | ] 22 | case ACTIONS.TOGGLE: 23 | return todos.map(todo => { 24 | if (todo.id === payload.id) { 25 | return { ...todo, completed: payload.completed } 26 | } 27 | 28 | return todo 29 | }) 30 | case ACTIONS.DELETE: 31 | return todos.filter(todo => todo.id !== payload.id) 32 | case ACTIONS.UPDATE: 33 | return todos.map(todo => { 34 | if (todo.id === payload.id) { 35 | return { ...todo, name: payload.name } 36 | } 37 | 38 | return todo 39 | }) 40 | default: 41 | throw new Error(`No action found for ${type}.`) 42 | } 43 | } 44 | 45 | export const TodoContext = createContext() 46 | 47 | function App() { 48 | const [filterName, setFilterName] = useState("") 49 | const [hideCompletedFilter, setHideCompletedFilter] = useState(false) 50 | const [todos, dispatch] = useReducer(reducer, [], initialValue => { 51 | const value = localStorage.getItem(LOCAL_STORAGE_KEY) 52 | if (value == null) return initialValue 53 | 54 | return JSON.parse(value) 55 | }) 56 | 57 | const filteredTodos = todos.filter(todo => { 58 | if (hideCompletedFilter && todo.completed) return false 59 | return todo.name.includes(filterName) 60 | }) 61 | 62 | useEffect(() => { 63 | localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(todos)) 64 | }, [todos]) 65 | 66 | function addNewTodo(name) { 67 | dispatch({ type: ACTIONS.ADD, payload: { name } }) 68 | } 69 | 70 | function toggleTodo(todoId, completed) { 71 | dispatch({ type: ACTIONS.TOGGLE, payload: { id: todoId, completed } }) 72 | } 73 | 74 | function updateTodoName(id, name) { 75 | dispatch({ type: ACTIONS.UPDATE, payload: { id, name } }) 76 | } 77 | 78 | function deleteTodo(todoId) { 79 | dispatch({ type: ACTIONS.DELETE, payload: { id: todoId } }) 80 | } 81 | 82 | return ( 83 | 92 | 98 | 99 | 100 | 101 | ) 102 | } 103 | 104 | export default App 105 | --------------------------------------------------------------------------------