From aa8abad17ddaa7e39214afec0116959d318bdfb1 Mon Sep 17 00:00:00 2001 From: Adrian <107351903+6lr61@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:44:28 +0200 Subject: [PATCH] refactor: don't allow null for context throw if the provider is missing and the context doesn't have a value give it that kasper je ne sais quoi --- src/App.tsx | 14 +++------ src/components/BadgeList.tsx | 2 +- src/components/LoginButton.tsx | 14 ++++----- src/components/MentionSegment.tsx | 6 ++-- .../{AuthStateContext.ts => AuthContext.ts} | 8 ++++- ...AuthStateProvider.tsx => AuthProvider.tsx} | 10 ++----- src/contexts/badges/TwitchBadgeContext.ts | 11 +++---- src/contexts/badges/TwitchBadgeProvider.tsx | 29 ++++++++++--------- src/contexts/event-sub/EventSubContext.ts | 8 ++++- src/hooks/useEventSub.ts | 16 ++-------- src/hooks/useGetProfile.ts | 5 ++-- src/hooks/useTwitchChat.ts | 16 ++-------- src/main.tsx | 6 ++-- src/utils/api/event-sub/subscribe.ts | 2 +- src/utils/api/event-sub/unsubscribe.ts | 2 +- src/utils/event-sub/EventSub.ts | 2 +- 16 files changed, 66 insertions(+), 85 deletions(-) rename src/contexts/auth-state/{AuthStateContext.ts => AuthContext.ts} (73%) rename src/contexts/auth-state/{AuthStateProvider.tsx => AuthProvider.tsx} (92%) diff --git a/src/App.tsx b/src/App.tsx index f34c74e..bd1cb28 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,11 +1,11 @@ import { useContext, useMemo } from "react"; -import { AuthStateContext } from "./contexts/auth-state/AuthStateContext"; +import { AuthContext } from "./contexts/auth-state/AuthContext"; import LoginButton from "./components/LoginButton"; import Chat from "./components/Chat"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; export default function App() { - const authContext = useContext(AuthStateContext); + const { authState } = useContext(AuthContext); const queryClient = useMemo( () => new QueryClient({ @@ -14,19 +14,13 @@ export default function App() { [] ); - if (!authContext) { - return

Missing AuthStateContext provider?

; - } - return ( <> - {authContext.authState && ( + {authState && (
-

- Hello: {authContext.authState.user.login} -

+

Hello: {authState.user.login}

Chat Messages:

diff --git a/src/components/BadgeList.tsx b/src/components/BadgeList.tsx index 1fc0b57..e917032 100644 --- a/src/components/BadgeList.tsx +++ b/src/components/BadgeList.tsx @@ -20,7 +20,7 @@ export default function BadgeList({ }: Props): React.ReactElement | undefined { const twitchBadges = useContext(TwitchBadgeConext); - if (!twitchBadges || badges.length === 0) { + if (badges.length === 0) { return; } diff --git a/src/components/LoginButton.tsx b/src/components/LoginButton.tsx index 48a0259..50315be 100644 --- a/src/components/LoginButton.tsx +++ b/src/components/LoginButton.tsx @@ -1,22 +1,18 @@ import { useContext } from "react"; -import { AuthStateContext } from "../contexts/auth-state/AuthStateContext"; +import { AuthContext } from "../contexts/auth-state/AuthContext"; export default function LoginButton(): React.ReactElement { - const context = useContext(AuthStateContext); + const { authState, login, signOut } = useContext(AuthContext); - if (!context) { - throw new Error("LoginButton: Missing AuthStateContext"); - } - - if (context.authState) { + if (authState) { return ( - ); } else { return ( - ); diff --git a/src/components/MentionSegment.tsx b/src/components/MentionSegment.tsx index fd5aff2..ebca731 100644 --- a/src/components/MentionSegment.tsx +++ b/src/components/MentionSegment.tsx @@ -1,5 +1,5 @@ import { useContext } from "react"; -import { AuthStateContext } from "../contexts/auth-state/AuthStateContext"; +import { AuthContext } from "../contexts/auth-state/AuthContext"; interface MentionProps { text: string; @@ -9,12 +9,12 @@ export default function MentionSegment({ text, }: MentionProps): React.ReactElement { const mentioned = text.replace("@", "").toLocaleLowerCase(); - const authStateContext = useContext(AuthStateContext); + const { authState } = useContext(AuthContext); return ( void; } -export const AuthStateContext = createContext(null); +export const AuthContext = createContext( + new Proxy({} as Value, { + get() { + throw new Error("AuthContext must be provided"); + }, + }) +); diff --git a/src/contexts/auth-state/AuthStateProvider.tsx b/src/contexts/auth-state/AuthProvider.tsx similarity index 92% rename from src/contexts/auth-state/AuthStateProvider.tsx rename to src/contexts/auth-state/AuthProvider.tsx index 00d9c82..eeb18fc 100644 --- a/src/contexts/auth-state/AuthStateProvider.tsx +++ b/src/contexts/auth-state/AuthProvider.tsx @@ -3,11 +3,11 @@ import { useLocalStorage } from "../../hooks/useLocalStorage"; import { validateToken } from "../../utils/validateToken"; import type { OAuthMessage } from "../../callback"; import { revokeToken } from "../../utils/revokeToken"; -import { type AuthState, AuthStateContext } from "./AuthStateContext"; +import { type AuthState, AuthContext } from "./AuthContext"; const OAUTH2_URL = "https://id.twitch.tv/oauth2/authorize"; -export default function AuthStateProvider({ +export default function AuthProvider({ children, }: { children: React.ReactNode; @@ -101,9 +101,5 @@ export default function AuthStateProvider({ const value = { login, signOut, authState }; - return ( - - {children} - - ); + return {children}; } diff --git a/src/contexts/badges/TwitchBadgeContext.ts b/src/contexts/badges/TwitchBadgeContext.ts index 241d520..ccb6914 100644 --- a/src/contexts/badges/TwitchBadgeContext.ts +++ b/src/contexts/badges/TwitchBadgeContext.ts @@ -1,9 +1,10 @@ import { createContext } from "react"; import type { Badge, BadgeSet } from "../../utils/api/getBadges"; -export type TwitchBadges = Map< - `${BadgeSet["set_id"]}/${Badge["id"]}`, - Omit ->; +export type BadgeKey = `${BadgeSet["set_id"]}/${Badge["id"]}`; +export type BadgeValue = Omit; +export type TwitchBadges = Map; -export const TwitchBadgeConext = createContext(null); +export const TwitchBadgeConext = createContext( + new Map() +); diff --git a/src/contexts/badges/TwitchBadgeProvider.tsx b/src/contexts/badges/TwitchBadgeProvider.tsx index 71fed8f..b537ead 100644 --- a/src/contexts/badges/TwitchBadgeProvider.tsx +++ b/src/contexts/badges/TwitchBadgeProvider.tsx @@ -1,6 +1,11 @@ import { useContext, useEffect, useState } from "react"; -import { TwitchBadgeConext, type TwitchBadges } from "./TwitchBadgeContext"; -import { AuthStateContext } from "../auth-state/AuthStateContext"; +import { + TwitchBadgeConext, + type BadgeKey, + type BadgeValue, + type TwitchBadges, +} from "./TwitchBadgeContext"; +import { AuthContext } from "../auth-state/AuthContext"; import { getBadges } from "../../utils/api/getBadges"; interface Props { @@ -10,21 +15,19 @@ interface Props { export default function TwitchBadgeProvider({ children, }: Props): React.ReactElement { - const [badges, setBadges] = useState(null); - const authStateContext = useContext(AuthStateContext); + const [badges, setBadges] = useState( + () => new Map() + ); + const { authState } = useContext(AuthContext); useEffect(() => { - if (!authStateContext?.authState) { + let keep = true; + + if (!authState) { return; } - let keep = true; - - getBadges( - authStateContext.authState.token.value, - authStateContext.authState.client.id, - authStateContext.authState.user.id - ) + getBadges(authState.token.value, authState.client.id, authState.user.id) .then((sets) => { const tmp = sets.flatMap(({ set_id, versions }) => versions.map(({ id, ...rest }) => [`${set_id}/${id}`, rest] as const) @@ -41,7 +44,7 @@ export default function TwitchBadgeProvider({ return () => { keep = false; }; - }, [authStateContext]); + }, [authState]); return ( diff --git a/src/contexts/event-sub/EventSubContext.ts b/src/contexts/event-sub/EventSubContext.ts index a97fe6c..52789c8 100644 --- a/src/contexts/event-sub/EventSubContext.ts +++ b/src/contexts/event-sub/EventSubContext.ts @@ -5,4 +5,10 @@ interface Value { subscribe: typeof EventSub.instance.subscribe; } -export const EventSubContext = createContext(null); +export const EventSubContext = createContext( + new Proxy({} as Value, { + get() { + throw new Error("EventSubContext must be provided"); + }, + }) +); diff --git a/src/hooks/useEventSub.ts b/src/hooks/useEventSub.ts index 0c80878..cde2ab5 100644 --- a/src/hooks/useEventSub.ts +++ b/src/hooks/useEventSub.ts @@ -1,25 +1,17 @@ import { useCallback, useContext, useEffect, useState } from "react"; import { EventSubContext } from "../contexts/event-sub/EventSubContext"; -import { AuthStateContext } from "../contexts/auth-state/AuthStateContext"; +import { AuthContext } from "../contexts/auth-state/AuthContext"; export function useEventSub( type: string | string[], condition: Record, bufferSize = 50 ) { - const authStateContext = useContext(AuthStateContext); + const { authState } = useContext(AuthContext); const eventSubContext = useContext(EventSubContext); const [messages, setMessages] = useState[]>([]); const [lastMessage, setLastMessage] = useState>(); - if (!authStateContext) { - throw new Error("useEventSub: Needs an AuthStateContext Provider!"); - } - - if (!eventSubContext) { - throw new Error("useEventSub: Needs a EventSubContext Provider!"); - } - const { subscribe } = eventSubContext; const handleMessage = useCallback( @@ -31,8 +23,6 @@ export function useEventSub( ); useEffect(() => { - const { authState } = authStateContext; - if (!authState) { return; } @@ -46,7 +36,7 @@ export function useEventSub( } return subscribe(authState, type, condition, handleMessage); - }, [authStateContext, condition, handleMessage, subscribe, type]); + }, [authState, condition, handleMessage, subscribe, type]); return { lastMessage, messages }; } diff --git a/src/hooks/useGetProfile.ts b/src/hooks/useGetProfile.ts index 23b486d..1238886 100644 --- a/src/hooks/useGetProfile.ts +++ b/src/hooks/useGetProfile.ts @@ -1,11 +1,10 @@ import { useContext } from "react"; -import { AuthStateContext } from "../contexts/auth-state/AuthStateContext"; +import { AuthContext } from "../contexts/auth-state/AuthContext"; import { getUser } from "../utils/api/getUser"; import { useQuery } from "@tanstack/react-query"; export function useGetProfile(login: string) { - const authStateContext = useContext(AuthStateContext); - const authState = authStateContext?.authState; + const { authState } = useContext(AuthContext); return useQuery({ enabled: !!authState, diff --git a/src/hooks/useTwitchChat.ts b/src/hooks/useTwitchChat.ts index 96e5753..a839a34 100644 --- a/src/hooks/useTwitchChat.ts +++ b/src/hooks/useTwitchChat.ts @@ -1,5 +1,5 @@ import { useCallback, useContext, useEffect, useState } from "react"; -import { AuthStateContext } from "../contexts/auth-state/AuthStateContext"; +import { AuthContext } from "../contexts/auth-state/AuthContext"; import type { ChatMessageEvent } from "../utils/event-sub/events/chat/message"; import { EventSubContext } from "../contexts/event-sub/EventSubContext"; import type { ChatEventCommon } from "../utils/event-sub/events/chat/_common"; @@ -31,18 +31,10 @@ const subscriptions = [ ]; export function useTwitchChat(bufferSize = 50, channelId?: string) { - const authStateContext = useContext(AuthStateContext); + const { authState } = useContext(AuthContext); const eventSubContext = useContext(EventSubContext); const [messages, setMessages] = useState([]); - if (!authStateContext) { - throw new Error("useTwitchChat: Needs an AuthStateContext Provider!"); - } - - if (!eventSubContext) { - throw new Error("useTwitchChat: Needs a EventSubContext Provider!"); - } - const { subscribe } = eventSubContext; const handleMessage = useCallback( @@ -82,8 +74,6 @@ export function useTwitchChat(bufferSize = 50, channelId?: string) { ); useEffect(() => { - const { authState } = authStateContext; - if (!authState) { return; } @@ -94,7 +84,7 @@ export function useTwitchChat(bufferSize = 50, channelId?: string) { }; return subscribe(authState, subscriptions, condition, handleMessage); - }, [authStateContext, channelId, handleMessage, subscribe]); + }, [authState, channelId, handleMessage, subscribe]); return messages; } diff --git a/src/main.tsx b/src/main.tsx index cdedc3c..ec7917b 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,7 +2,7 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import App from "./App.tsx"; import "./index.css"; -import AuthStateProvider from "./contexts/auth-state/AuthStateProvider.tsx"; +import AuthProvider from "./contexts/auth-state/AuthProvider.tsx"; import EventSubProvider from "./contexts/event-sub/EventSubProvider.tsx"; const rootElement = document.querySelector("#root"); @@ -13,10 +13,10 @@ if (!(rootElement instanceof HTMLDivElement)) { createRoot(rootElement).render( - + - + ); diff --git a/src/utils/api/event-sub/subscribe.ts b/src/utils/api/event-sub/subscribe.ts index a577d1c..1469da2 100644 --- a/src/utils/api/event-sub/subscribe.ts +++ b/src/utils/api/event-sub/subscribe.ts @@ -1,4 +1,4 @@ -import type { AuthState } from "../../../contexts/auth-state/AuthStateContext"; +import type { AuthState } from "../../../contexts/auth-state/AuthContext"; interface SubscriptionsResponse { data: [ diff --git a/src/utils/api/event-sub/unsubscribe.ts b/src/utils/api/event-sub/unsubscribe.ts index ba59c36..1980ba9 100644 --- a/src/utils/api/event-sub/unsubscribe.ts +++ b/src/utils/api/event-sub/unsubscribe.ts @@ -1,4 +1,4 @@ -import type { AuthState } from "../../../contexts/auth-state/AuthStateContext"; +import type { AuthState } from "../../../contexts/auth-state/AuthContext"; export async function unsubscribe( authState: AuthState, diff --git a/src/utils/event-sub/EventSub.ts b/src/utils/event-sub/EventSub.ts index 550ff45..13c45d6 100644 --- a/src/utils/event-sub/EventSub.ts +++ b/src/utils/event-sub/EventSub.ts @@ -1,4 +1,4 @@ -import type { AuthState } from "../../contexts/auth-state/AuthStateContext"; +import type { AuthState } from "../../contexts/auth-state/AuthContext"; import { subscribe } from "../api/event-sub/subscribe"; import type { EventSubMessage,