Skip to content

Commit

Permalink
feat(test) Radix ui provider in ssr relay
Browse files Browse the repository at this point in the history
  • Loading branch information
Thiago-Mota-Santos committed Sep 11, 2023
1 parent 7deed42 commit 092940d
Show file tree
Hide file tree
Showing 11 changed files with 647 additions and 129 deletions.
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@radix-ui/react-alert-dialog": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-toast": "^1.1.4",
"@radix-ui/themes": "^1.1.2",
"@types/jsonwebtoken": "^9.0.2",
"@types/node": "20.4.1",
"@types/react": "18.2.14",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/components/NoAppointment.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Button } from '@radix-ui/themes'
import DialogButton from './DialogButton'

function NoAppointment() {
Expand All @@ -8,6 +9,7 @@ function NoAppointment() {
Welcome! 🚀 Create your first Appointment
</h2>
<DialogButton />
<Button>Teste </Button>
</div>
</div>
)
Expand Down
17 changes: 17 additions & 0 deletions apps/web/src/components/providers/Providers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Theme } from '@radix-ui/themes';
import React from 'react';
import { RelayEnvironmentProvider, useRelayEnvironment } from 'react-relay';

type ProvidersProps = { children: React.ReactNode };

const Providers = ({ children }: ProvidersProps) => {
const environment = useRelayEnvironment();

return (
<RelayEnvironmentProvider environment={environment}>
<Theme>{children}</Theme>
</RelayEnvironmentProvider>
);
};

export default Providers;
43 changes: 43 additions & 0 deletions apps/web/src/components/providers/RelayProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { RelayEnvironmentProvider } from 'react-relay';
import type { FetchFunction } from 'relay-runtime';
import { Environment, Network, RecordSource, Store } from 'relay-runtime';

/**
*
* Define a function that fetches the results of an operation (query/mutation)
* and returns its results as a Promise.
*/
const fetchQuery: FetchFunction = async (
operation,
variables,
// cacheConfig,
// uploadables
) => {
const response = await fetch('/', {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
query: operation.text,
variables,
}),
});

return await response.json();
};

/**
* Create a network layer from the fetch function
*/
const network = Network.create(fetchQuery);
const store = new Store(new RecordSource());

const environment = new Environment({
network,
store,
});

export const RelayProvider = ({ children }: { children: React.ReactNode }) => {
return <RelayEnvironmentProvider environment={environment}>{children}</RelayEnvironmentProvider>;
};
1 change: 1 addition & 0 deletions apps/web/src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import '@/styles/globals.css'
import '@radix-ui/themes/styles.css';
import type { AppProps } from 'next/app'
import { Suspense } from 'react'
import { ReactRelayContainer } from '../relay/ReactRelayContainer'
Expand Down
77 changes: 35 additions & 42 deletions apps/web/src/relay/ReactRelayContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,70 +1,63 @@
/*eslint-disable @typescript-eslint/no-unused-vars*/
/*eslint-disable @typescript-eslint/no-explicit-any*/
import { NextComponentType, NextPageContext } from 'next'
import { Suspense, useMemo } from 'react'
import { useRelayEnvironment, RelayEnvironmentProvider } from 'react-relay'
import { createEnvironment } from './environment'
import type { NextComponentType, NextPage, NextPageContext } from 'next';
import React, { Suspense, useMemo } from 'react';
import { ReactRelayContext, useRelayEnvironment } from 'react-relay';

import Providers from '../components/providers/Providers';
import { createEnvironment } from './RelayEnvironment';

type NextPageWithLayout<T> = NextPage<T> & {
getLayout?: (page: React.ReactElement) => React.ReactNode;
};

export function ReactRelayContainer({
Component,
props,
}: {
Component: NextComponentType<NextPageContext, any, any>
props: any
Component: NextComponentType<NextPageContext, any, any>;
props: any;
}) {
const environment = useMemo(() => createEnvironment(), [])

const environment = useMemo(() => createEnvironment(), []);
return (
<RelayEnvironmentProvider environment={environment}>
<ReactRelayContext.Provider value={{ environment }}>
<Suspense fallback={null}>
<Hyderate Component={Component} props={props} />
<Hydrate Component={Component} props={props} />
</Suspense>
</RelayEnvironmentProvider>
)
</ReactRelayContext.Provider>
);
}

