Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

registerModule would be better to called in beforeCreate instead of asyncData #166

Open
JounQin opened this issue Dec 13, 2017 · 13 comments

Comments

@JounQin
Copy link

JounQin commented Dec 13, 2017

@yyx990803 When we are using SSR, client will not call asyncData at first render, then we will not be able to use registered store module because it has not been registered on client.

So, if it is as expected, registerModule would be better to called in beforeCreate instead of asyncData, so that it will be registered on client at first render, of course preserveState: process.env.VUE_ENV !== 'server' should also be used.

@yyx990803
Copy link
Member

asyncData should be called for all activated route components in router.beforeResolve.

Also asyncData is called before beforeCreate, so if module is registered in the latter, asyncData won't be able to dispatch actions.

@JounQin
Copy link
Author

JounQin commented Dec 13, 2017

So how to preserve store module be registered on client?
For first render, asyncData should not be called on client, right?

I can only register the module again before dispatching an action on client now, is this as expected?

@yyx990803
Copy link
Member

yyx990803 commented Dec 13, 2017

Ah, that's right. So for now calling registerModule again for the client with preserveState: true seems necessary.

@yyx990803
Copy link
Member

I probably need a bit more time to properly rethink this, but if you have a reasonable workaround feel free to submit a PR for the moment.

@JounQin
Copy link
Author

JounQin commented Dec 14, 2017

@yyx990803 I found more problems here.

My project store structure is not much like recommended way as document. Store will be created for every user request with a single http request (axios exactly) with user headers for authorization, so store actions should request with the singleton axios, like createApp.

Example:
https://github.com/JounQin/Rubick/blob/master/src/store/chunks/application.ts

Pass axios into every action as part of payload is really boring, and it will be great if we can add some custom global property on store instance and included in ActionContext, so that we do not need a factory any more.

It works very well before I starting using registerModule.

When I try to registerModule with preserveState: true or not, registered action is called twice, three times and more and more times. So actions is not merged by action name but function value?

And also, unregisterModule seems not a good idea on destroyed.

After the first render on client, if we leave this route then unregisterModule will be called, it will throw an error because this module has not been registered on client. And if there is a computed value via mapState/@State from this module, it will throw an error because the module is unregistered then state.moduleName is undefined now.

My workaround for now is checking whether the module has been registered on client, example: https://github.com/JounQin/Rubick/blob/master/src/views/container/application/Application.vue#L38.

hasModule may help with this or a new option for registerModule to do nothing if module has been registered would be helpful.

@arpowers
Copy link

just ran up against this and spent a couple hours trying to figure out why the docs were saying what they were saying..

Anyway an idea:

  • add a utility to the Vue prototype to allow adding store modules the same way as vue components. This would save a lot of manual work and handle the lifecycle automagically in different applications.

stores: { storeModule }

@devCrossNet
Copy link

I just ran into the same problem, any news on this? I think I almost had a solution with beforeResolve for the server side and created for the client side. But it felt really hacky and it was not good to test.

@arpowers
Copy link

I ended up writing my own handler for store modules like the approach I suggested

@jumika
Copy link

jumika commented Jul 2, 2018

@arpowers

Could you please create an exmple repo of your solution? I'm new to vue and i don't really understand what do you mean by "add a utility to the Vue prototype to allow adding store modules the same way as vue components. "

@arpowers
Copy link

arpowers commented Jul 3, 2018

@jumika just an example

router.onReady(() => {
  const loadRouteComponents = router.getMatchedComponents()

  loadRouteComponents.forEach(c => {
    if (c.storeModule) {
      c.storeModule.forEach(mod => {
        if (!store._modules.root._children[mod.name]) {
          store.registerModule(mod.name, mod)
        }
      })
    }
  })

  app.$mount("#app")
})

@960590968
Copy link

I register store in server side,only state can use in client side, mapGetter and mapActions not working.how to resolve it.

@Avcajaraville
Copy link

Yeah, Im having the same issue and I can't find a proper workaround for this.

Any idea guys ?

And btw, wouldn't be great to have the source code of the full project ? Something we can use as reference during our local experiments ;)

@Avcajaraville
Copy link

@devCrossNet Thanks ! This definitely put me on the right track !!

What I ended up doing, as suggested on your reference (and so this solutions is also here in case someone else is coming, or in case someone have some toughs on it), is to create a helper function in charge of load modules:

export function registerStoreModule({ module, moduleName, store }) {
  const moduleIsRegistered = store._modules.root._children[moduleName] !== undefined;
  const stateExists = store.state[moduleName];
  if (!moduleIsRegistered) {
    store.registerModule(moduleName, module, { preserveState: stateExists });
  }
}

This function will only register the module if it wasn't registered before.

Then, on your modules (where you wanna lazy load your nested store); you declare two methods:

  • asyncData: as specified on the guide, but register the module via the previously mentioned helper.
  • beforeCreate: which will be called on the client on page load (as opposed to asyncData, which wont).
  ...
  asyncData ({ store }) {
    registerStoreModule({ module: homeStore, moduleName: storeName, store });
    return store.dispatch('home/add');
  },
  ...
  beforeCreate() {
    console.log('before');
    registerStoreModule({ module: homeStore, moduleName: storeName, store: this.$store });
  },
  ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants