Skip to content

Commit

Permalink
Merge branch 'main' of github.com:redwoodjs/redwood into feat/dc-rc-o…
Browse files Browse the repository at this point in the history
…g-gen-mw-p2

* 'main' of github.com:redwoodjs/redwood:
  feat(og-gen): Update implementation of useLocation | Update App template (#10441)
  • Loading branch information
dac09 committed Apr 12, 2024
2 parents 6e297e9 + d2ae37a commit bf03f82
Show file tree
Hide file tree
Showing 13 changed files with 58 additions and 47 deletions.
10 changes: 10 additions & 0 deletions .changesets/10441.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- feat(og-gen): Update implementation of useLocation | Update App template (#10441) by @dac09
**Updated App.tsx template**
We modified the `App.tsx` template to accept possible children, and render them if present. This lets the og:image handler inject your component into the Document tree, without including the entire Router, but still style your og:image component using whatever you used to style the rest of your app (Tailwind, perhaps?)

**Updated useLocation implementation**
We also modified the `useLocation()` hook to now return everything that the [URL API](https://developer.mozilla.org/en-US/docs/Web/API/URL) returns. Previously it only returned three attributes of the url (pathname, search, hash), now it returns everything available to a call to `new URL()` (origin, href, searchParams, etc.).

The reason for this is now that we have SSR, we can get access to more details in the hook - in this case we needed origin

Both changes should be non-breaking!
4 changes: 2 additions & 2 deletions __fixtures__/empty-project/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import Routes from 'src/Routes'

import './index.css'

const App = () => (
const App = ({ children }) => (
<FatalErrorBoundary page={FatalErrorPage}>
<RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
<RedwoodApolloProvider>
<Routes />
{children ? children : <Routes />}
</RedwoodApolloProvider>
</RedwoodProvider>
</FatalErrorBoundary>
Expand Down
9 changes: 7 additions & 2 deletions __fixtures__/test-project/web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { ReactNode } from 'react'

import { FatalErrorBoundary, RedwoodProvider } from '@redwoodjs/web'
import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'

Expand All @@ -8,13 +10,16 @@ import { AuthProvider, useAuth } from './auth'

import './scaffold.css'
import './index.css'
interface AppProps {
children?: ReactNode
}

const App = () => (
const App = ({ children }: AppProps) => (
<FatalErrorBoundary page={FatalErrorPage}>
<RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
<AuthProvider>
<RedwoodApolloProvider useAuth={useAuth}>
<Routes />
{children ? children : <Routes />}
</RedwoodApolloProvider>
</AuthProvider>
</RedwoodProvider>
Expand Down
4 changes: 2 additions & 2 deletions packages/create-redwood-app/templates/js/web/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import Routes from 'src/Routes'

import './index.css'

const App = () => (
const App = ({ children }) => (
<FatalErrorBoundary page={FatalErrorPage}>
<RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
<RedwoodApolloProvider>
<Routes />
{children ? children : <Routes />}
</RedwoodApolloProvider>
</RedwoodProvider>
</FatalErrorBoundary>
Expand Down
9 changes: 7 additions & 2 deletions packages/create-redwood-app/templates/ts/web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import type { ReactNode } from 'react'

import { FatalErrorBoundary, RedwoodProvider } from '@redwoodjs/web'
import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'

import FatalErrorPage from 'src/pages/FatalErrorPage'
import Routes from 'src/Routes'

import './index.css'
interface AppProps {
children?: ReactNode
}

const App = () => (
const App = ({ children }: AppProps) => (
<FatalErrorBoundary page={FatalErrorPage}>
<RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
<RedwoodApolloProvider>
<Routes />
{children ? children : <Routes />}
</RedwoodApolloProvider>
</RedwoodProvider>
</FatalErrorBoundary>
Expand Down
10 changes: 9 additions & 1 deletion packages/prerender/src/runPrerender.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,16 @@ async function recursivelyRender(
}),
)

// `renderPath` is *just* a path, but the LocationProvider needs a full URL
// object so if you need the domain to be something specific when
// pre-rendering (because you're showing it in HTML output or the og:image
// uses useLocation().host) you can set the RWJS_PRERENDER_ORIGIN env variable
// so that it doesn't just default to localhost
const prerenderUrl =
process.env.RWJS_PRERENDER_ORIGIN || 'http://localhost' + renderPath

const componentAsHtml = ReactDOMServer.renderToString(
<LocationProvider location={{ pathname: renderPath }}>
<LocationProvider location={new URL(prerenderUrl)}>
<CellCacheContextProvider queryCache={queryCache}>
<App />
</CellCacheContextProvider>
Expand Down
31 changes: 8 additions & 23 deletions packages/router/src/location.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,20 @@ import { createNamedContext } from './createNamedContext'
import { gHistory } from './history'
import type { TrailingSlashesTypes } from './util'

export interface LocationContextType {
pathname: string
search?: string
hash?: string
}
export interface LocationContextType extends URL {}

const LocationContext = createNamedContext<LocationContextType>('Location')

interface Location {
pathname: string
search?: string
hash?: string
}
interface Location extends URL {}

interface LocationProviderProps {
location?: Location
trailingSlashes?: TrailingSlashesTypes
children?: React.ReactNode
}

interface LocationProviderState {
context: Location
context: Location | undefined
}

class LocationProvider extends React.Component<
Expand Down Expand Up @@ -75,27 +68,19 @@ class LocationProvider extends React.Component<
break
}

windowLocation = window.location
} else {
windowLocation = {
pathname: this.context?.pathname || '',
search: this.context?.search || '',
hash: this.context?.hash || '',
}
windowLocation = new URL(window.location.href)
}

const { pathname, search, hash } = this.props.location || windowLocation

return { pathname, search, hash }
return this.props.location || this.context || windowLocation
}

componentDidMount() {
this.HISTORY_LISTENER_ID = gHistory.listen(() => {
const context = this.getContext()
this.setState((lastState) => {
if (
context.pathname !== lastState.context.pathname ||
context.search !== lastState.context.search
context?.pathname !== lastState?.context?.pathname ||
context?.search !== lastState?.context?.search
) {
globalThis?.scrollTo(0, 0)
}
Expand Down
6 changes: 3 additions & 3 deletions packages/vite/src/streaming/createReactStreamingHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ export const createReactStreamingHandler = async (
let currentRoute: RWRouteManifestItem | undefined
let parsedParams: any = {}

const { pathname: currentPathName } = new URL(req.url)
const currentUrl = new URL(req.url)

// @TODO validate this is correct
for (const route of routes) {
const { match, ...rest } = matchPath(
route.pathDefinition,
currentPathName,
currentUrl.pathname,
)
if (match) {
currentRoute = route
Expand Down Expand Up @@ -174,7 +174,7 @@ export const createReactStreamingHandler = async (
{
ServerEntry,
FallbackDocument,
currentPathName,
currentUrl,
metaTags,
cssLinks,
isProd,
Expand Down
12 changes: 5 additions & 7 deletions packages/vite/src/streaming/streamHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { createServerInjectionTransform } from './transforms/serverInjectionTran
interface RenderToStreamArgs {
ServerEntry: ServerEntryType
FallbackDocument: React.FunctionComponent
currentPathName: string
currentUrl: URL
metaTags: TagDescriptor[]
cssLinks: string[]
isProd: boolean
Expand Down Expand Up @@ -64,7 +64,7 @@ export async function reactRenderToStreamResponse(
const {
ServerEntry,
FallbackDocument,
currentPathName,
currentUrl,
metaTags,
cssLinks,
isProd,
Expand Down Expand Up @@ -103,7 +103,7 @@ export async function reactRenderToStreamResponse(

const timeoutTransform = createTimeoutTransform(timeoutHandle)

const renderRoot = (path: string) => {
const renderRoot = (url: URL) => {
return React.createElement(
ServerAuthProvider,
{
Expand All @@ -112,9 +112,7 @@ export async function reactRenderToStreamResponse(
React.createElement(
LocationProvider,
{
location: {
pathname: path,
},
location: url,
},
React.createElement(
ServerHtmlProvider,
Expand Down Expand Up @@ -157,7 +155,7 @@ export async function reactRenderToStreamResponse(
},
}

const root = renderRoot(currentPathName)
const root = renderRoot(currentUrl)

const reactStream: ReactDOMServerReadableStream =
await renderToReadableStream(root, renderToStreamOptions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { AuthContextInterface } from '@redwoodjs/auth'

globalThis.RWJS_API_GRAPHQL_URL = 'https://api.example.com/graphql'

import { FetchConfigProvider, useFetchConfig } from './FetchConfigProvider.js'
import { FetchConfigProvider, useFetchConfig } from '../FetchConfigProvider.js'

const FetchConfigToString: React.FunctionComponent = () => {
const c = useFetchConfig()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
useQuery,
useMutation,
useSubscription,
} from './GraphQLHooksProvider'
} from '../GraphQLHooksProvider'

const TestUseQueryHook: React.FunctionComponent = () => {
// @ts-expect-error - Purposefully not passing in a DocumentNode type here.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react'
import { render } from '@testing-library/react'
import { describe, beforeAll, it, expect } from 'vitest'

import { Metadata } from './Metadata.js'
import { Metadata } from '../Metadata.js'

// DOCS: can return a structured object from the database and just give it to `og` and it works

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import React from 'react'
import { render } from '@testing-library/react'
import { vi, describe, it, expect } from 'vitest'

import PortalHead from './PortalHead.js'
import * as ServerInject from './ServerInject.js'
import PortalHead from '../PortalHead.js'
import * as ServerInject from '../ServerInject.js'

const serverInsertionHookSpy = vi
.spyOn(ServerInject, 'useServerInsertedHTML')
Expand Down

0 comments on commit bf03f82

Please sign in to comment.