Skip to content

Commit c7e4fa2

Browse files
committed
De-effectify some utilities
1 parent 09187e0 commit c7e4fa2

File tree

5 files changed

+68
-59
lines changed

5 files changed

+68
-59
lines changed

src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import useLocalStorage from "@app/hooks/localStorage"
1313
import { LocalStorageKeys } from "@app/utils/defaults"
1414

1515
function App() {
16-
const [colorMode] = useLocalStorage(LocalStorageKeys.colorMode, "light")
16+
const [colorMode] = useLocalStorage(LocalStorageKeys.colorMode, "system")
1717

1818
return (
1919
<CssVarsProvider

src/components/PopupAlertDisplay.tsx

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CloseRounded } from "@mui/icons-material"
22
import { Alert, IconButton, Stack, Typography } from "@mui/joy"
3-
import { useEffect, useState } from "react"
3+
import { useState } from "react"
44

55
import { AlertId, AlertType, useAlerts } from "@app/hooks/alerts"
66

@@ -31,34 +31,28 @@ export default function PopupAlertDisplay() {
3131
commands: { clear },
3232
} = useAlerts()
3333

34-
// Clear out used alerts
35-
useEffect(() => {
36-
const alertIds = new Set(alerts.map((a) => a.id))
37-
const clearedIds = Array.from(alertTimeouts.keys()).filter(
38-
(alertId) => !alertIds.has(alertId)
39-
)
40-
41-
if (clearedIds.length === 0) return
34+
const alertIds = new Set(alerts.map((a) => a.id))
35+
const clearedIds = Array.from(alertTimeouts.keys()).filter(
36+
(alertId) => !alertIds.has(alertId)
37+
)
4238

39+
if (clearedIds.length > 0) {
4340
const newAlertTimeouts = new Map(alertTimeouts)
44-
clearedIds.forEach((alertId) => newAlertTimeouts.delete(alertId))
41+
clearedIds.forEach((id) => newAlertTimeouts.delete(id))
4542

4643
setAlertTimeouts(newAlertTimeouts)
47-
}, [alerts, alertTimeouts, setAlertTimeouts])
44+
}
4845

49-
// Initiate timeouts for new alerts
50-
useEffect(() => {
51-
alerts.forEach((alert) => {
52-
if (alertTimeouts.has(alert.id)) return
46+
alerts.forEach((alert) => {
47+
if (alertTimeouts.has(alert.id)) return
5348

54-
alertTimeouts.set(
55-
alert.id,
56-
setTimeout(() => {
57-
clear(alert.id)
58-
}, alert.durationMillis)
59-
)
60-
})
61-
}, [alerts, alertTimeouts, clear])
49+
alertTimeouts.set(
50+
alert.id,
51+
setTimeout(() => {
52+
clear(alert.id)
53+
}, alert.durationMillis)
54+
)
55+
})
6256

6357
const handleClose = (alertId: AlertId) => {
6458
return () => clear(alertId)

src/globals.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { StorageUpdateEvent } from "@app/hooks/localStorage"
2+
3+
declare global {
4+
interface WindowEventMap {
5+
"rsseditor:storage-update": StorageUpdateEvent
6+
}
7+
}
8+
9+
export {}

src/hooks/feedTransform.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createContext, useContext, useEffect } from "react"
1+
import { createContext, useContext } from "react"
22

33
import useLocalStorage from "@app/hooks/localStorage"
44
import { LocalStorageKeys } from "@app/utils/defaults"
@@ -92,18 +92,13 @@ export const useFeedTransform = () => useContext(FeedTransformContext)
9292
export const useFeedTransformDispatch = () =>
9393
useContext(FeedTransformDispatchContext)
9494

95-
export const useStoredFeedTransform = (
95+
export function useStoredFeedTransform(
9696
initial: FeedTransform
97-
): [FeedTransform, React.Dispatch<React.SetStateAction<FeedTransform>>] => {
97+
): [FeedTransform, (newValue: FeedTransform) => void] {
9898
const [storedFeedTransform, setStoredFeedTransform] = useLocalStorage(
9999
LocalStorageKeys.feedTransform,
100100
initial
101101
)
102102

103-
// Runs migrations if needed exactly once on mount
104-
useEffect(() => {
105-
setStoredFeedTransform(migrate(storedFeedTransform))
106-
}, []) // eslint-disable-line react-hooks/exhaustive-deps
107-
108103
return [migrate(storedFeedTransform), setStoredFeedTransform]
109104
}

src/hooks/localStorage.ts

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState } from "react"
1+
import { useCallback, useSyncExternalStore } from "react"
22

33
import { LocalStorageKeys } from "@app/utils/defaults"
44

@@ -11,38 +11,49 @@ export type Storable =
1111
| Storable[]
1212
| { [key: string]: Storable }
1313

14-
function coerce<T extends Storable>(
15-
value: string,
16-
{ fallback }: { fallback: T }
17-
): T {
18-
try {
19-
return JSON.parse(value) as T
20-
} catch (e) {
21-
console.error(e)
22-
return fallback
23-
}
14+
type KeyValue = (typeof LocalStorageKeys)[keyof typeof LocalStorageKeys]
15+
16+
type UpdateEventPayload = {
17+
key: string
2418
}
2519

26-
type KeyValue = (typeof LocalStorageKeys)[keyof typeof LocalStorageKeys]
20+
export class StorageUpdateEvent extends CustomEvent<UpdateEventPayload> {
21+
static type: keyof WindowEventMap = "rsseditor:storage-update" as const
22+
23+
constructor(payload: UpdateEventPayload) {
24+
super(StorageUpdateEvent.type, { detail: payload })
25+
}
26+
}
2727

2828
export default function useLocalStorage<T extends Storable>(
2929
key: KeyValue,
3030
initialValue: T
31-
): [T, React.Dispatch<React.SetStateAction<T>>] {
32-
const [value, setValue] = useState(() => {
33-
if (!window.localStorage) return initialValue
34-
35-
const currentValue = localStorage.getItem(key)
36-
return currentValue
37-
? coerce<T>(currentValue, { fallback: initialValue })
38-
: initialValue
39-
})
40-
41-
useEffect(() => {
42-
if (window.localStorage) {
31+
): [T, (newValue: T) => void] {
32+
const currentStoredItem = useSyncExternalStore(
33+
(callback) => {
34+
const listener = (evt: StorageUpdateEvent) => {
35+
if (evt.detail.key === key) callback()
36+
}
37+
38+
window.addEventListener("rsseditor:storage-update", listener)
39+
return () => {
40+
window.removeEventListener("rsseditor:storage-update", listener)
41+
}
42+
},
43+
() => localStorage.getItem(key)
44+
)
45+
46+
const setStoredValue = useCallback(
47+
(value: T) => {
4348
localStorage.setItem(key, JSON.stringify(value))
44-
}
45-
}, [key, value])
49+
window.dispatchEvent(new StorageUpdateEvent({ key }))
50+
},
51+
[key]
52+
)
53+
54+
const value: T = currentStoredItem
55+
? JSON.parse(currentStoredItem)
56+
: initialValue
4657

47-
return [value, setValue]
58+
return [value, setStoredValue]
4859
}

0 commit comments

Comments
 (0)