Gatsby server-side rendering of page with client-only routes, based on window location
Asked Answered
S

1

0

I am working on a Gatsby project (a language learning blog) and am having trouble with an issue that only happens in production builds due to server-side rendering.

I programmatically generate a page for each blog post with the following scheme: /posts/{post-name}. Each post can also have a number of subpaths that map to open tabs in the UI. For example, /posts/important-spanish-verbs/lesson would open the lesson tab. Each page has a matchPath property of /posts/{post-name}/*, and these are handled as client-only routes, implemented with reach-router.

With gatsby develop, the app works exactly as intended. When I visit /posts/some-post/podcast, it successfully opens to the podcast tab and I can see the active tab being set correctly.

The problem is that with gatsby build && gatsby serve, when I fresh-load a URL that directly navigates to a specific tab inside of the post (either manually visiting the whole URL or refreshing), the active tab is not set correctly in the UI.

The following snippet (highly simplified and condensed to demonstrate the issue) roughly outlines the problem:

function TabbedLayout({ children, basePath }) {
  return (
    <LocationProvider>
      {({ location }) => {
        return (
          <Fragment>
            <ul>
              React.Children.map(children, (child, index) => {
                const isActive = child.props.path === location.pathname
                console.log(`tab ${child.props.path} active? ${isActive}`)
                return <li className={isActive ? 'active' : 'inactive'}>{/* construct a link based on child props */}</li>
              })
            </ul>
            <Router basepath={basePath}>
              {children}
            </Router>
          </Fragment>
        )
      }}
    </LocationProvider>
}

What happens when I run my code which uses the method demonstrated above in a production build (with SSR) is that when I visit for example posts/example-post/lesson:

  • The content is rendered correctly via Reach router (i.e. the content of the lesson tab is open)
  • The lesson tab has class inactive (an error)
  • In the console, I see tab /posts/example-post/lesson active? true printed, even though the CSS class didn't get updated!

I've tried everything I can think of to try to update the UI. As soon as I click a different tab and the window location changes without a refresh, everything updates fine and the active tab UI works as expected, but I just cannot seem to get it to re-render based on the isActive variable.

Does anyone know a way to fix or work around this issue?

Thanks!

Sprag answered 18/1, 2020 at 22:53 Comment(0)
S
4

I have managed to find a workaround for the issue, although it's a bit frustrating to not understand in detail why the problem is actually happening.

If I wrap the tab <li>s (which don't correctly show as active/inactive on first load with SSR) in a condition to check that we're in the browser, it works:

return typeof window !== 'undefined' && (
  <li className={isActive ? 'active' : 'inactive'}>
    {/* construct a link based on child props */}
  </li>
)
Sprag answered 20/1, 2020 at 19:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.