Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
at main 2.6 kB view raw
1import * as React from 'react'; 2import { ServerInsertedHTMLContext } from 'next/navigation'; 3import type { UrqlResult } from './useUrqlValue'; 4import { htmlEscapeJsonString } from './htmlescape'; 5 6interface DataHydrationValue { 7 isInjecting: boolean; 8 operationValuesByKey: Record<number, UrqlResult>; 9 RehydrateScript: () => 10 | React.DetailedReactHTMLElement< 11 { dangerouslySetInnerHTML: { __html: string } }, 12 HTMLElement 13 > 14 | React.FunctionComponentElement<any>; 15} 16 17const DataHydrationContext = React.createContext< 18 DataHydrationValue | undefined 19>(undefined); 20 21function transportDataToJS(data: any) { 22 const key = 'urql_transport'; 23 return `(window[Symbol.for("${key}")] ??= []).push(${htmlEscapeJsonString( 24 JSON.stringify(data) 25 )})`; 26} 27 28export const DataHydrationContextProvider = ({ 29 nonce, 30 children, 31}: React.PropsWithChildren<{ nonce?: string }>) => { 32 const dataHydrationContext = React.useRef<DataHydrationValue>(); 33 if (typeof window == 'undefined') { 34 if (!dataHydrationContext.current) 35 dataHydrationContext.current = buildContext({ nonce }); 36 } 37 38 return React.createElement( 39 DataHydrationContext.Provider, 40 { value: dataHydrationContext.current }, 41 children 42 ); 43}; 44 45export function useDataHydrationContext(): DataHydrationValue | undefined { 46 const dataHydrationContext = React.useContext(DataHydrationContext); 47 const insertHtml = React.useContext(ServerInsertedHTMLContext as any) as ( 48 cb: () => any 49 ) => any; 50 51 if (typeof window !== 'undefined') return; 52 53 if (insertHtml && dataHydrationContext && !dataHydrationContext.isInjecting) { 54 dataHydrationContext.isInjecting = true; 55 insertHtml(() => 56 React.createElement(dataHydrationContext.RehydrateScript, {}) 57 ); 58 } 59 return dataHydrationContext; 60} 61 62let key = 0; 63function buildContext({ nonce }: { nonce?: string }): DataHydrationValue { 64 const dataHydrationContext: DataHydrationValue = { 65 isInjecting: false, 66 operationValuesByKey: {}, 67 RehydrateScript() { 68 dataHydrationContext.isInjecting = false; 69 if (!Object.keys(dataHydrationContext.operationValuesByKey).length) 70 return React.createElement(React.Fragment); 71 72 const __html = transportDataToJS({ 73 rehydrate: { ...dataHydrationContext.operationValuesByKey }, 74 }); 75 76 dataHydrationContext.operationValuesByKey = {}; 77 78 return React.createElement('script', { 79 key: key++, 80 nonce: nonce, 81 dangerouslySetInnerHTML: { __html }, 82 }); 83 }, 84 }; 85 86 return dataHydrationContext; 87}