question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Discussion] Add a 'page' attribute to the RouteObject

See original GitHub issue

It’s fairly common that an app will want to load a particular “page” or scene for a given location type.

This is correctly pointed out in the Medium article where the following example is provided:

Here’s another kind of reducer you’d likely have:

const page = (state, action) => {
  switch(action.type) {
    case 'HOME':
       return 'homeScene'
    case 'POST':
       return 'postScene'
    case 'REPOS':
      return 'reposScene'
    case 'REPO':
      return 'repoScene'
    case 'USER':
      return 'userScene'
  }
  
  return state
}

And its corresponding component:

const PageComponent = ({ page }) => {
  const Page = pages[page]
  return Page ? <Page /> : null
}
const mapState = ({ page }) => ({ page })
export default connect(mapState)(UserComponent)
// could be a switch (but this is cached):
const pages = {
  homeScene: HomeComponent, 
  postScene: PostComponent,
  reposScene: ReposComponent,
  repoScene: RepoComponent,
  userScene: UserComponent
}

What if we just added a page attribute to the RouteObject? Then we could have:

// routes.js

import HomePage from './HomePage.js'
import ContactPage from './ContactPage.js'

const routeMap = {
  HOME: { path: '/', page: HomePage },
  CONTACT: { path: '/', page: ContactPage }
}

and:

// PageComponent.js

import React from 'react'
import { connect } from 'react-redux'
import routes from './routes'

function mapStateToProps (state) {
  return {
    pathKey: state.location.type
  }
}

const PageComponent = ({ pathKey }) => {
  const Page = routes[pathKey].page
  return <Page />
}

export default connect(mapStateToProps)(PageComponent)

Thoughts?

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:16 (8 by maintainers)

github_iconTop GitHub Comments

1reaction
corydeppencommented, Sep 22, 2017

…as for the idea of adding arbitrary keys on route objects, in the full-featured demo now has a auth filtering based on this exact technique. There’s a role key added to the ADMIN route

I believe this would suggest the Flow and TypeScript definitions for RouteObject need to be tweaked to include an indexer/ index signature property to allow for properties like role.

As an aside, is it worth revisiting the idea of maintaining TS definitions in this repo instead of in the DefinitelyTyped repo? DT just seems to add unnecessary friction from both publisher and consumer perspectives. I’ve been noticing a few inconsistencies in the TS definitions while working with the demo app and I would think posting issues in this repo would give the library author and others an opportunity to read and comment on them and might be easier than remembering to tag specific folks in issues submitted in the DT repo.

/cc @valbrand @viggyfresh

1reaction
faceyspaceycommented, Jun 20, 2017

Whatup fellas. Nice thread.

I love how you noticed how you can add arbitrary key/vals to your route objects. No changes from the redux-first router are needed to do what you want.

You also could just use the type by the way, and maintain a map of types to their component. Or in the code-splitting example’s case, a map from types to scenes. That would keep your routesMap cleaner and representative of actual built-in capabilities. My examples where i switched on a scene or had a map of them was just to make it obvious by using a known pattern. I wouldn’t even build a reducer for this since it’s not needed 😃

As for the code-splitting thing, just FYI using import() without require.resolveWeakwon’t work with SSR. It only works for SPAs. But there’s nothing stopping you from using React Loadable or my React Universal Component package. In fact, there is gonna be direct support for prefetching if ur using that package.

Also, any day now I’m releasing the async-reactor interface: https://github.com/xtuc/async-reactor

where you can both perform dynamic calls to import() and data fetches in the same promise function. You can even call import() multiple types. You can also request different things based on props. And that brings us to my main point:

you will be able to do this:

./components/MyUniversalComponent.js:

import universal from 'react-universal-component'

const asyncWork = async (props) => {
   const { page, slug } = props
   const data = await Promise.all([import(`./pages/${page}`), fetch(`/post/${slug}`)])

   const Component = data[0].default
   const post = await data[1].json()

   return <Component post={post} />
}

const MyUniversalComponent = universal(asyncWork)
export default MyUniversalComponent

./components/App.js:

export default () =>
  <div>
    <MyUniversalComponent 
      page='Post'  
      slug='ssr-splitting-fetching-redux-routing' 
    />

    <div>
        <Link prefetch href='ssr-splitting-fetching-redux-routing' />
        <Link prefetch href='magic-comments' prefetchProps={{ slug, page }} />
        <Link prefetch href='redux-first-router-renamed-rudy' />
    </div>
  </div>

