superwf / vuex-cache Goto Github PK
View Code? Open in Web Editor NEWcache vuex action when dispatch
License: MIT License
cache vuex action when dispatch
License: MIT License
In my Component tests, I want to mock/fake my Vuex Store
and the cache
property to make sure that my component is integrating with my actions correctly.
However because the StoreCache
interface isn't exported I can't create a typesafe mock/fake.
If you pass a payload with circular references to store.cache.dispatch it throws TypeError: Converting circular structure to JSON
.
This can be checked by running the code below.
import Vue from 'vue';
import Vuex, { Store } from 'vuex';
import cache from 'vuex-cache';
Vue.use(Vuex);
const store = new Store({
plugins: [
cache({})
],
actions: {
show(_, value) {
console.log(value);
}
}
});
const a = {}, b = {};
a.b = b;
b.a = a;
store.cache.dispatch('show', a); // It throws an error.
See by yourself on CodeSandbox.
@superwf vuex-cache
uses JSON.stringify
and it carries its limitations. So I propose fallback to uncached action instead of just throws Error.
Hi,
I'm testing this plugin. However it does not seem to be working.
app.js
import Vue from 'vue'
import Vuex from 'vuex'
import vuexCache from 'vuex-cache'
Vue.use(Vuex)
// Load store modules dynamically.
const requireContext = require.context('./modules', false, /.*\.js$/)
const modules = requireContext.keys()
.map(file =>
[file.replace(/(^.\/)|(\.js$)/g, ''), requireContext(file)]
)
.reduce((modules, [name, module]) => {
if (module.namespaced === undefined) {
module.namespaced = true
}
return { ...modules, [name]: module }
}, {})
export default new Vuex.Store({
plugins: [vuexCache],
modules
})
modules/exchange.js
import Trade from '~/api/trade.js'
import * as types from '../mutation-types'
// state
export const state = {
exchanges: null,
symbols: null
}
// getters
export const getters = {
exchanges: state => state.exchanges
}
// mutations
export const mutations = {
[types.SET_EXCHANGES] (state, { data }) {
state.exchanges = data
},
[types.SET_SYMBOLS] (state, { data }) {
state.symbols = data
}
}
// actions
export const actions = {
getExchanges ({ commit, dispatch }) {
return Trade.getExchanges()
.then(exchanges => {
commit(types.SET_EXCHANGES, exchanges)
})
.catch(error => console.log(error))
},
getSymbols ({ commit, dispatch }) {
return Trade.getSymbols()
.then(symbols => {
commit(types.SET_SYMBOLS, symbols)
})
.catch(error => console.log(error))
}
}
And in the component I have this
created () {
if (this.$store.cache.has('exchange/getExchanges')) {
console.log('cached')
}
this.$store.cache.dispatch('exchange/getExchanges')
.then(() => {
console.log(this.exchange.exchanges)
})
this.$store.cache.dispatch('exchange/getSymbols')
.then(() => {
console.log(this.exchange.symbols)
})
}
I expect the console to output cached
and I also expect the actions not to happen at each load, however both fail.
How can I solve this?
Could you please explain how to use this plugin with a Vuex in a module mode?
I like the new timeout feature. It would be nice if we could set a default timeout while still being able to give a timeout per dispatch as well. Perhaps it could be passed into the constructor when the plugin is initialized:
import Vuex from 'vuex'
import vuexCache from 'vuex-cache'
const store = new Vuex.Store({
state: {
...
},
plugins: [vuexCache(DEFAULT_TIMEOUT_VALUE_HERE)],
mutations: {
...
},
actions: {
...
}
})
When passing a timeout value in the Vuex call like so:
await this.$store.cache.dispatch('customer/getInfo', {
timeout: SettingsConstants.longCacheTimeout
})
It looks like it is adding the timeout value to the name of the cache key. So after making that call and then running this.$store.cache.entries()
, I see that the cache entry name is set to:
customer/getInfo:{"timeout":21600000}
This is kind of annoying because now I will have to pass the timeout value to all calls, such as .has()
and .delete()
.
For example, the following would not find or delete the cache key (which was unexpected):
this.$store.cache.has('customer/getInfo')
this.$store.cache.delete('customer/getInfo')
So I would have to do this instead:
this.cache.has('customer/getInfo', {
timeout: SettingsConstants.longCacheTimeout
})
this.cache.delete('customer/getInfo', {
timeout: SettingsConstants.longCacheTimeout
})
Could the settings object be excluded from the cache key name while still including the Vuex params in the name? Thanks.
If the action return a promise in which there is an axios request (post or get), how to use vuex-cache?
I made commit(response.data) within axios response.
I tried, but the content within cache is promise. Though there is no other network request for the second time request, but the result is not correct.
Receiving the following error on an iOS 9.3.2 device when using this plugin:
Unexpected keyword 'const'. Const declarations are not supported in strict mode.
I assume it has to do with babel 7 configuration (as old Safari versions do not natively handle const
). So would this be something we need to handle in then config of our project or is this something wrong in the plugin code? If so, any tips to help me accomplish that? I am lost when it comes to webpack and babel configs. Thanks!
Would be nice to have a helper function similar to mapActions
, but for our cache actions.
import types from '@/store/types';
import { mapCacheActions } from 'vuex-cache';
export default {
...,
methods: mapCacheActions({
fetchUser: 'user/FETCH',
fetchServices: { type: 'service/FETCH', timeout: 5000, param: 'ACTIVE' }
}),
mounted () {
this.fetchUser();
this.fetchServices();
}
};
mapCacheActions should resolve it's arguments
mapCacheActions({ A: 'ACTION' }) === ({
A () {
return this.$store.cache.dispatch('ACTION');
}
})
mapCacheActions({ B: { type: 'ACTION', timeout: 200, param: '0' } }) === ({
B () {
return this.$store.cache.dispatch({ type: 'ACTION', timeout: 200, param: '0' });
}
})
The argsToString
function ignore falsy values.
Lines 10 to 12 in 637815b
We shouldn't ignore any value, even undefined
should be used to generate cache's key.
This can be checked by running the code below.
import Vue from 'vue';
import Vuex, { Store } from 'vuex';
import cache from 'vuex-cache';
Vue.use(Vuex);
const store = new Store({
plugins: [
cache({})
],
actions: {
hi(_, payload) {
console.log('Hi', payload);
}
}
});
store.cache.dispatch('hi', undefined); // It shows 'Hi undefined'
store.cache.dispatch('hi', null); // It don't show anything
store.cache.dispatch('hi', 0); // It don't show anything
store.cache.dispatch('hi', ''); // It don't show anything
store.cache.dispatch('hi', NaN); // It don't show anything
store.cache.dispatch('hi', false); // It don't show anything
See by yourself on CodeSandbox.
import { cacheAction } from 'vuex-cache';
// ...
const actions = {
'FETCH_STARGAZERS': cacheAction(
({ cache, commit }, payload) => (
cache.dispatch('FETCH_REPOSITORIES')
.then((repos) => Promise.all(repos.map(getStargazers)))
.then((stargazers) => {
commit('STARGAZERS', [].concat(...stargazers));
})
)
),
'SET_STARGAZERS': (context, payload) => { ... }
}
FETCH_LISTINGSV2_CACHED: cacheAction(function({ cache, dispatch, commit }, id) {
console.info('FETCH_LISTINGSV2_CACHED', id)
cache.dispatch('GET_LISTINGV2', id)
}),
async GET_LISTINGV2({ commit }, id) {
const response = await this.$axios.get('/listings/' + id, {
baseURL: `https://${APISERVERS[Math.floor(Math.random() * APISERVERS.length)]}/api/v1`,
headers: headers
})
const { listing } = await response
return listing
},
How might one go about using vuex-cache with Nuxt.js?
The way the Vuex store works with Nuxt doesn't support a way to use the plugins: [vuexCache]
syntax, so I was wondering if there might be an alternate way to instantiate vuex-cache with the store?
A similar plugin (vuex-persistedstate) has this as a solution:
// nuxt.config.js
...
plugins: [{ src: '~/plugins/localStorage.js', ssr: false }]
...
// ~/plugins/localStorage.js
import createPersistedState from 'vuex-persistedstate'
export default ({store}) => {
window.onNuxtReady(() => {
createPersistedState({
key: 'yourkey',
paths: [...]
...
})(store)
})
}
Perhaps a similar method might work for vuex-cache?
Should a rejected promise be cached or skipped?
It could be a great feature to have some kind of configuration for all cache actions.
We could pass to the cache.dispatch an options objeto which may have a ttl (seconds or something else) that will clear that cached item after some configurable time.
This would avoid the delete of all cached items.
I'll try to do it and then I'll make a pill request as soon as I can.
Thanks for the plug-in ;)
I have a store module named 'cache', like so:
import cache from '@/modules/cache/_store';
export default new Vuex.Store({
modules: {
cache,
},
strict: true,
});
But using this.$store.cache.dispatch('cache/doSomething');
from within a component throws a "TypeError: this.$store.cache is undefined"
Originally posted by @bradley-varol in #9 (comment)
When default timeout is defined in:
const store = new Vuex.Store({
plugins: [createCache({timeout: 15000})],
modules: {
mystore: MyStore,
}
and then you have createAction used in a module
const MyStore = {
namespaced: true,
actions: {
"loadCached": cacheAction(async ({ cache }) => {
cache.dispatch('load');
}),
}
}
With this combination when called store.cache.dispatch('MyStore/loadCached')
it will never timeout as cacheAction uses own timeout options and not default one. With this combination you always need to define timeout to cacheAction as well.
Someone try to use it with https://github.com/ktsn/vuex-class and https://github.com/vuejs/vue-class-component ?
Should a native action call (e.g. without .cache
) update an already existing cache? Or is the sequence below intended?
let users = ['Sven', 'Kurt']
store.dispatch('setUsers', users)
store.cache.dispatch('getUsers') // ['Sven', 'Kurt'] (not cached)
users.push('Nisse')
store.dispatch('setUsers', users)
store.dispatch('getUsers') // ['Sven', 'Kurt', 'Nisse'] (native action, not cached)
store.cache.dispatch('getUsers') // ['Sven', 'Kurt'] (cached)
I'm using Vuex 3 for a while with vuex-cache
and today I realize vuex-cache
is warning about incorrect peer depedency.
warning " > [email protected]" has incorrect peer dependency "vuex@^2.2.1".
Adding 3.x to peerDependencies would solve it, because 3.0 doesn't break plugins and dispatch APIs, but looking at Vuex 1 docs you can also notice plugins and dispatch APIs are same too. So, vuex-cache
is compatible with all vuex versions and there's no reason to keep a peerDependencies
checking it.
I'm just ignoring Vuex 0.x...
Hi!
I miss a complete working example how to use vuex-cache
. Can this be added to the documentation or a separate repo please?
Thx
Are there any plans on adding the option to persist the cached data?
I want to keep the cached data within a browser session (including page reloads). I thought about combining this plugin with vuex-persistedstate to get the expected behaviour. Unfortunately I found out that the cache data is not stored in the module's state, so it doesn't work as expected.
Are there any plans on making this plugin compatible with other plugins by offering an interface to load an initial state, ..? Or even better implementing a persistence option in this plugin?
Thanks in advance.
Hello I have a feature request for vuex-cache
An idea to cache also the requests with parameters?
'use strict';
var index = (function (store) {
store.cache = new Map();
store.cache.dispatch = function () {
var type = arguments[0];
if (arguments[1]) {
type = type + JSON.stringify(arguments[1]);
}
if (!store.cache.has(type)) {
store.cache.set(type, store.dispatch.apply(store, arguments));
}
return store.cache.get(type);
};
});
module.exports = index;
Hi, thank you for creating this plugin.
I made vuex-cache as plugin on NuxtJS and successfully call it on pages as mention in documentation , call store.cache.dispatch
in fetch
method to initiate cache of ajax call.
I think I need to call cache
on store module actions,
I have this sample store module on NuxtJS
// store module for User Interface
export const state = () => ({
snackbar: false,
snackbarText: '',
snackbarColor: 'secondary',
snackTimeout: 4000
})
// mutations defined as object
export const mutations = {
setState(state, params) {
const keys = Object.keys(params)
keys.forEach(key => (state[key] = params[key]))
}
}
export const actions = {
showAlert({ commit }, params) {
commit('setState', {
snackbar: true,
snackbarText: params.msg,
snackTimeout: params.timeout > 0 ? params.timeout : 4000,
snackbarColor: params.color !== '' ? params.color : 'secondary'
})
},
// I hope there is `cache` object
getUUID({ cache, dispatch, commit, state }) {
this.$axios.setHeader('Authorization', 'Bearer ' + state.accKey)
return this.$axios.$get('/jsonapi').catch(() => {
const alertMsg = {
msg: 'Get UUID failed'
}
dispatch('showAlert', alertMsg)
})
},
async doLogin({ cache, commit }) {
// an axios call
....
commit('setState', {
isLogin: true,
uuid: 'sample-uuid-string',
})
....
// I hope I can do this in the store action
cache({timeout: 60000}).dispatch('showAlert', alertMsg)
},
async doLogout({ commit }) {
commit('setState', {
isLogin: false,
uuid: '',
})
// I hope I can do this in the store action
cache.clear()
},
}
How I can call cache
on store module itself ?
Using the latest version of the library, I receive the following error on an Android 4.4.2 WebView. Wondering if this was caused by recent changes to babel configs (#23)?
Uncaught ReferenceError: Map is not defined
Cache promise rejections is a common question (#7, #16), and I believe we need to discuss if we need cache actions errors or dismiss them?
vue-cache
rejections on our stores is undesirable behavior;This issue relates to #41 as I'm wanting to mock/fake the StoreCache
for Component tests.
When dispatching (see vuex-cache.js#L297, #L305) this
is bound to the store
not the store cache. Given the cache.dispatch
method doesn't use this
, this bug probably hasn't surfaced yet. However when mocking/faking store.cache
the cache methods are being called with the wrong this
.
Hi superwf,
I think this is a great package and would like to help a bit with the README to make it clearer and easier to read.
Would you accept a pull request for this?
Cheers!
I'm did read docs.
I'm create plugins/vuex-cache.js
End config nuxt.config.js
I use this.$store.cache.dispatch("modulesA/actionA") in folder components.
F5 router => alert " Cannot read property 'dispatch' of undefined"
? How to fix?
cache.dispatch = async (...args) => {
const type = argsToString(args)
const timeout = getTimeout(args, option)
if (timeout) {
const now = Date.now()
if (!timeoutCache.has(type)) {
timeoutCache.set(type, now)
} else {
const timeoutOfCurrentType = timeoutCache.get(type)
// console.log(now - timeout, timeoutOfCurrentType)
if (now - timeout > timeoutOfCurrentType) {
cache.delete(type)
timeoutCache.delete(type)
}
}
}
if (!cache.has(type)) {
cache.set(type, await store.dispatch.apply(store, args))
}
cache.dispatch 使用async/await,当dispatch报错时不set cache?
The cache
's method has
returns true
for expired actions. For me it should return false
because the cache
will be ignored, so is like it was not there.
This can be checked by running the code below.
import Vue from 'vue';
import Vuex, { Store } from 'vuex';
import cache from 'vuex-cache';
Vue.use(Vuex);
const store = new Store({
plugins: [
cache({ timeout: 10000 })
],
actions: {
hi () {
console.log('Hi');
}
}
});
store.cache.dispatch('hi'); // It shows 'Hi'
store.cache.dispatch('hi'); // It don't show anything
setTimeout(() => {
store.cache.has('hi'); // It returns `true`, but the action is already expired
store.cache.dispatch('hi'); // It shows 'Hi'
}, 10000);
See by yourself on CodeSandbox.
Each time i run a production build, the build fails on Vue-cache module with this error.
UglifyJs Unexpected token: operator (>) [./node_modules/vuex-cache/index.module.js:1,0][app.js:43610,17]
In Nuxt 2.12+, there's a new fetch()
method.
Example:
<script>
export default {
async fetch() {
await this.$store.cache.dispatch('getUser');
},
};
</script>
Results in:
ERROR Error in fetch(): Cannot read property 'dispatch' of undefined
at VueComponent.fetch (server.js:6668:29)
at VueComponent.serverPrefetch (server.js:2515:31)
at waitForServerPrefetch (node_modules/vue-server-renderer/build.dev.js:8276:34)
at renderComponentInner (node_modules/vue-server-renderer/build.dev.js:8426:3)
at renderComponent (node_modules/vue-server-renderer/build.dev.js:8383:5)
at RenderContext.renderNode (node_modules/vue-server-renderer/build.dev.js:8294:5)
at RenderContext.next (node_modules/vue-server-renderer/build.dev.js:2598:23)
at cachedWrite (node_modules/vue-server-renderer/build.dev.js:2451:9)
at renderElement (node_modules/vue-server-renderer/build.dev.js:8544:5)
at renderNode (node_modules/vue-server-renderer/build.dev.js:8296:5)
My vuex-cache.js
in ~/plugins
:
import createVuexCache from 'vuex-cache';
export default ({ store, isHMR }) => {
if (process.browser) {
if (isHMR) {
return;
}
const options = {
timeout: 2 * 60 * 60 * 1000, // Equal to 2 hours in milliseconds.
};
const setupVuexCache = createVuexCache(options);
window.onNuxtReady(() => setupVuexCache(store));
}
};
I assume it's just because it is attempting to dispatch server-side but vuex-cache
hasn't attached itself yet correctly to the $store
instance.
Not sure if there's a solution to ensure that all dispatches server-side bypass the cache, and all client-side navigations include the cache action - barring doing twice the dispatch calls wrapped within if (process.browser) {
checks?
Provide type definitions for TypeScript developers and IDE/Editor intellisense.
Just a suggestion,
Would be great if there was a way to set an expiry on the cache (ie 5 mins etc).
Something like store.cache.dispatch('LIST', 5 * 60, {..additional args})
etc
I can see where there might be issues keeping track of if additional arguments are expiries or normal data, but just off the top of my head:
store.cache.dispatch = function () {
const type = arguments[0]
let current = store.cache.get(type)
let now = new Date();
if (!store.cache.has(type) || current.expiry < now) {
// Somehow add the expiry with expiry = now.setSeconds(now.getSeconds + arguments[1]) then splice it from the arguments?
store.cache.set(type, store.dispatch.apply(store, arguments))
}
return store.cache.get(type)
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.