function Hyderate({
Component,
props,
}: {
Component: NextComponentType<NextPageContext, any, any>
props: any
}) {
const environment = useRelayEnvironment()
function Hydrate<T>({ Component, props }: { Component: NextPageWithLayout<T>; props: any }) {
const environment = useRelayEnvironment();

const getLayout = Component.getLayout ?? ((page) => page);

const transformedProps = useMemo(() => {
if (props == null) {
return props
return props;
}
const { preloadedQueries, ...otherProps } = props
// eslint-disable-next-line react/prop-types
const { preloadedQueries, ...otherProps } = props;
if (preloadedQueries == null) {
return props
return props;
}

const queryRefs: any = {}
for (const [queryName, { params, variables, response }] of Object.entries(
preloadedQueries,
) as any) {
const queryId = params.id || params.text

environment
.getNetwork()
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - seems to be a private untyped api 🤷‍♂️
.responseCache.set(queryId, variables, response)
const queryRefs: any = {};
for (const [queryName, { params, variables, response }] of Object.entries(preloadedQueries) as any) {
environment.getNetwork().responseCache.set(params.id, variables, response);
// TODO: create using a function exported from react-relay package
queryRefs[queryName] = {
environment,
fetchKey: queryId,
fetchKey: params.id,
fetchPolicy: 'store-or-network',
isDisposed: false,
name: params.name,
kind: 'PreloadedQuery',
variables,
}
};
}

return { ...otherProps, queryRefs }
}, [props])
return { ...otherProps, queryRefs };
}, [props]);

return <Component {...transformedProps} />
}
return <Providers>{getLayout(<Component {...transformedProps} />)}</Providers>;
}
25 changes: 25 additions & 0 deletions apps/web/src/relay/RelayEnvironment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Environment, RecordSource, Store } from 'relay-runtime';

import { createNetwork } from './network';

const IS_SERVER = typeof window === typeof undefined;
const CLIENT_DEBUG = true;
const SERVER_DEBUG = false;

export function createEnvironment() {
const network = createNetwork();
const environment = new Environment({
network,
store: new Store(new RecordSource(), {}),
isServer: IS_SERVER,
log(event) {
if ((IS_SERVER && SERVER_DEBUG) || (!IS_SERVER && CLIENT_DEBUG)) {
console.debug('[relay environment event]', event);
}
},
});

environment.getNetwork().responseCache = network.responseCache;

return environment;
}
56 changes: 34 additions & 22 deletions apps/web/src/relay/environment.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,41 @@
import { Environment, RecordSource, Store } from 'relay-runtime'
import type { FetchFunction } from 'relay-runtime';
import { Environment, Network, RecordSource, Store } from 'relay-runtime';

import { createNetwork } from './network'

const IS_SERVER = typeof window === typeof undefined
const CLIENT_DEBUG = false
const SERVER_DEBUG = false
/**
*
* Define a function that fetches the results of an operation (query/mutation)
* and returns its results as a Promise.
*/

function createEnvironment() {
const network = createNetwork()
const environment = new Environment({
network,
store: new Store(new RecordSource(), {}),
isServer: IS_SERVER,
log(event) {
if ((IS_SERVER && SERVER_DEBUG) || (!IS_SERVER && CLIENT_DEBUG)) {
console.debug('[relay environment event]', event)
}
const GRAPHQL_ENPOINT = process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT as string
const fetchQuery: FetchFunction = async (
operation,
variables,
// cacheConfig,
// uploadables
) => {
const response = await fetch(GRAPHQL_ENPOINT, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
})
body: JSON.stringify({
query: operation.text,
variables,
}),
});

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore Private API Hackery? 🤷‍♂️
environment.getNetwork().responseCache = network.responseCache
return await response.json();
};

return environment
}
/**
* Create a network layer from the fetch function
*/
const network = Network.create(fetchQuery);
const store = new Store(new RecordSource());

export { createEnvironment }
export const environment = new Environment({
network,
store,
});
Loading

1 comment on commit 092940d

@vercel
Copy link

@vercel vercel bot commented on 092940d Sep 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

api-relay – ./apps/server

api-relay-thiago-mota-santos.vercel.app
api-relay-git-main-thiago-mota-santos.vercel.app
api-relay.vercel.app

Please sign in to comment.