-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
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
Allow function to be passed as namespace argument for mapState, mapGetters, mapMutations & mapActions #863
Comments
ref #748 |
You could use |
Another workarround which I am considering is to use vuex-connect. |
I think this is covered by passing a function to mapState/mapActions/mapMutations (related #924). {
props: ['namespace'],
computed: mapState({
state (state) {
return state[this.namespace]
},
someGetter (state, getters) {
return getters[this.namespace + '/someGetter']
}
}),
methods: {
...mapActions({
someAction (dispatch, payload) {
return dispatch(this.namespace + '/someAction', payload)
}
}),
...mapMutations({
someMutation (commit, payload) {
return commit(this.namespace + '/someMutation', payload)
})
})
}
} |
Will the dispatch return promise? If yes, personally, is it good practice to assign the data from that? |
@ktsn I'm surprised that when I use arrow function |
@ktsn it won't work neatly for deeper namespaces - e.g. mapState({
someField: (state) => state["level1/level2/level3"].someField, // doesn't work
someOtherField: (state) => state.level1.level2.level3.someOtherField, // works
}); To make it work you would have to replace |
@rATRIJS Yes, the nested state need to be handled by kind of I'm closing this issue since this usage can be covered by #863 (comment). |
The workaround does not really improve the code in my opinion. Workaround: computed: ...mapState({
state (state) {
return state[this.namespace]
},
someGetter (state, getters) {
return getters[this.namespace + '/someGetter']
}
}), Code without mapState: computed: {
state () {
return this.$store.state[this.namespace]
},
someGetter () {
return this.$store.getters[this.namespace + '/someGetter']
}
}, The only thing mapState gives me in this case is getting rid of "this.$store." The price is adding "...mapState" and the function parameters "state" and "getters" (state even unused in the case of getters). I think i will go with something along the lines of Linus suggestion #863 (comment) for now, or implement some mapper on my own. Please think about better support for dynamic namespaces again :) |
I agree with @ThomasKruegl here in that the proposed workaround is not ideal as it makes for fairly different looking code throughout the app if you use a mix of statically and dynamically named modules. With a static namespace:
If this feature was implemented as proposed, dynamic namespaces would look quite similar:
Compared to the proposed workaround, which looks and feels different - and loses the ability to use the shorthands provided by the array notation:
Even the workaround proposed by @LinusBorg above is a fairly significant deviation from the standard pattern and usage of @ktsn Is there any chance you'd reconsider? |
For the time being, we've re-implemented these functions locally using something like the following:
But we'd love to get rid of the variance between using In our specific use case, we use route-specific modules so we can use the same Vuex store across dynamic routes. Consider an e-commerce site with a route like Here's a codepen showing the issue that caused us to go down this route-specific module path in the first place: https://codepen.io/brophdawg11/pen/zeyoBN |
@ktsn Thanks you help me a lot |
I have something like this which seems working for me.
use them like:
or mutations:
|
I like your answer @GopherJ but I have a doubt: where do you define "stateWrapper" within the component script? It needs access to this.namespace so it should be a component method, right? Clearly I'm missing something here. |
I see here only "workarounds" but no real support, which causes a headache. We need dynamic namespaces for better support for a big codebase.
still does not work, We have to do crap like this
or else junk like this |
As Linus recommended above, I've found another style of work around by utilising beforeCreate to access the variables you want from the props passed into your component instance: import { createNamespacedHelpers } from "vuex";
import module from '@/store/modules/mymod';
export default {
name: "someComponent",
props: ['namespace'],
beforeCreate() {
let namespace = this.$options.propsData.namespace;
const { mapActions, mapState } = createNamespacedHelpers(namespace);
// register your module first
this.$store.registerModule(namespace, module);
// now that createNamespacedHelpers can use props we can now use neater mapping
this.$options.computed = {
...mapState({
name: state => state.name,
description: state => state.description
}),
// because we use spread operator above we can still add component specifics
aFunctionComputed(){ return this.name + "functions";},
anArrowComputed: () => `${this.name}arrows`,
};
// set up your method bindings via the $options variable
this.$options.methods = {
...mapActions(["initialiseModuleData"])
};
},
created() {
// call your actions passing your payloads in the first param if you need
this.initialiseModuleData({ id: 123, name: "Tom" });
}
} I personally use a helper function in the module I'm importing to get a namespace, so if I hadmy module storing projects and passed a import projectModule from '@/store/project.module';
export default{
props['projectId'], // eg. 123
...
beforeCreate() {
// dynamic namespace built using whatever module you want:
let namespace = projectModule.buildNamespace(this.$options.propsData.projectId); // 'project:123'
// ... everything else as above
}
} Hope you find this useful. |
I would love to have this feature built in as well. |
I've been struggling with this lately, and I approached the problem this way: {
computed() {
const namespace = this.$options.propsData.namespace;
return {
...mapGetters(namespace, ['one', 'two', 'three']);
}
}
} Unfortunately, for some reason Vue doesn't support this for computed...So I went ahead and made a simple plugin that allows us to do just that: const ComputedOnSteroids = {
install(Vue) {
Vue.mixin({
beforeCreate() {
let computed = this.$options.computedOnSteroids || {};
if (typeof computed === 'function') {
computed = computed.call(this, this);
}
this.$options.computed = {
...(this.$options.computed || {}),
...computed
};
}
})
}
} Install the plugin: Vue.use(ComputedOnSteroids); And now in our components we can simply pass a function to {
computedOnSteroids() {
const namespace = this.$options.propsData.namespace;
return {
...mapGetters(namespace, ['one', 'two', 'three']);
}
}
} WARNING: I'm not a Vue internals expert, so use with caution :) |
Just FYI in case it's helpful for anyone, we finally got around to open-sourcing our solution to this, referenced in #863 (comment). The 4 Hope this helps! I also wrote up a blog series about some of the use cases we had that led to this: https://www.brophy.org/post/instance-aware-vuex-modules-1 |
@ktsn Is this a way to do this? |
@Gazorpazo0rp thanks, that helped me a lot. this.$options.computed = {
...this.$options.computed,
...mapState(['dataList']),
...mapGetters(["getById"])
}
this.$options.methods = {
...this.$options.methods,
...mapActions(["loadData","deleteRows"]),
...mapMutations({ cpRow: 'copyRow2Model'})
} |
What problem does this feature solve?
Right now I can set namespace when calling mapState. However, mapState only accepts string as a namespace. Unfortunately, I can not use Vue props values as these are only available after the component has been created. It means that I need to hardcode Vuex namespace and it is impossible to reuse the component.
What does the proposed API look like?
I would like the following functionality:
To implement this, one would need to change mapState implementation. normalizeNamespace function would need to be called later (inside mappedState). Since mappedState is called only after the component has been initialized, it would be possible to resolve .namespace prop by calling the passed function.
I could provide a small code sample with my implementation (rewrite of current mapState) if that would help in any way.
The text was updated successfully, but these errors were encountered: