Advertisement
Guest User

Untitled

a guest
Mar 19th, 2019
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 23.08 KB | None | 0 0
  1. ## How we assign components to modules
  2.  
  3. This insures each component only can access the state from the module its part of.
  4.  
  5.  
  6. 1) here's the intermediary data structure we generate in a single babel pass:
  7.  
  8. ```js
  9. const fileDeps = {
  10. '/src/components/LayoutModule.js': {
  11. moduleName: 'layout-module', // not every file will have this; only files with `createModule` will have it, but at the end we can assign one to each file
  12. importDependencies: [
  13. '/src/components/FooComponent.js',
  14. '/src/components/AnotherModule.js',
  15. 'etc'
  16. ]
  17. },
  18. '/src/components/AnotherModule.js': {
  19. moduleName: 'another-module',
  20. importDependencies: []
  21. },
  22. '/src/components/FooComponent.js': { // will inherit layout-module
  23. importDependencies: [
  24. '/src/components/BazComponent.js' // also will inherit layout-module
  25. ]
  26. },
  27. }
  28. ```
  29.  
  30.  
  31. 2) in the `completion` hook that babel offers, we convert it to a simple hash in a json file we generate:
  32. ```js
  33. const moduleNamesByFileName = {
  34. '/src/components/LayoutModule.js': 'layout-module',
  35. '/src/components/FooComponent.js': 'layout-module',
  36. '/src/components/BazComponent.js': 'layout-module',
  37. '/src/components/AnotherModule.js': 'another-module',
  38. }
  39. ```
  40.  
  41. 3) the developer has to be sure to import the `moduleNamesByFileName` json file we generate, and supply it to the runtime, eg: `createStore(moduleNamesByFileName, reducers, selectors, initialState, enhancer)`
  42.  
  43.  
  44.  
  45. 4) then pillar #1 of the babel plugin is modified to pass the name of the--and this is the trick--CURRENT FILE when acquiring state, eg: `const state = useRemixxState('/src/components/AnotherModule.js');`
  46.  
  47. ```js
  48. const RemixxComponent = (props) => {
  49. const state = useRemixxState('/src/components/AnotherModule.js') // babel plugin simply injects name of current file!
  50. const dispatch = useRemixxDispatch()
  51. const actions = useRemixxActions()
  52. return MyComponent(props, state, bindActions(dispatch, actions))
  53. }
  54. ```
  55. > Remixx is our fork of Redux
  56.  
  57.  
  58.  
  59. ## Modules Look like this
  60.  
  61. ```js
  62. // main module
  63. export default createModule({
  64. name: '345456645', // importing module specifies namespace
  65. components,
  66. reducers,
  67. routes: {
  68. HOME: {
  69. path: '/home'
  70. },
  71. FEED: {
  72. path: '/feed',
  73. thunk: ({ state, actions }) => {
  74. if (!state.user && state.cookie) return actions.auth.login({ params: { state.cookie } })
  75. if (!state.user) return actions.auth.signup() // havent figured this out yet, but basically, all action creators need to be available ALWAYS like the `routeDepsManifest` below, and they need to be assigned namespaces like `auth`, which overates the numerical guaranteed-to-be-unique generated ID; this is the final piece
  76. }
  77. }
  78. }
  79. })
  80.  
  81. // auth module
  82. export default createModule({
  83. name: '234345345', // namespace is generated at first
  84. components,
  85. reducers,
  86. routes: {
  87. SIGNUP: {
  88. path: '/signup'
  89. },
  90. LOGIN: {
  91. path: '/login/:param',
  92. thunk: ({ api, params }) => api.fetch(params.param)
  93. }
  94. }
  95. })
  96. ```
  97.  
  98.  
  99. ```js
  100. const moduleNamesByFileName = {
  101. '/src/components/AuthModule.js': '234345345', // replaced with "auth" or whatever parent module names it
  102. }
  103. ```
  104.  
  105.  
  106. ## A Manifest of action type to module import is generated and sent to the client
  107.  
  108. ```js
  109. const routeDepsManifest = {
  110. HOME: () => import('modules/main'), // notice these 2
  111. FEED: () => import('modules/main'), // import the same chunk/remixxModule
  112. SIGNUP: () => import('modules/signup'),
  113. LOGIN: ({ param }) => import(`modules/login/${param}`)
  114. }
  115. ```
  116. > Routes can be built while the "airplane is flying" just based on this little amount of info. THEREFORE WE CAN AUTOMATICALLY SPLIT ALL ROUTES!
  117.  
  118. These import() functions are the equivalent of `route.load`. Essentially they exist for every single route now, and are automatically generated. NO need to specify them.
  119.  
  120.  
  121.  
  122. ## Reducers need to receive types injected too:
  123.  
  124. ```js
  125. const myReducer = (state, action, types, actions) => {
  126. if (action.type === types.FOO) ... // where real FOO type might be FOO/234345345
  127. }
  128. ```
  129.  
  130. ## actions.auth.signup() ??
  131.  
  132. SO you might have saw this above. this is key. All actions creators for yet-to-be-loaded routes need to be available in all other routes. OR, we gotta implement a system where parent routes only have the actions to potential routes you can navigate to.
  133.  
  134. That sounds even more complex. So let's just figure out how to get all the action creators we need first, and optimize later.
  135.  
  136. The second bit is this: `actions.auth` is the namespace. So look at this:
  137.  
  138. ```js
  139. // auth module
  140. export default createModule({
  141. name: '234345345', // namespace is generated at first
  142. ```
  143.  
  144. Our babel plugin will assign the name/id `234345345` which is basically just a hash of the file name, which is guaranteed to be unique. All imported components before the next "module boundary" will be assigned this hash id. That insures that when they access state, they access just the same namespace.
  145.  
  146.  
  147. What that means is that the following from above:
  148.  
  149. ```js
  150. const moduleNamesByFileName = {
  151. '/src/components/LayoutModule.js': 'layout-module',
  152. '/src/components/FooComponent.js': 'layout-module',
  153. '/src/components/BazComponent.js': 'layout-module',
  154. '/src/components/AnotherModule.js': 'another-module',
  155. }
  156. ```
  157.  
  158. is actually this:
  159.  
  160.  
  161. ```js
  162. const moduleNamesByFileName = {
  163. '/src/components/LayoutModule.js': '234345345',
  164. '/src/components/FooComponent.js': '234345345',
  165. '/src/components/BazComponent.js': '234345345le',
  166. '/src/components/AnotherModule.js': '12342343434',
  167. }
  168. ```
  169.  
  170. or in this case:
  171. ```js
  172. const moduleNamesByFileName = {
  173. '/src/components/AuthModule.js': '234345345',
  174. }
  175. ```
  176.  
  177.  
  178. Then what I have in mind is this:
  179.  
  180. ```js
  181. loadModule('auth', () => import('modules/authModule'))
  182. ```
  183.  
  184. which changes our hash to:
  185.  
  186. ```js
  187. const moduleNamesByFileName = {
  188. '/src/components/AuthModule.js': 'auth', // now we can refer to this module in parent modules via `auth`
  189. }
  190. ```
  191.  
  192. This enables `actions.auth.login()`, eg:
  193.  
  194. ```js
  195. FEED: {
  196. path: '/feed',
  197. thunk: ({ state, actions }) => {
  198. if (!state.user && state.cookie) return actions.auth.login({ params: { state.cookie } })
  199. if (!state.user) return actions.auth.signup()
  200. ```
  201.  
  202.  
  203. So we've come a long way at this point. But what we are now missing is 2 things:
  204.  
  205. A) How we get all the actions, i.e. as generated via `createScene`. We'll have to add em to another manifest like `routeDepsManifest`. Hopefully we can keep that small
  206.  
  207. B) More importantly, where `loadModule('auth', () => import('modules/authModule'))` is actually called to make name the anonymous numerical namespace `auth`. The problem is that multiple parent modules may import the `auth` namespace. So we have to find the perfect (hopefully singular) place to name a module.
  208.  
  209. Let's forget A for a second. Imagine this: some of th4e above modules created via `createModule` are on NPM and made by 3rd parties, e.g. a Stripe module made for Respond (yay!). So Stripe won't name the module `payment`. Within their code, the babel plugin will have generated this:
  210.  
  211. ```js
  212. export default createModule({
  213. name: '234345345',
  214. components,
  215. reducers,
  216. routes
  217. })
  218. ```
  219.  
  220. However, in our app, when we load this module, we need to assign it to a namspace, which will prefix all actions, and which will namespace the state in the store. So the following has to happen somewhere:
  221.  
  222. ```js
  223. loadModule('payment', () => import('stripe'))
  224. ```
  225.  
  226. Again, the problem is we may use the Stripe module in several of our own modules. And in several of our routes. Let's just say for a moment, different of our modules can refer to it by different namespaces (similar to how ES6 modules can be imported into different files and assigned different aliases). In that case, I imagine a module must simply state its dependencies, eg:
  227.  
  228.  
  229. ```js
  230. export default createModule({
  231. name: 'parent-module',
  232. components,
  233. reducers,
  234. routes,
  235. dependencies: [
  236. payment: () => import('stripe'),
  237. ]
  238. })
  239. ```
  240.  
  241. > now we can use `actions.payment.charge()` or something because the actions of all deps are bundled into the parent!
  242.  
  243.  
  244. Let's look at how the original example looks according to this approach.
  245.  
  246.  
  247. ```js
  248. // main module
  249. export default createModule({
  250. name: '345456645', // generated ID by babel plugin (hashing of current file name)
  251. components,
  252. reducers,
  253. routes: {
  254. HOME: {
  255. path: '/home'
  256. },
  257. FEED: {
  258. path: '/feed',
  259. thunk: ({ state, actions }) => {
  260. if (!state.user && state.cookie) return actions.auth.login({ params: { state.cookie } })
  261. if (!state.user) return actions.auth.signup()
  262. }
  263. }
  264. },
  265. dependencies: [
  266. auth: () => import('modules/authModule'),
  267. ]
  268. })
  269. ```
  270.  
  271. Now, I'm not exactly sure how the above relates to our manifest:
  272.  
  273. ```js
  274. const routeDepsManifest = {
  275. HOME: () => import('modules/main'),
  276. FEED: () => import('modules/main'),
  277. SIGNUP: () => import('modules/auth'),
  278. LOGIN: () => import(`modules/auth`)
  279. }
  280. ```
  281.  
  282. ->
  283.  
  284. ```js
  285. const routeDepsManifest = {
  286. HOME: {
  287. load: () => import('modules/main'),
  288. },
  289. FEED: {
  290. load: () => import('modules/main'),
  291. action: ['customCreator'],
  292. customCreator
  293. }
  294. }
  295. ```
  296. > other info we need to *fully* generate action creators is scene, basename, formatter, subtypes
  297.  
  298.  
  299. I guess the deps is some sort of compiler flag we must parse with babel to insure we get the actions for deps in the parente, g: `actions.auth.signup()`. Maybe there is no `routeDepsManifest`, but just the dependencies, and we use that to insure the parent has the action creators, nothing more.
  300.  
  301. I think that's it. Now I just gotta think through how we will intelligently get the actions out of the child and into the parent. At compile time? Runtime? A little of both? Basically we gotta generate the action creators at compile time, and then embed them in the parent module, so they can be executed at first load of the parent, so that its then on available in the runtime.
  302.  
  303. In Conclusion, almost all of the hard problems we're solving is dependent on compile time code generation. Moreover, what's unique about what we're doing is that we're doing a hybrid approach. It's not so different than all the webpack stuff with manifests. It's more like routing/redux-specific manifests which are also in relation to splitting.
  304.  
  305.  
  306.  
  307. ## NEXT: make parent state available in child module:
  308.  
  309. ```js
  310. export default createModule({
  311. name: '345456645',
  312. components,
  313. reducers,
  314. routes: {
  315. HOME: {
  316. path: '/home'
  317. },
  318. },
  319. dependencies: [
  320. payment: {
  321. load: () => import('modules/stripe'),
  322. stateMappings: {
  323. user: 'session',
  324. pay: 'charge'
  325. }
  326. }
  327. ]
  328. })
  329. ```
  330.  
  331. The idea here is that the stripe module expects to have access to `state.session` and `state.charge`, but in the parent module, they are available as `state.user` and `state.pay`. So we have to provide mappings.
  332.  
  333. That results in the following working correctly, and accessing a single time-travellable store:
  334.  
  335. ```js
  336. const MyRemixxComponent = (props, state, actions) => {
  337. return <div>{state.session}</div>
  338. }
  339. ```
  340.  
  341. So in our DevTools the real value is `state.session`, which is essentially being aliased.
  342.  
  343.  
  344. ## LAST: Action creator generation
  345.  
  346. So basically the plan is to use our `createScene` utilities to generate actions statically at build time! Then we can embed their corresponding code into parent modules, and insure parent modules can dispatch the actions corresponding to all deps.
  347.  
  348. Hopefully that doesn't add too much to the chunk size. A minimal amount of information is needed to generate actions, basically just the routesMap keys. But unfortunately there's a few key/vals on each route which can customize the generation of these actions, so we gotta parse those with the babel plugin and paste them into the action creator manifest, which is ultimately used along with `createScene` to generate the final action creators AT RUNTIME.
  349.  
  350.  
  351.  
  352.  
  353. ## NESTED ROUTES + MODULES!!!
  354.  
  355. ```js
  356. export default createModule({
  357. name: '345456645',
  358. components,
  359. reducers,
  360. routes: {
  361. HOME: {
  362. path: '/home'
  363. },
  364. CHECKOUT: {
  365. path: '/checkout',
  366. routes: {
  367. STEP1: {
  368. path: '/step-1', // final path: /checkout/step-1
  369. },
  370. STEP2: {
  371. path: '/step-2', // final path: /checkout/step-2
  372. },
  373. PAYMENT: {
  374. load: () => import('modules/stripe'),
  375. path: '/payment',
  376. appendChildPaths: false, // children dont use parent's segment, but may use grandparent etc, eg: /checkout/thank-you
  377. stateMappings: {
  378. user: 'session',
  379. pay: 'charge'
  380. }
  381. }
  382. },
  383. },
  384. AUTH: {
  385. load: () => import('modules/auth'), // routes that will map to the top level
  386. stateMappings: {
  387. user: 'user',
  388. }
  389. }
  390. },
  391. })
  392.  
  393. // stripe module
  394. export default createModule({
  395. components,
  396. reducers,
  397. routes: {
  398. CHARGE: {
  399. thunk: ({ stripe, payload }) => stripe.charge(payload)
  400. },
  401. CONFIRMATION: {
  402. path: '/thank-you',
  403. }
  404. }
  405. })
  406.  
  407.  
  408. // auth module
  409. export default createModule({
  410. components,
  411. reducers,
  412. routes: {
  413. SIGNUP: {
  414. path: '/signup',
  415. action: ['customCreator'],
  416. customCreator
  417. },
  418. LOGIN: {
  419. path: '/login/:param',
  420. thunk: ({ api, params }) => api.fetch(params.param)
  421. },
  422. HELP: {
  423. path: '/help',
  424. appendPath: '/forgot-password', // u can speciy an alternative path to append to
  425. load: () => import('modules/forgotPassword'),
  426. }
  427. }
  428. })
  429. ```
  430.  
  431. So the above will appear partially in the manifest like the following (keep in mind there is tons of KBs saved from being sent over the wire because the reducers, components and 90% of each route isn't sent over the wire in the first load):
  432.  
  433.  
  434. ```js
  435. const { store, firstRoute } createApp({
  436. main: {
  437. load: () => import('modules/main'),
  438. routes: {
  439. HOME: {},
  440. CHECKOUT: {
  441. load: () => import('modules/checkout'),
  442. routes: {
  443. STEP1: {},
  444. STEP2: {},
  445. PAYMENT: {
  446. load: () => import('modules/stripe'),
  447. routes: {
  448. CHARGE: {},
  449. CONFIRMATION: {}
  450. }
  451. }
  452. },
  453. }
  454. }
  455. },
  456. auth: {
  457. load: () => import('modules/auth'),
  458. routes: {
  459. LOGIN: {},
  460. SIGNUP: {}
  461. }
  462. }
  463. }, {
  464. initialState, // redux initial state(createApp does createRouter + createStore!!)
  465. enhancer, // redux enhancer!
  466. // ...rest of the rudy options
  467. }, [
  468. codeSplit('load'
  469. call('beforeEnter'),
  470. call('enter')
  471. call('thunk')
  472. ])
  473.  
  474.  
  475. const render = App => {
  476. ReactDOM.hydrate(
  477. <AppContainer>
  478. <Provider store={store}>
  479. <App />
  480. </Provider>
  481. </AppContainer>,
  482. document.getElementById('root')
  483. )
  484. }
  485.  
  486. (async function() {
  487. await store.dispatch(firstRoute())
  488. render(App)
  489. })()
  490. ```
  491.  
  492. TBD:
  493. - scene creation requires knowledge of route.path + route.scene
  494.  
  495.  
  496. ## Misc Problems/Solutions
  497.  
  498. - Actions prevent conflicts by the same strategy as components, where state (and actions) are powered by a proxy, which makes available only the action creators + state slices available to the particular
  499. module.
  500.  
  501. - like `stateMappings` possibly there is also `actionMappings` that use the proxy mechanism to allow
  502. child reducers to listen to parent action types
  503.  
  504. -parent reducers can listen to all child action types, by virtue of the developer choosing to do so throgh
  505. knowing the child namespaces, eg: `checkout.payment.CHARGE` could be listened to in reducers from the top level `CHECKOUT` module, or perhaps even in other parallel modules like `HOME`. But the inverse isn't true without using `actionMappings` (i.e. children have to be explicitly given access, in order to prevent conflicts by child modules that are supposed to be clueless of where they are used). See, that's the difference, parent modules, get to know whats up with the children, but not the other way around (kind of like in real live :)). ...That makes me think about selectors--parent module selectors may also want access to child module selectors. That should be fine, but that's a bridge I'm ok with crossing when we get to it; unlike most this other stuff I intuit it doesn't need to be as aggressively pre-planned.
  506.  
  507. -what about module routes that are used just as namespaces, but without paths, eg:
  508.  
  509. ```js
  510. AUTH: {
  511. load: () => import('modules/auth'), // routes that will map to the top level
  512. }
  513. ```
  514.  
  515. What I came up with is:
  516.  
  517. - A) if the path isn't present, imported routes have their paths treated as `appendPaths: false`, i.e. they are not appended on to anything. Well, that's not true, it's a recursive system, so that means it may append to a parent route path (if there is one). But it won't append to anything at the `AUTH` level.
  518.  
  519. - B) if the path is present, the imported child routes are prepended, eg:
  520.  
  521. ```js
  522. AUTH: {
  523. path: '/auth',
  524. load: () => import('modules/auth'),
  525. }
  526. ```
  527.  
  528. would produce something like: `'/auth/login'`
  529.  
  530.  
  531. - C) there's a third option where the parent has a path but `appendPaths: false`, in which case, it's treated like A), eg:
  532.  
  533. ```js
  534. AUTH: {
  535. path: '/auth',
  536. appendPath: false,
  537. load: () => import('modules/auth'),
  538. }
  539. ```
  540.  
  541. So a child route for LOGIN would exist simply at `'/login'`
  542.  
  543.  
  544. So in short, when the parent module path isn't used, ur basically just using the module to group routes together. So that means that modules aren't necessarily tied to a path. It's up to the user to do determine there needs. Perhaps we have a 4th option:
  545.  
  546. - C) `'something-else/login'`:
  547.  
  548. ```js
  549. AUTH: {
  550. path: '/auth',
  551. appendPath: '/something-else',
  552. load: () => import('modules/auth'),
  553. }
  554. ```
  555.  
  556. That should cover all our bases. Again, the paradigm is like ES6 modules where the responsibility is left with the parent to alias important aspects in order to prevent conflicts. That works very nicely for us.
  557.  
  558.  
  559. The final thing to consider with A) above is basically we will do things like `dispatch(actions.auth.login())`
  560.  
  561. So again, we're gaining the benefits of the `auth` namespace without using it as a true parent route. ..The next thing to consider along those lines is thunks and callbacks--I guess it does still make sense to be able to assign shared callbacks even tho it doesnt have a path of its own. I.e. all children will share the same callbacks here:
  562.  
  563. ```js
  564. AUTH: {
  565. load: () => import('modules/auth'),
  566. thunk: () => ...,
  567. }
  568. ```
  569.  
  570.  
  571. The remaining question is can we do this:
  572.  
  573. `dispatch(actions.auth())`
  574.  
  575. And what happens exactly without a path? I mean the above one with a thunk would pass as a pathless route. So that means its thunk would be called, even tho the thunk is only intended for child routes. We could detect that this isn't a regular route, it's not a pathless route, it's simply a namespace *handle* by firtue of the fact that it has a `load` key but not a `path` key. Or similarly a `routes` key, but not a `path` key (in the case where code splitting isnt being used).
  576.  
  577.  
  578.  
  579. ## Let's take a final look at how our routes map becomes our manifest:
  580.  
  581.  
  582.  
  583. ```js
  584. export default createModule({
  585. name: '345456645',
  586. components,
  587. reducers,
  588. routes: {
  589. HOME: {
  590. path: '/home'
  591. },
  592. CHECKOUT: {
  593. path: '/checkout',
  594. routes: {
  595. STEP1: {
  596. path: '/step-1', // final path: /checkout/step-1
  597. },
  598. STEP2: {
  599. path: '/step-2', // final path: /checkout/step-2
  600. },
  601. PAYMENT: {
  602. load: () => import('modules/stripe'),
  603. path: '/payment',
  604. appendChildPaths: false, // children dont use parent's segment, but may use grandparent etc, eg: /checkout/thank-you
  605. stateMappings: {
  606. user: 'session',
  607. pay: 'charge'
  608. }
  609. }
  610. },
  611. },
  612. AUTH: {
  613. load: () => import('modules/auth'), // routes that will map to the top level
  614. stateMappings: {
  615. user: 'user',
  616. }
  617. }
  618. },
  619. })
  620.  
  621. // stripe module
  622. export default createModule({
  623. components,
  624. reducers,
  625. routes: {
  626. CHARGE: {
  627. thunk: ({ stripe, payload }) => stripe.charge(payload)
  628. },
  629. CONFIRMATION: {
  630. path: '/thank-you',
  631. }
  632. }
  633. })
  634.  
  635.  
  636. // auth module
  637. export default createModule({
  638. components,
  639. reducers,
  640. routes: {
  641. SIGNUP: {
  642. path: '/signup',
  643. action: ['customCreator'],
  644. customCreator
  645. },
  646. LOGIN: {
  647. path: '/login/:param',
  648. thunk: ({ api, params }) => api.fetch(params.param)
  649. },
  650. HELP: {
  651. path: '/help',
  652. appendPath: '/forgot-password', // u can speciy an alternative path to append to
  653. load: () => import('modules/forgotPassword'),
  654. }
  655. }
  656. })
  657. ```
  658.  
  659.  
  660. -->
  661.  
  662. ```js
  663. window.ROUTES_MANIFEST = {
  664. main: {
  665. load: () => import('modules/main'),
  666. routes: {
  667. HOME: {},
  668. CHECKOUT: {
  669. load: () => import('modules/checkout'),
  670. routes: {
  671. STEP1: {},
  672. STEP2: {},
  673. PAYMENT: {
  674. load: () => import('modules/stripe'),
  675. routes: {
  676. CHARGE: {},
  677. CONFIRMATION: {}
  678. }
  679. }
  680. },
  681. }
  682. }
  683. },
  684. auth: {
  685. load: () => import('modules/auth'),
  686. routes: {
  687. LOGIN: {},
  688. SIGNUP: {},
  689. HELP: {
  690. load: () => import('modules/forgotPassword')
  691. }
  692. }
  693. }
  694. }
  695. ```
  696.  
  697.  
  698. So the idea is that all the missing information in the manifest gets attached when the `load` imports happen. However, until then we have all the information we need to call any action in order to trigger the yet-to-be-loaded routes in the first place!
  699.  
  700.  
  701.  
  702. ## CODE SPLITTING MIDDLEWARE (NOTHING NEW HERE YET)
  703.  
  704.  
  705. This is the old code splitting middleware. 2 similar drafts.
  706.  
  707. Now, we need a new one to consider modules + nesting above. Basically this middleware along with modications to the `call` middleware + a special usage of `createScene` + of course all the babel compilation stage manifest generation will be the core of this task. We should be able to imagine the `call` + `codeSplit` middleware indepently though and get to work. coming soon...
  708.  
  709.  
  710. ```js
  711. export default (name = 'load') => (api) => async (req, next) => {
  712. const load = req.route && req.route[name]
  713.  
  714. if (load) { // if `route.load` does not exist short-circuit
  715. const parts = await load(req)
  716. addPartsToRuntime(req, parts)
  717. }
  718.  
  719. return next()
  720. }
  721.  
  722.  
  723. const addPartsToRuntime = (req, parts) => {
  724. const { route, action, options, tmp, ctx, commitDispatch } = req
  725. const { components, reducers, chunk, ...rest } = parts
  726.  
  727. if (ctx.chunks.includes(chunk)) return // chunk was already added to runtime, so short-circuit
  728.  
  729. if (reducers) {
  730. // options.replaceReducer(reducers)
  731. }
  732.  
  733. if (components) {
  734. req.location.components = components
  735. action.components = components // we need to modify `createReducer` to store `state.location.components` so after load they can be dynamically rendered within existing components!
  736. }
  737.  
  738. if (tmp.committed && (components || reducers)) { // if the route change action has already been dispatched, we need to re-dispatch it again, so the new goodies are received
  739. action.force = true // we need a flag to force this action through, so component are added to state or new reducers receive action -- the `force` flag doesn't already exist, it's a placeholder for something we can already use to force the action passed the `isDoubleDispatch` check; we may have some other piece of infrastructure that precludes needing to create a new custom flag
  740. commitDispatch(action)
  741. }
  742.  
  743. Object.assign(route, rest) // rest allows you to tack on additional thunks, sagas, etc, to your route object (optionally) -- i.e. you can build the "plane" (aka route) while flying
  744. ctx.chunks.push(chunk)
  745. }
  746. ```
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement