├── 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 | setShow(currentShow => !currentShow)}>
12 | Show/Hide
13 |
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 | setShow(currentShow => !currentShow)}>
12 | Show/Hide
13 |
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 | setShow(currentShow => !currentShow)}>
12 | Show/Hide
13 |
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 | setShow(currentShow => !currentShow)}>
12 | Show/Hide
13 |
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 |
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 |
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 |
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 | setAge(a => a - 1)}>-
13 | {age}
14 | setAge(a => a + 1)}>+
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 |
5 | toggleTodo(id, e.target.checked)}
10 | />
11 | {name}
12 |
13 | deleteTodo(id)} data-button-delete>
14 | Delete
15 |
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 |
5 | toggleTodo(id, e.target.checked)}
10 | />
11 | {name}
12 |
13 | deleteTodo(id)} data-button-delete>
14 | Delete
15 |
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 | setAge(currentAge => currentAge - 1)}>-
13 | {age}
14 | setAge(currentAge => currentAge + 1)}>+
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 |
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 |
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 |
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 |
15 | My App
16 |
17 |
18 | Posts
19 |
20 |
21 | Users
22 |
23 |
24 | Todos
25 |
26 |
27 |
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 |
15 | My App
16 |
17 |
18 | Posts
19 |
20 |
21 | Users
22 |
23 |
24 | Todos
25 |
26 |
27 |
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 |
15 | My App
16 |
17 |
18 | Posts
19 |
20 |
21 | Users
22 |
23 |
24 | Todos
25 |
26 |
27 |
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 |
24 | this.setState(state => {
25 | return { age: state.age - 1 }
26 | })
27 | }
28 | >
29 | -
30 |
31 | {this.state.age}
32 |
34 | this.setState(state => {
35 | return { age: state.age + 1 }
36 | })
37 | }
38 | >
39 | +
40 |
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 | set([4, 5, 6])}>Set to [4, 5, 6]
21 | push(4)}>Push 4
22 | replace(1, 9)}>
23 | Replace the second element with 9
24 |
25 | filter(n => n < 3)}>
26 | Keep numbers less than 3
27 |
28 | remove(1)}>Remove second element
29 | Clear
30 | Reset
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 |
27 |
28 |
29 | New Todo
30 |
31 | Add Todo
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 | setAge(a => a - 1)}>-
41 | {age}
42 | setAge(a => a + 1)}>+
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 | set([4, 5, 6])}>Set to [4, 5, 6]
23 | push(4)}>Push 4
24 | replace(1, 9)}>
25 | Replace the second element with 9
26 |
27 | filter(n => n < 3)}>
28 | Keep numbers less than 3
29 |
30 | remove(1)}>Remove second element
31 | Clear
32 | Reset
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 |
24 | this.setState(currentState => {
25 | return {
26 | age: currentState.age - 1,
27 | }
28 | })
29 | }
30 | >
31 | -
32 |
33 | {this.state.age}
34 |
36 | this.setState(currentState => {
37 | return {
38 | age: currentState.age + 1,
39 | }
40 | })
41 | }
42 | >
43 | +
44 |
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 |
25 | ) : (
26 | <>
27 |
28 | toggleTodo(id, e.target.checked)}
33 | />
34 | {name}
35 |
36 | setIsEditing(true)}>
37 | Edit
38 |
39 | deleteTodo(id)} data-button-delete>
40 | Delete
41 |
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 | First Name
26 | setFirstName(e.target.value)}
30 | />
31 |
32 |
33 |
41 | Last Name
42 | setLastName(e.target.value)}
46 | />
47 |
48 |
49 | {hobbies.join(", ")}
50 |
52 | setHobbies(currentHobbies => [...currentHobbies, "New Hobby"])
53 | }
54 | >
55 | Add Hobby
56 |
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 |
26 | setUrl(URLS.USERS)}
30 | />
31 | Users
32 |
33 |
34 | setUrl(URLS.POSTS)}
38 | />
39 | Posts
40 |
41 |
42 | setUrl(URLS.COMMENTS)}
46 | />
47 | Comments
48 |
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 | First Name
26 | setFirstName(e.target.value)}
30 | />
31 |
32 |
33 | {/* Bonus: */}
34 | {/*
42 | Last Name
43 | setLastName(e.target.value)}
47 | />
48 |
*/}
49 |
50 | {/* Bonus: */}
51 | {/* {hobbies.join(", ")}
52 |
54 | setHobbies(currentHobbies => [...currentHobbies, "New Hobby"])
55 | }
56 | >
57 | Add Hobby
58 | */}
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 | New Todo
54 | setNewTodoName(e.target.value)}
59 | />
60 | Add Todo
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 | New Todo
54 | setNewTodoName(e.target.value)}
59 | />
60 | Add Todo
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 |
19 |
45 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/68-69-advanced-todo-list/before/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
9 |
19 |
45 |
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 |
36 | setUrl(URLS.USERS)}
40 | />
41 | Users
42 |
43 |
44 | setUrl(URLS.POSTS)}
48 | />
49 | Posts
50 |
51 |
52 | setUrl(URLS.COMMENTS)}
56 | />
57 | Comments
58 |
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 |
57 | this.setState(state => {
58 | return { age: state.age - 1 }
59 | })
60 | }
61 | >
62 | -
63 |
64 | {this.state.age}
65 |
67 | this.setState(state => {
68 | return { age: state.age + 1 }
69 | })
70 | }
71 | >
72 | +
73 |
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 |
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 |
33 | Email
34 |
35 |
setEmail(e.target.value)}
41 | />
42 | {emailErrors.length > 0 && (
43 |
{emailErrors.join(", ")}
44 | )}
45 |
46 | 0 ? "error" : ""}`}>
47 |
48 | Password
49 |
50 |
setPassword(e.target.value)}
56 | />
57 | {passwordErrors.length > 0 && (
58 |
{passwordErrors.join(", ")}
59 | )}
60 |
61 |
62 | Submit
63 |
64 |
65 | )
66 | }
67 |
--------------------------------------------------------------------------------
/78-79-advanced-blog-project/before/html/new-post.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document
7 |
8 |
9 |
10 | My App
11 |
16 |
17 |
18 |
New Post
19 |
20 |
42 |
48 |
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 |
31 | Email
32 |
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 |
50 | Password
51 |
52 |
setPasswordErrors(checkPassword(e.target.value))
60 | : undefined
61 | }
62 | />
63 | {passwordErrors.length > 0 && (
64 |
{passwordErrors.join(", ")}
65 | )}
66 |
67 |
68 | Submit
69 |
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 | Remove First Element
63 |
64 | removeSpecificLetter("B")}>Remove All B's
65 |
66 | addLetterToStart("B")}>Add To Start
67 |
68 | addLetterToEnd("Z")}>Add To End
69 |
70 | Clear
71 |
72 | Reset
73 |
74 | Update A To H
75 |
76 | addLetterAtIndex("C", 2)}>Add C At 2
77 |
78 | setValue(e.target.value)} />
79 |
80 | addLetterToStart(value)}>
81 | Add Value To Array
82 |
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 |
10 | My App
11 |
16 |
17 |
18 |
Edit Post
19 |
20 |
41 |
52 |
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 |
46 | Email
47 |
48 |
49 |
50 |
51 |
52 | Password
53 |
54 |
60 |
61 |
62 |
63 | Country
64 |
65 |
72 |
73 |
74 | Submit
75 |
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 |
10 | My App
11 |
16 |
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 | Query
40 |
41 |
42 |
43 | Author
44 |
45 | Any
46 | {users.map(user => (
47 |
48 | {user.name}
49 |
50 | ))}
51 |
52 |
53 | Filter
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 |
10 | My App
11 |
16 |
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 |
--------------------------------------------------------------------------------