Skip to content

Commit f5dda2e

Browse files
Round 2
1 parent f7bbcdb commit f5dda2e

File tree

11 files changed

+262
-1
lines changed

11 files changed

+262
-1
lines changed

src/10-useFetch/FetchComponent.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { useState } from "react"
2+
import useFetch from "./useFetch"
3+
4+
export default function FetchComponent() {
5+
const [id, setId] = useState(1)
6+
const { loading, error, value } = useFetch(
7+
`https://jsonplaceholder.typicode.com/todos/${id}`,
8+
{},
9+
[id]
10+
)
11+
12+
return (
13+
<div>
14+
<div>{id}</div>
15+
<button onClick={() => setId(currentId => currentId + 1)}>
16+
Increment ID
17+
</button>
18+
<div>Loading: {loading.toString()}</div>
19+
<div>{JSON.stringify(error, null, 2)}</div>
20+
<div>{JSON.stringify(value, null, 2)}</div>
21+
</div>
22+
)
23+
}

src/10-useFetch/useFetch.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import useAsync from "../9-useAsync/useAsync"
2+
3+
const DEFAULT_OPTIONS = {
4+
headers: { "Content-Type": "application/json" },
5+
}
6+
7+
export default function useFetch(url, options = {}, dependencies = []) {
8+
return useAsync(() => {
9+
return fetch(url, { ...DEFAULT_OPTIONS, ...options }).then(res => {
10+
if (res.ok) return res.json()
11+
return res.json().then(json => Promise.reject(json))
12+
})
13+
}, dependencies)
14+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { useState } from "react"
2+
import usePrevious from "./usePrevious"
3+
4+
export default function PreviousComponent() {
5+
const [count, setCount] = useState(0)
6+
const [name, setName] = useState("Kyle")
7+
const previousCount = usePrevious(count)
8+
9+
return (
10+
<div>
11+
<div>
12+
{count} - {previousCount}
13+
</div>
14+
<div>{name}</div>
15+
<button onClick={() => setCount(currentCount => currentCount + 1)}>
16+
Increment
17+
</button>
18+
<button onClick={() => setName("John")}>Change Name</button>
19+
</div>
20+
)
21+
}

src/6-usePrevious/usePrevious.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { useRef } from "react"
2+
3+
export default function usePrevious(value) {
4+
const currentRef = useRef(value)
5+
const previousRef = useRef()
6+
7+
if (currentRef.current !== value) {
8+
previousRef.current = currentRef.current
9+
currentRef.current = value
10+
}
11+
12+
return previousRef.current
13+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { useState } from "react"
2+
import useStateWithHistory from "./useStateWithHistory"
3+
4+
export default function StateWithHistoryComponent() {
5+
const [count, setCount, { history, pointer, back, forward, go }] =
6+
useStateWithHistory(1)
7+
const [name, setName] = useState("Kyle")
8+
9+
return (
10+
<div>
11+
<div>{count}</div>
12+
<div>{history.join(", ")}</div>
13+
<div>Pointer - {pointer}</div>
14+
<div>{name}</div>
15+
<button onClick={() => setCount(currentCount => currentCount * 2)}>
16+
Double
17+
</button>
18+
<button onClick={() => setCount(currentCount => currentCount + 1)}>
19+
Increment
20+
</button>
21+
<button onClick={back}>Back</button>
22+
<button onClick={forward}>Forward</button>
23+
<button onClick={() => go(2)}>Go To Index 2</button>
24+
<button onClick={() => setName("John")}>Change Name</button>
25+
</div>
26+
)
27+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { useCallback, useRef, useState } from "react"
2+
3+
export default function useStateWithHistory(
4+
defaultValue,
5+
{ capacity = 10 } = {}
6+
) {
7+
const [value, setValue] = useState(defaultValue)
8+
const historyRef = useRef([value])
9+
const pointerRef = useRef(0)
10+
11+
const set = useCallback(
12+
v => {
13+
const resolvedValue = typeof v === "function" ? v(value) : v
14+
if (historyRef.current[pointerRef.current] !== resolvedValue) {
15+
if (pointerRef.current < historyRef.current.length - 1) {
16+
historyRef.current.splice(pointerRef.current + 1)
17+
}
18+
historyRef.current.push(resolvedValue)
19+
20+
while (historyRef.current.length > capacity) {
21+
historyRef.current.shift()
22+
}
23+
pointerRef.current = historyRef.current.length - 1
24+
}
25+
setValue(resolvedValue)
26+
},
27+
[capacity, value]
28+
)
29+
30+
const back = useCallback(() => {
31+
if (pointerRef.current <= 0) return
32+
pointerRef.current--
33+
setValue(historyRef.current[pointerRef.current])
34+
}, [])
35+
36+
const forward = useCallback(() => {
37+
if (pointerRef.current >= historyRef.current.length - 1) return
38+
pointerRef.current++
39+
setValue(historyRef.current[pointerRef.current])
40+
}, [])
41+
42+
const go = useCallback(index => {
43+
if (index < 0 || index >= historyRef.current.length - 1) return
44+
pointerRef.current = index
45+
setValue(historyRef.current[pointerRef.current])
46+
}, [])
47+
48+
return [
49+
value,
50+
set,
51+
{
52+
history: historyRef.current,
53+
pointer: pointerRef.current,
54+
back,
55+
forward,
56+
go,
57+
},
58+
]
59+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { useSessionStorage, useLocalStorage } from "./useStorage"
2+
3+
export default function StorageComponent() {
4+
const [name, setName, removeName] = useSessionStorage("name", "Kyle")
5+
const [age, setAge, removeAge] = useLocalStorage("age", 26)
6+
7+
return (
8+
<div>
9+
<div>
10+
{name} - {age}
11+
</div>
12+
<button onClick={() => setName("John")}>Set Name</button>
13+
<button onClick={() => setAge(40)}>Set Age</button>
14+
<button onClick={removeName}>Remove Name</button>
15+
<button onClick={removeAge}>Remove Age</button>
16+
</div>
17+
)
18+
}

src/8-useStorage/useStorage.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { useCallback, useState, useEffect } from "react"
2+
3+
export function useLocalStorage(key, defaultValue) {
4+
return useStorage(key, defaultValue, window.localStorage)
5+
}
6+
7+
export function useSessionStorage(key, defaultValue) {
8+
return useStorage(key, defaultValue, window.sessionStorage)
9+
}
10+
11+
function useStorage(key, defaultValue, storageObject) {
12+
const [value, setValue] = useState(() => {
13+
const jsonValue = storageObject.getItem(key)
14+
if (jsonValue != null) return JSON.parse(jsonValue)
15+
16+
if (typeof initialValue === "function") {
17+
return defaultValue()
18+
} else {
19+
return defaultValue
20+
}
21+
})
22+
23+
useEffect(() => {
24+
if (value === undefined) return storageObject.removeItem(key)
25+
storageObject.setItem(key, JSON.stringify(value))
26+
}, [key, value, storageObject])
27+
28+
const remove = useCallback(() => {
29+
setValue(undefined)
30+
}, [])
31+
32+
return [value, setValue, remove]
33+
}

src/9-useAsync/AsyncComponent.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import useAsync from "./useAsync"
2+
3+
export default function AsyncComponent() {
4+
const { loading, error, value } = useAsync(() => {
5+
return new Promise((resolve, reject) => {
6+
const success = false
7+
setTimeout(() => {
8+
success ? resolve("Hi") : reject("Error")
9+
}, 1000)
10+
})
11+
})
12+
13+
return (
14+
<div>
15+
<div>Loading: {loading.toString()}</div>
16+
<div>{error}</div>
17+
<div>{value}</div>
18+
</div>
19+
)
20+
}

src/9-useAsync/useAsync.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { useCallback, useEffect, useState } from "react"
2+
3+
export default function useAsync(callback, dependencies = []) {
4+
const [loading, setLoading] = useState(true)
5+
const [error, setError] = useState()
6+
const [value, setValue] = useState()
7+
8+
const callbackMemoized = useCallback(() => {
9+
setLoading(true)
10+
setError(undefined)
11+
setValue(undefined)
12+
callback()
13+
.then(setValue)
14+
.catch(setError)
15+
.finally(() => setLoading(false))
16+
}, dependencies)
17+
18+
useEffect(() => {
19+
callbackMemoized()
20+
}, [callbackMemoized])
21+
22+
return { loading, error, value }
23+
}

0 commit comments

Comments
 (0)