-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #744 from thunderstore-io/ssr-context
CS: Implement SSR compatible dapper context
- Loading branch information
Showing
9 changed files
with
102 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
"use client"; | ||
import React from "react"; | ||
|
||
import { Dapper, DapperProvider } from "@thunderstore/dapper/src"; | ||
import { API_DOMAIN } from "@/utils/constants"; | ||
|
||
export function ClientDapper(props: React.PropsWithChildren) { | ||
const dapperConstructor = () => new Dapper(API_DOMAIN, undefined); | ||
return ( | ||
<DapperProvider dapperConstructor={dapperConstructor}> | ||
<>{props.children}</> | ||
</DapperProvider> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import React from "react"; | ||
|
||
import { Dapper, DapperProvider } from "@thunderstore/dapper/src"; | ||
import { API_DOMAIN } from "@/utils/constants"; | ||
|
||
export function ServerDapper(props: React.PropsWithChildren) { | ||
const dapperConstructor = () => new Dapper(API_DOMAIN, undefined); | ||
return ( | ||
<DapperProvider dapperConstructor={dapperConstructor}> | ||
<>{props.children}</> | ||
</DapperProvider> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,27 @@ | ||
import React from "react"; | ||
import { PropsWithChildren } from "react"; | ||
|
||
import { DapperInterface } from "./dapper"; | ||
import { getDapperContext } from "./singleton"; | ||
|
||
type ContextProps = { dapper: DapperInterface; children?: React.ReactNode }; | ||
const DapperContext = React.createContext<DapperInterface | null>(null); | ||
type DapperProviderProps = PropsWithChildren<{ | ||
dapperConstructor: () => DapperInterface; | ||
}>; | ||
|
||
export function DapperProvider(props: ContextProps) { | ||
const { dapper, children } = props; | ||
return ( | ||
<DapperContext.Provider value={dapper}>{children}</DapperContext.Provider> | ||
); | ||
export function DapperProvider(props: DapperProviderProps) { | ||
/** | ||
* Does NOT support changing the dapper instance after initialization. The | ||
* dapper instance will be created only once regardless of prop changes and | ||
* bound to the global scope. On NextJS the instance may be shared between all | ||
* requests handled by the server depending on the server configuration. | ||
* | ||
* TREAT AS A GLOBAL SINGLETON THAT'S SHARED ACROSS THE ENTIRE PROCESS. | ||
*/ | ||
const dapperContext = getDapperContext(); | ||
dapperContext.initialize(props.dapperConstructor); | ||
return <>{props.children}</>; | ||
} | ||
|
||
export const useDapper = (): DapperInterface => { | ||
const contextState = React.useContext(DapperContext); | ||
|
||
if (contextState === null) { | ||
throw new Error("useDapper must be used within a DapperProvider tag"); | ||
} | ||
|
||
return contextState; | ||
const dapperContext = getDapperContext(); | ||
return dapperContext.getDapper(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,42 @@ | ||
/* | ||
Singleton variant of dapper initialization, as React context is not yet | ||
supported in NextJS 13. | ||
Usage: | ||
- Call `SetDapperSingleton` in the initialization phase of the app | ||
- Use the `useDapper` React hook as usual afterwards. | ||
*/ | ||
import { DapperInterface } from "./dapper"; | ||
|
||
let instance: DapperInterface | null = null; | ||
interface GlobalContext { | ||
Dapper?: DapperContext; | ||
} | ||
|
||
function getGlobalContext(): GlobalContext { | ||
if (typeof window === "undefined") { | ||
return globalThis as unknown as GlobalContext; | ||
} else { | ||
if (globalThis as unknown) { | ||
return globalThis as unknown as GlobalContext; | ||
} else { | ||
return window as unknown as GlobalContext; | ||
} | ||
} | ||
} | ||
|
||
const globalContext = getGlobalContext(); | ||
|
||
class DapperContext { | ||
private dapper?: DapperInterface = undefined; | ||
|
||
export function SetDapperSingleton(dapper: DapperInterface) { | ||
instance = dapper; | ||
public initialize(dapperConstructor: () => DapperInterface) { | ||
if (this.dapper) return; | ||
this.dapper = dapperConstructor(); | ||
} | ||
|
||
public getDapper(): DapperInterface { | ||
if (!this.dapper) { | ||
throw new Error("Attempted to access dapper before initialization!"); | ||
} | ||
return this.dapper; | ||
} | ||
} | ||
|
||
export function GetDapperSingleton(): DapperInterface { | ||
if (instance == null) { | ||
throw new Error("Attempted to access dapper before initialization!"); | ||
export function getDapperContext(): DapperContext { | ||
if (!globalContext.Dapper) { | ||
globalContext.Dapper = new DapperContext(); | ||
} | ||
return instance; | ||
return globalContext.Dapper; | ||
} |