├── .gitignore ├── README.md ├── components ├── Accordion.final.js ├── Accordion.js ├── Badge.final.js ├── Badge.js ├── CharacterLimit.final.js ├── CharacterLimit.js ├── CharacterSheet.final.js ├── CharacterSheet.js ├── Counter.final.js ├── Counter.js ├── CounterInput.js ├── DatePicker.final.js ├── DatePicker.js ├── Description.js ├── EffectExample.js ├── Pokemon.final.js ├── Pokemon.js └── Todo.js ├── jsconfig.json ├── package.json ├── pages ├── exercises │ ├── 1-badges.js │ ├── 2-character-counter.js │ ├── 3-effect-examples.js │ ├── 4-date-picker.js │ └── 5-todo.js ├── index.js └── lessons │ ├── 1-props-and-state.js │ ├── 2-controlled-components.js │ ├── 3-effects.js │ ├── 4-compound-components.js │ └── 5-reducers.js ├── public └── favicon.ico └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | 21 | # debug 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # local env files 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## React Hooks Workshop 2 | 3 | Go ahead and clone this repo, and then run: 4 | 5 | ``` 6 | yarn install 7 | 8 | // or 9 | 10 | npm install 11 | ``` 12 | 13 | To run the rest of the content, you'll do: 14 | 15 | ``` 16 | yarn run dev 17 | 18 | // or 19 | 20 | npm run dev 21 | 22 | ``` 23 | 24 | ...and then you'll open up your localhost:3000 (or other port if your computer changes it). 25 | 26 | We'll be live-coding the entire time, so get your fingers ready! 27 | -------------------------------------------------------------------------------- /components/Accordion.final.js: -------------------------------------------------------------------------------- 1 | import { Fragment, useState, useContext, createContext } from 'react' 2 | import Description from '@components/Description' 3 | 4 | function Accordion({ data }) { 5 | const [activeIndex, setActiveIndex] = useState(0) 6 | return ( 7 |
8 | {data.map((tab, index) => { 9 | const isActive = index === activeIndex 10 | return ( 11 |
12 |
setActiveIndex(index)} 16 | > 17 | {tab.label} 18 | {tab.icon} 19 |
20 |
21 | ) 22 | })} 23 |
24 | ) 25 | } 26 | 27 | let AccordionContext = createContext() 28 | 29 | function AccordionCC({ children }) { 30 | let [activeIndex, setActiveIndex] = useState(0) 31 | return ( 32 |
33 | {children.map((child, index) => { 34 | return ( 35 | 39 | {child} 40 | 41 | ) 42 | })} 43 |
44 | ) 45 | } 46 | 47 | let SectionContext = createContext() 48 | 49 | function Section({ children, disabled }) { 50 | return ( 51 | 52 |
{children}
53 |
54 | ) 55 | } 56 | 57 | function Title({ children }) { 58 | let { index, activeIndex, setActiveIndex } = useContext(AccordionContext) 59 | let isActive = index === activeIndex 60 | let { disabled } = useContext(SectionContext) 61 | return ( 62 |
{ 65 | if (!disabled) setActiveIndex(index) 66 | }} 67 | className={disabled ? 'disabled' : isActive ? 'expanded' : ''} 68 | > 69 | {children} 70 |
71 | ) 72 | } 73 | 74 | function Content({ children }) { 75 | let { index, activeIndex } = useContext(AccordionContext) 76 | let isActive = index === activeIndex 77 | return ( 78 |
79 | {children} 80 |
81 | ) 82 | } 83 | 84 | function App() { 85 | return ( 86 |
87 | 88 |
89 | 90 | Paris <span>🧀</span> 91 | 92 | 93 | 94 | 95 |
96 |
97 | 98 | Lech <span>⛷</span> 99 | 100 | 101 | 102 | 103 |
104 |
105 | 106 | Madrid <span>🍷</span> 107 | 108 | 109 | 110 | 111 |
112 |
113 | 181 |
182 | ) 183 | } 184 | 185 | export default App 186 | -------------------------------------------------------------------------------- /components/Accordion.js: -------------------------------------------------------------------------------- 1 | import { Fragment, useState } from 'react' 2 | import Description from '@components/Description' 3 | 4 | function Accordion({ data }) { 5 | const [activeIndex, setActiveIndex] = useState(0) 6 | return ( 7 |
8 | {data.map((tab, index) => { 9 | const isActive = index === activeIndex 10 | return ( 11 | 12 |
setActiveIndex(index)} 16 | > 17 | {tab.label} 18 | {tab.icon} 19 |
20 |
21 | {tab.content} 22 |
23 |
24 | ) 25 | })} 26 |
27 | ) 28 | } 29 | 30 | function App() { 31 | const data = [ 32 | { 33 | label: 'Paris', 34 | icon: '🧀', 35 | content: , 36 | }, 37 | { 38 | label: 'Lech', 39 | icon: '⛷', 40 | content: , 41 | }, 42 | { 43 | label: 'Madrid', 44 | icon: '🍷', 45 | content: , 46 | }, 47 | ] 48 | 49 | return ( 50 |
51 | 52 | 120 |
121 | ) 122 | } 123 | 124 | export default App 125 | -------------------------------------------------------------------------------- /components/Badge.final.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | 3 | const Badge = ({ children, color, dismissable }) => { 4 | const [dismissed, setDismissed] = useState(false) 5 | 6 | if (dismissed) return null 7 | 8 | return ( 9 | { 12 | if (dismissable) setDismissed(true) 13 | }} 14 | > 15 | {children} 16 | 17 | ) 18 | } 19 | 20 | export default function BadgeList() { 21 | return ( 22 |
23 |

Oh boy, statuses!

24 | Success This is operational.
25 | 26 | Removed 27 | {' '} 28 | This is critical.
29 | 30 | Warning 31 | {' '} 32 | This is a warning.
33 | Beta This is in progress.
34 | 68 |
69 | ) 70 | } 71 | -------------------------------------------------------------------------------- /components/Badge.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | 3 | // Change this file so that: 4 | // 1) The Badge Component takes in the correct props 5 | // 2) If a badge is dismissable, when you click on it, it renders as null 6 | 7 | // Hints: 8 | // 1) Use the provided styles to guide you! 9 | // 2) Remember useState() returns [someState, setSomeState] 10 | 11 | // Bonus points: Try making it so the look of the badge changes somehow 12 | // when you hover over it, perhaps an x for if you're about to dismiss it? 13 | 14 | const Badge = () => { 15 | return Hello, world! 16 | } 17 | 18 | export default function BadgeList() { 19 | return ( 20 |
21 |

Oh boy, statuses!

22 | Success This is operational.
23 | 24 | Removed 25 | {' '} 26 | This is critical.
27 | 28 | Warning 29 | {' '} 30 | This is a warning.
31 | Beta This is in progress.
32 | 66 |
67 | ) 68 | } 69 | -------------------------------------------------------------------------------- /components/CharacterLimit.final.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | 3 | const CharacterLimitInput = ({ text, defaults }) => { 4 | const [message, setMessage] = useState('') 5 | const maxLength = 20 6 | 7 | return ( 8 |
maxLength ? 'tooLong' : ''}`} 10 | > 11 |
12 | {defaults.map((b) => { 13 | return ( 14 | 22 | ) 23 | })} 24 |
25 |