…I’m complicating things here since I’m combining 2 concepts: the upcoming react-universal-render package (which helps u resolve combination import()+fetch stateless async components) and redux-first-routing. The former is for a world without redux ultimately.

When using Redux, the thunk route option got you covered. I just wanted to show off what was to come 😉

But that said, you can still use React Universal Component the old way–i.e. just for chunk imports. And <Link prefetch href='rudy' /> will be able to prefetch it just like your thunks or chunks you specify in your route object. There’s a few things I’m trying to work out with it though–namely that if you dynamically import based on a prop. Prefetching must get the fetchProps to your async promise function.

Anyway, I know these thoughts aren’t final or fully clear. I just wanted to let you know that there’s gonna be first-class support for every angle of code-splitting, which means prefetching is a top priority. Also note: ur thunks will be prefetched as part of prefetching ur chunks. This is about to be deployed (hopefully tonight). So basically u can do all the stuff u can do with Next.js minus the framework. That’s one of the primary goals here. And ultimately to do it better with more flexibility, caching, better performance, etc. Ur route objects will get a new cache option you can use to provide a function that receives the same state as your thunk, so you can produce a cache key from it, and if it’s the same, ur thunk won’t be executed. It will be assumed u cached the result in redux. U can provide cache: false to turn this capability off and manually handle it. And you can not set it, and by default it will hash on the corresponding URL. So if u visit the URL again, the thunk won’t be requested, saving u cycles on ur server and tidbits of perf on clients (no need to have ur app doing stuff it already did).

The challenge is tying this into React Universal Component and React Universal Render before I release it. React Universal Component also has a caching feature based on user-provided cache keys. So I’m playing with creating a global cache (window.ASYNC_CACHE) that they both can store hashes in (e.g: { ‘some-url’: true, ‘customKey’: true}), and determine if data + imports need to be fetched. Imports will of course only be requested once, since they aren’t dynamic like paths with ids and slugs.

Lastly, this may be of no relevance to you–because as i mentioned Universal Render shines in a world without redux state where developers are requesting data ad-hoc in their components. See, this solution requires a virtual-dom-only “pre-render” to resolve all promises recursively (Apollo does this). After which it renders your app again–this time synchronously and quickly getting ur data from a hash map where it’s stored. It’s a minor perf thing, but if u can avoid it because ur Redux-First Router’s thunk capability as shown in the SSR docs, u should. That said, if ur using Apollo, Universal.render(<App />) will also work with Apollo. So all bases will be covered.

ps. you call Universal.render(<App />) just like that on the client and server. Whatchu think? Slick huh? There are other args and options of course, but with sensible defaults. For example, just like that, on the client, it will attach ur app to <div id="root" />. On the server, it automatically uses Webpack Flush Chunks to handle serving your dynamic chunks, using its defaults for chunks named vendor, main and bootstrap. And during development it will create a good-enough string to serve. But of course u can do this:

Universal.render(<App />, ({ app, js, styles, asyncStateJson }) => {
   res.send(
      `<!doctype html>
        <html>
          <head>
            ${styles}
          </head>
          <body>
            <div id="root">${app}</div>
            <script>window.ASYNC_STATE = ${asyncStateJson}</script>
            ${js}
          </body>
        </html>`
    )
  })
}

It will have both a high level api + low level api + mid level usage all covered. But the marketing concept is just Universal.render() and not having to deal with all this shit unless u choose to. To me, it has a ring to it.

Thoughts are much appreciated. Im on the verge of getting this stuff out and wanna get it right.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Can you add attributes to the $route to identify which ... - GitHub
The Route object adds a "method" attribute with values: link, replace, push, go. When the page is not using the vue-router api, ...
Read more >
Routing to controller actions in ASP.NET Core - Microsoft Learn
Using page as a route parameter with attribute routing is a common error. Doing that results in inconsistent and confusing behavior with URL ......
Read more >
Page Attributes – WordPress.com Support
Go to My Site(s) → Pages in your dashboard; Find the page About and click the title. Locate the Page Attributes module to...
Read more >
Managing Route Objects in the IRR - RIPE
Creating route objects for out-of-region (non-RIPE) resources ... mnt-by: attribute of the route object; the mnt-routes: attribute of the ...
Read more >
Custom page attribute with Gutenberg | WordPress.org
Hi,. I created a theme few years ago, a one of the features was to display a use custom background color on pages....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found