Skip to content

Commit

Permalink
finish exercises and in-code instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
kentcdodds committed Jun 28, 2024
1 parent ca7c48d commit 7dca270
Show file tree
Hide file tree
Showing 15 changed files with 383 additions and 52 deletions.
3 changes: 2 additions & 1 deletion exercises/01.use-state/01.problem.initial-state/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { createRoot } from 'react-dom/client'

// 🐨 create a `useState` function which accepts the initial state and returns
// an array of the state and a function to update it.
// an array of the state and a no-op function: () => {}
// 🦺 note you may need to ignore some typescript errors here. We'll fix them later.
// Feel free to make the `useState` a generic though!

function Counter() {
// @ts-expect-error 💣 delete this comment
const [count, setCount] = useState(0)
// 🦺 you'll get an error for this we'll fix that next
const increment = () => setCount(count + 1)
return (
<div className="counter">
Expand Down
5 changes: 3 additions & 2 deletions exercises/01.use-state/01.solution.initial-state/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { createRoot } from 'react-dom/client'

function useState<State>(initialState: State) {
let state = initialState
const setState = (newState: State) => (state = newState)
const state = initialState
const setState = () => {}
return [state, setState] as const
}

function Counter() {
const [count, setCount] = useState(0)
// @ts-expect-error we'll fix this soon
const increment = () => setCount(count + 1)
return (
<div className="counter">
Expand Down
7 changes: 5 additions & 2 deletions exercises/01.use-state/02.problem.update-state/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { createRoot } from 'react-dom/client'

function useState<State>(initialState: State) {
let state = initialState
const setState = (newState: State) => (state = newState)
// 🐨 change this to let
const state = initialState
// 🐨 update this to accept newState and assign state to that
const setState = () => {}
return [state, setState] as const
}

function Counter() {
const [count, setCount] = useState(0)
// @ts-expect-error 💣 delete this comment
const increment = () => setCount(count + 1)
return (
<div className="counter">
Expand Down
5 changes: 5 additions & 0 deletions exercises/01.use-state/03.problem.re-render/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createRoot } from 'react-dom/client'

function useState<State>(initialState: State) {
let state = initialState
// 🐨 update this function to call render after setting the state
const setState = (newState: State) => (state = newState)
return [state, setState] as const
}
Expand All @@ -19,4 +20,8 @@ function Counter() {
const rootEl = document.createElement('div')
document.body.append(rootEl)
const appRoot = createRoot(rootEl)

// 🐨 place this in a new function called render
appRoot.render(<Counter />)

// 🐨 call render here to kick things off
12 changes: 10 additions & 2 deletions exercises/01.use-state/03.solution.re-render/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { createRoot } from 'react-dom/client'

function useState<State>(initialState: State) {
let state = initialState
const setState = (newState: State) => (state = newState)
const setState = (newState: State) => {
state = newState
render()
}
return [state, setState] as const
}

Expand All @@ -19,4 +22,9 @@ function Counter() {
const rootEl = document.createElement('div')
document.body.append(rootEl)
const appRoot = createRoot(rootEl)
appRoot.render(<Counter />)

function render() {
appRoot.render(<Counter />)
}

render()
21 changes: 19 additions & 2 deletions exercises/01.use-state/04.problem.preserve-state/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import { createRoot } from 'react-dom/client'

// 🐨 create state and setState variables here using let
// 🦺 set their type to "any"

function useState<State>(initialState: State) {
// 🐨 remove the "let" and "const" here so this function references the
// variables declared above
// 🐨 Next, change this so we only do these assignments if the state is undefined
let state = initialState
const setState = (newState: State) => (state = newState)
const setState = (newState: State) => {
state = newState
render()
}
// 🦺 because our state and setState are now typed as any, you may choose to
// update this to as [State, (newState: State) => void] so we can preserve
// the type of state
return [state, setState] as const
}

Expand All @@ -19,4 +31,9 @@ function Counter() {
const rootEl = document.createElement('div')
document.body.append(rootEl)
const appRoot = createRoot(rootEl)
appRoot.render(<Counter />)

function render() {
appRoot.render(<Counter />)
}

render()
21 changes: 17 additions & 4 deletions exercises/01.use-state/04.solution.preserve-state/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import { createRoot } from 'react-dom/client'

let state: any, setState: any

function useState<State>(initialState: State) {
let state = initialState
const setState = (newState: State) => (state = newState)
return [state, setState] as const
if (state === undefined) {
state = initialState
setState = (newState: State) => {
state = newState
render()
}
}
return [state, setState] as [State, (newState: State) => void]
}

function Counter() {
const [count, setCount] = useState(0)
const increment = () => setCount(count + 1)

return (
<div className="counter">
<button onClick={increment}>{count}</button>
Expand All @@ -19,4 +27,9 @@ function Counter() {
const rootEl = document.createElement('div')
document.body.append(rootEl)
const appRoot = createRoot(rootEl)
appRoot.render(<Counter />)

function render() {
appRoot.render(<Counter />)
}

render()
41 changes: 36 additions & 5 deletions exercises/02.multiple-hooks/01.problem.phase/index.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,53 @@
import { createRoot } from 'react-dom/client'

// 🐨 create two Symbols for the phase: "INITIALIZATION" and "UPDATE"
// 💯 as extra credit, give them a descriptive name

// 🦺 create a type called Phase which is the typeof INITIALIZATION | typeof UPDATE

// 🐨 create a variable called phase of type Phase and set it to INITIALIZATION

let state: any, setState: any

function useState<State>(initialState: State) {
let state = initialState
const setState = (newState: State) => (state = newState)
return [state, setState] as const
// 🐨 change this to check whether the phase is INITIALIZATION
if (state === undefined) {
state = initialState
setState = (newState: State) => {
state = newState
// 🐨 pass the UPDATE phase to render here
render()
}
}
return [state, setState] as [State, (newState: State) => void]
}

function Counter() {
const [count, setCount] = useState(0)
const increment = () => setCount(count + 1)

const [enabled, setEnabled] = useState(true)
const toggle = () => setEnabled(!enabled)

return (
<div className="counter">
<button onClick={increment}>{count}</button>
<button onClick={toggle}>{enabled ? 'Disable' : 'Enable'}</button>
<button disabled={!enabled} onClick={increment}>
{count}
</button>
</div>
)
}

const rootEl = document.createElement('div')
document.body.append(rootEl)
const appRoot = createRoot(rootEl)
appRoot.render(<Counter />)

// 🐨 accept a newPhase argument
function render() {
// 🐨 assign the phase to the newPhase
appRoot.render(<Counter />)
}

// 🐨 call this with the INITIALIZATION phase
render()
35 changes: 30 additions & 5 deletions exercises/02.multiple-hooks/01.solution.phase/index.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,47 @@
import { createRoot } from 'react-dom/client'

const INITIALIZATION = Symbol('phase.initialization')
const UPDATE = Symbol('phase.update')
type Phase = typeof INITIALIZATION | typeof UPDATE
let phase: Phase

let state: any, setState: any

function useState<State>(initialState: State) {
let state = initialState
const setState = (newState: State) => (state = newState)
return [state, setState] as const
if (phase === INITIALIZATION) {
state = initialState
setState = (newState: State) => {
state = newState
render(UPDATE)
}
}
return [state, setState] as [State, (newState: State) => void]
}

function Counter() {
const [count, setCount] = useState(0)
const increment = () => setCount(count + 1)

const [enabled, setEnabled] = useState(true)
const toggle = () => setEnabled(!enabled)

return (
<div className="counter">
<button onClick={increment}>{count}</button>
<button onClick={toggle}>{enabled ? 'Disable' : 'Enable'}</button>
<button disabled={!enabled} onClick={increment}>
{count}
</button>
</div>
)
}

const rootEl = document.createElement('div')
document.body.append(rootEl)
const appRoot = createRoot(rootEl)
appRoot.render(<Counter />)

function render(newPhase: Phase) {
phase = newPhase
appRoot.render(<Counter />)
}

render(INITIALIZATION)
45 changes: 40 additions & 5 deletions exercises/02.multiple-hooks/02.problem.hook-id/index.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,57 @@
import { createRoot } from 'react-dom/client'

const INITIALIZATION = Symbol('phase.initialization')
const UPDATE = Symbol('phase.update')
type Phase = typeof INITIALIZATION | typeof UPDATE
let phase: Phase
// 🐨 make a hookIndex variable here that starts at 0
// 🐨 make a variable called "states" which is an array of arrays (one for each
// return value of a useState call)

// 💣 delete these variable declarations
let state: any, setState: any

function useState<State>(initialState: State) {
let state = initialState
const setState = (newState: State) => (state = newState)
return [state, setState] as const
// 🐨 create a variable called "id" and assign it to "hookIndex++"
if (phase === INITIALIZATION) {
// 🐨 assign states[id] to an array with the initialState and the setState function
// rather than assigning the values to the old variables
state = initialState
setState = (newState: State) => {
// 🐨 instead of reassigning the variable state to the newState, update states[id][0] to it.
state = newState
render(UPDATE)
}
}
// 🐨 return the value at states[id] instead of the old variables
return [state, setState] as [State, (newState: State) => void]
}

function Counter() {
const [count, setCount] = useState(0)
const increment = () => setCount(count + 1)

const [enabled, setEnabled] = useState(true)
const toggle = () => setEnabled(!enabled)

return (
<div className="counter">
<button onClick={increment}>{count}</button>
<button onClick={toggle}>{enabled ? 'Disable' : 'Enable'}</button>
<button disabled={!enabled} onClick={increment}>
{count}
</button>
</div>
)
}

const rootEl = document.createElement('div')
document.body.append(rootEl)
const appRoot = createRoot(rootEl)
appRoot.render(<Counter />)

function render(newPhase: Phase) {
// 🐨 set the hookIndex to 0
phase = newPhase
appRoot.render(<Counter />)
}

render(INITIALIZATION)
39 changes: 34 additions & 5 deletions exercises/02.multiple-hooks/02.solution.hook-id/index.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,51 @@
import { createRoot } from 'react-dom/client'

const INITIALIZATION = Symbol('phase.initialization')
const UPDATE = Symbol('phase.update')
type Phase = typeof INITIALIZATION | typeof UPDATE
let phase: Phase
let hookIndex = 0
const states: Array<[any, (newState: any) => void]> = []

function useState<State>(initialState: State) {
let state = initialState
const setState = (newState: State) => (state = newState)
return [state, setState] as const
const id = hookIndex++
if (phase === INITIALIZATION) {
states[id] = [
initialState,
(newState: State) => {
states[id][0] = newState
render(UPDATE)
},
]
}
return states[id] as [State, (newState: State) => void]
}

function Counter() {
const [count, setCount] = useState(0)
const increment = () => setCount(count + 1)

const [enabled, setEnabled] = useState(true)
const toggle = () => setEnabled(!enabled)

return (
<div className="counter">
<button onClick={increment}>{count}</button>
<button onClick={toggle}>{enabled ? 'Disable' : 'Enable'}</button>
<button disabled={!enabled} onClick={increment}>
{count}
</button>
</div>
)
}

const rootEl = document.createElement('div')
document.body.append(rootEl)
const appRoot = createRoot(rootEl)
appRoot.render(<Counter />)

function render(newPhase: Phase) {
hookIndex = 0
phase = newPhase
appRoot.render(<Counter />)
}

render(INITIALIZATION)
Loading

0 comments on commit 7dca270

Please sign in to comment.