Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Update routesMap dynamically #62

Closed
qw-in opened this issue Aug 3, 2017 · 10 comments
Closed

Update routesMap dynamically #62

qw-in opened this issue Aug 3, 2017 · 10 comments

Comments

@qw-in
Copy link

qw-in commented Aug 3, 2017

I was recently working on a webapp for work and started messing around with per-client routing (it's a private webapp, SEO is not of concern). I currently have it implemented with slugs /:client/:thing but that was only after I tried wrapping the redux-first-router reducer to allow injecting new routes into the routesMap on the fly.

I was a bit disappointed to find that when I changed the routesMap in the redux store that change was not reflected by the middleware / enhancer. I had a brief poke through the code and it seems that this is due to the routesmap being passed into the enhancer when connectRoutes is called. I would assume it is possible for the enhancer to read the routesMap from the store instead.

So I suppose I am wondering if there would be any large issues if this was implemented. To my understanding it would change nothing in terms of the API (unless it had to be specifically enabled in the connectRoutes options). Certainly not a high priority feature but it would be nice to be able to define more solid routes for each client.

@faceyspacey
Copy link
Owner

Comin in a week or so. U gotta make the middleware respond to an action containing the new routes. As well as the reducer which also maintains a reference to it (but that is more standard).

It also may be possible to just update the reducer state and always read from state rather than closure. In a few places that might not be possible, but hopefully that's not the case.

@GuillaumeCisco
Copy link
Contributor

Dynamically injecting routes would be great!
I was looking for this feature too but did not find it.

Would be great being able to inject new routes when we async load a component we want to display.
It's really a pain to have to declare all the possible routes at first.

Thanks for explanation btw

@qw-in
Copy link
Author

qw-in commented Aug 9, 2017

Assuming routesMap can always be read from state, is there an argument to be made for not adding a specific action (like NOT_FOUND) for modifying it?

// ...
const { reducer } = connectRoutes(history, routesMap);

// Note the lack of default value
const myReducer = (lastState, action) => {
  const state = reducer(lastState, action);

  switch (action.type) {
    case 'MY_ACTION': {
      return {
        ...state,
       routesMap: {
          ...state.routesMap,
         SOME_NEW_PATH: '/some/new/:path',
       },
      };
    }
    default:
      return state;
  }
};

const rootReducer = combineReducers({ location: myReducer });
// ...

Would be interested in your thoughts or any issues with this approach

@faceyspacey
Copy link
Owner

a specific action is exactly what it will need no matter what, but i still need to look into the middleware and see if it can read from state.

@faceyspacey
Copy link
Owner

faceyspacey commented Aug 15, 2017

ok, this is done. it's in the @next dist-tag on NPM. get it like this:

yarn upgrade redux-first-router@next

here's how you use it:

import { addRoutes } from 'redux-first-router'
import universal from 'react-universal-component'

const UniversalComponent = universal(props => import(`./${props.page}`), {
   onLoad: module => {
       const aThunk = addRoutes(module.newRoutes)
       store.dispatch(aThunk)
   } 
})

this is just an example using Universal. you can use any code splitting component/tools you want.

So essentially you have an action creator now to dispatch new routes received via code-splitting.

There's one thing I'm noticing now that I'm using it with React Universal Component--onLoad doesn't receive props, so a global store variable had to be imported and used, which is no good. I'll have to add a 2nd props arg there so your redux connected component can pass dispatch. It should be:

onLoad: => (module, props) => props.dispatch(addRoutes(module.newRoutes))

I'll make that tweak in RUC.

For now if you're looking at this and interested, find a way to access store directly or if you're using other code splitting tools, you know what to do.

Otherwise, i'm closing this issue. feel free to open any specific new Issues. Feel free to add some docs about this. 🚀

@qw-in
Copy link
Author

qw-in commented Aug 17, 2017

Thanks! Looks awesome!

Unfortunately don't have time to mess with it at the moment but looking forward to it. If there is still a need for docs when I get time to check it out I'll put something together. Maybe this weekend

@faceyspacey
Copy link
Owner

faceyspacey commented Aug 17, 2017

Ok, React Universal Component has been updated to better support the various things you might wanna do when code-splitting. The onLoad option now receives both props and context. Context is passed so you don't need to import your store, which is problematic when doing SSR where you must have the store corresponding to the specific express request. Here's what this looks like:

import { addRoutes } from 'redux-first-router'
import universal from 'react-universal-component'

const UniversalComponent = universal(props => import(`./${props.page}`), {
   onLoad: (module, info, props, context) => {
     const aThunk = addRoutes(module.newRoutes) // export new routes from component file
     context.store.dispatch(aThunk)

     context.store.replaceReducer({ ...otherReducers, foo: module.fooReducer })

     // if a route triggered component change, new reducers needs to reflect it
     context.store.dispatch({ type: 'INIT_ACTION_FOR_ROUTE', payload: { param: props.param } })
  }
})

do yarn upgrade react-universal-component if that's what you're using for code splitting.

@brianephraim
Copy link

addRoutes is missing from npm versions later than 0.0.9-next

@faceyspacey
Copy link
Owner

@defualt sorry about that. it's back in.

+ redux-first-router@0.0.20-next

@rcalabro
Copy link

@faceyspacey can you share your configureStore from this last example?
`import { addRoutes } from 'redux-first-router'
import universal from 'react-universal-component'

const UniversalComponent = universal(props => import(./${props.page}), {
onLoad: (module, info, props, context) => {
const aThunk = addRoutes(module.newRoutes) // export new routes from component file
context.store.dispatch(aThunk)

 context.store.replaceReducer({ ...otherReducers, foo: module.fooReducer })

 // if a route triggered component change, new reducers needs to reflect it
 context.store.dispatch({ type: 'INIT_ACTION_FOR_ROUTE', payload: { param: props.param } })

}
})`

How are you combining the new reducer with the already loaded ones and keeping the state?

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants