Vuex with Nuxt - Unknown Action Type: - javascript

I am trying to move my local methods to a VueX Store. Im using nuxt.js so the store format is a bit different than usual. When I try to call my store action, i get "vuex unknown action type: fetchProducts". Its gotta be something to do with how im calling the store, but I havent quite figured it out.
So, Nuxt uses the store folder system, and I call to import map actions -
import { mapActions } from 'vuex';
then in my methods, I list map actions alongside the directory the method exists in an index.js file.
...mapActions('RyansBag/Inventory', [
'fetchProducts',
]),
in the same methods, I request to start pulling the data from the store from this method, which is mounted.
async getProducts(){
this.TableData.isLoading = true;
await this.$store.dispatch('fetchProducts', this.options);
this.TableData.isLoading = false;
},
my understanding is in nuxt i dont have to call an action by its module name - which nothing is in module - I think? Unless store folder system in nuxt is considered modules which I may have misunderstood.

for the fetch request, it should have been this.fetchProducts() instead of a string reference. Fixed. :)

Related

How to use plugins in vuex (Vue 2x) [duplicate]

I declare a global variable in the main.js of the Vue.js project.
Vue.prototype.$API = "myapihere"
And I want to use this from everywhere.
and it's work properly by using this.$API.
But in Vuex it does not work.
console.log(this.$API);
Here this.$API is undefined.
How I use my $API in Vuex.
Vue 2 and Vuex 3 answer
In the store you can access the vue instance by accessing this._vm
const store = new Vuex.Store({
mutations: {
test(state) {
console.log(this._vm);
}
}
});
I'm using Vue 3 and Vue.prototype.$foo seems to have been removed for this version. I also found that in my version of VueX there is no this._vm.
I explored the Provide / Inject method which is recommended by the Vue 3 docs. This worked nicely for accessing globals from within my components, but I couldn't access them from within store.
The solution I went for was to use globalProperties on the Vue object and standard properties on store, and set them just before mounting the app.
main.js:
import store from './store/index';
import App from './App.vue';
// Load custom globals
import conf from '#/inc/myapp.config';
const app = createApp(App)
.use(store);
// Register globals in app and store
app.config.globalProperties.$conf = conf;
store.$conf = conf;
app.mount('#app');
What I like about this is that I can access the globals in the same way in both store and components.
In a component:
export default {
data() {
return {
};
},
created() {
console.log( this.$conf.API_URL );
},
}
...and you can access this.$conf.API_URL in the same way from actions, mutations and getters.
Once I'd found this solution I no longer needed access to the whole Vue instance from within store, but if you need it for some reason you can assign store.$app = app; in the same place in main.js.
You have 2 approaches:
Pass down the property (or even access the _vm property from inside Vuex) as an argument from a component
methods: {
this.$store.dispatch('someAction', this.$API)
}
Declare and export that same variable from another file and consume it from your main.js AND your Vuex file:
// api.js
export const API = "http://localhost:5000/api"
// main.js
import { API } from './api.js
...
Vue.prototype.$API = API
// store.js
import { API } from './api.js
// you can use API now!
Although I would personally lean towards the second, I would not store the API path in Vue at all as I'd rather have the api.js file as a service to perform all ajax calls and consume that file from where I need.
use this._vm
here is why
by default when you access this in vuex store it will point store so it will output something like this
so after that, you see that there is something called _vm in store here it is
so that _vm points to the vue component so to access it you will need to use this._vue
you can better create a getter of the vue instance like
const store = new Vuex.Store({
getters: {
vue(state) {
return this._vm
}
}
});
//so you can use it across your store
store.getters.vue
//Note
//the above way of accessing getter works on non `namespaced` stores
As of recently, under Vuex 4.* and Vue 3.*, this.$app hasn't been defined for the store object. Instead you have Vue Router defined as this.$router.
So for javascript, the way to get app in store would be like so:
The code would now be: router.app = app; and inside, say, an action: let app = this.$router.app;

Is it safe to use setTimeout to bypass module import order issues?

In my CRA project, I have a configureStore.ts file that exports my redux store:
// ...
export const store = createStore(
rootReducer,
initialState,
compose(applyMiddleware(...middleware), ...enhancers)
);
and I subscribe to the store manually in staticIntl.ts, which is imported hundreds of times throughout the whole project
import { store } from "../configureStore"
class IntlManager {
constructor() {
store.subscribe(this.onStoreChange);
}
// ...
}
const manager = new IntlManager()
But when I run the project, the store.subscribe fails due to
TypeError: Cannot read property 'subscribe' of undefined
I am practically certain this is due to import order/availability
I managed to "fix" the issue by pushing the subscribe callback to the end of the task queue via no-delay setTimeout (I know it actually rounds up to couple of MS, but I don't think I really need to mind about that):
constructor() {
setTimeout(() => store.subscribe(this.onStoreChange));
}
Is this safe? I am mostly concerned about three things:
is it possible for the subscribe to still fail due to the same reason, even when wrapped in setTimeout ? For example if somehow processing my modules takes a long time
Is it possible for a first store update to happen before subscribtion (and thus missing on the subscribe callback call)?
Could such update happen as a result of my React app fetching data from the server and dispatching the response?
Are there any other potential pitfalls of my fix/hack?
Also, what other ways could I fix my problem? I'd be especially thankful for a link to a propper guide.
I practically certain this is due to import order
Yes, this sounds very much like a problem with circular dependencies. Your configureStore imports something that depends on staticIntl, which in turn imports the store from a module that is not yet evaluated.
Do not use setTimeout, fix the underlying problem with your module dependencies.
In particular, I would recommend
move const manager = new IntlManager() into a different file where you clearly control that it is executed after the createStore(…) call.
Use dependency inversion - remove the store.subscribe(this.onStoreChange) line from the IntlManager constructor and instead write store.subscribe(manager.onStoreChange) after you created store.
I'm assuming IntlManager is a singleton, and you only need one of these in your app - if this is not true, ignore the following.
Following the classic redux setup https://redux.js.org/recipes/configuring-your-store my advice is to pass the store as a prop to your main App component. And then create an instance of IntlManager by passing the store prop to the constructor:
const store = configureStore()
render(
<Provider store={store}>
<App store={store} />
</Provider>,
document.getElementById('root')
)
in App component:
const { store } = this.props;
const manager = new IntlManager(store);
Wether this works depends a bit on how IntlManager is actually used by your app. You wrote that it is "imported hundreds of times throughout the whole project". This is ok I think, you might have to rewrite the class a bit so that you can instantiate it right away, but subscribe to the store later, when the store is actually available. I envision something like this:
in App component:
import manager from '.../IntlManager';
// somewhere in componentDidMount for example:
const { store } = this.props;
manager.subscribeToStore(store);

When using dynamic import in a function, how can I specify type info in global variable?

My simplified server code looks like below.
server.ts
import google from "googleapis";
const androidPublisher = google.androidpublisher("v3");
app.use('something', function(req, res, n){
...
})
...(only one of the dozens of other methods use androidPublisher)
I am importing googleapis library in order to setup androidpublisher variable. However, this googleapis library is big and it takes 400ms~700ms to fully import file, when it takes 10ms~30ms to import other library files.
Because my environment is serverless architecture (firebase functions), AND because approximately 1 out of 100 requests actually need androidPublisher, I want to take advantage of dynamic import to import googleapis when it is necessary. Otherwise, above setup actually adds 400ms/700ms latency to every request that spins up new serverless instance, even when androidPublisher is not needed.
So I have made changes like below.
server.ts
let androidPublisherInstance:any;
async function getAndroidPublisher() {
const googleapis = await import("googleapis");
if (androidPublisherInstance === undefined) {
const ap = googleapis.google.androidpublisher("v3");
androidPublisherInstance = ap;
}
return androidPublisherInstance;
}
...(one of methods use getAndroidPublisher() to get androidPublisher instance)
with above setup where I am using global variable & helper function to initialize androidPublisher only when needed. This works as intended and 400ms~700ms latency gets added when androidPublisher is needed for the first time. However, I ended up with type of androidPublisherInstance to be any. I couldn't correctly define the type because type definition is available inside of googleapis and that is residing inside of getAndroidPublisher function.
Thus, I lose all benefit of using typescript and have to play guess games while using methods/properties when I use androidPublisherInstance.
And I think I must use global variable, because I do not want to initialize androidPublisher multiple times (googleapis.google.androidpublisher("v3")) whenever a function calls getAndroidPublisher()
Am I missing something? Is there a way to use dynamic import & let client to be initialized only once without needing to use global variable?
You can just import the type. As long as you use it only in type definitions, not in value expressions, the compiled JavaScript will never load the module:
import { androidpublisher_v3 } from "googleapis";
let androidpublisher: androidpublisher_v3 | undefined;
Alternatively, to make sure you don't accidentally reference it in the wrong place, use only import types:
let androidpublisher: import("googleapis").androidpublisher_v3 | undefined;

How to structure Meteor app and load into Meteor shell

I'm having a number of issues putting together a very simple piece of code as I learn Meteor. See the comments, which are questions.
server/main.js
import { Meteor } from 'meteor/meteor';
import { Post } from './schema'
// Why is this required to make Post available in Meteor.startup?
// Isn't there auto-loading?
Meteor.startup(() => {
console.log(Post)
// This works, but why isn't Post available to meteor shell?
});
server/schema.js
import { Post } from './models/post'
export { Post }
server/models/post.js
import { Class } from 'meteor/jagi:astronomy';
// Why can't this be imported elsewhere, like main.js?
const Posts = new Mongo.Collection('posts');
const Post = Class.create({
name: 'Post',
collection: Posts,
fields: {
title: { type: String },
userId: String,
publishedAt: Date
},
});
export { Post }
In addition to these questions, how can I load my app into meteor shell? Post is undefined there, even though it's defined in Meteor.startup. I tried using .load with an absolute path, but this breaks my app's imports, which use relative paths.
As for which errors I'm confused about:
When I try and use import inside Meteor.startup(), I get an error that the keyword import is undefined. I'm using the ecmascript package.
When I don't import { Class } in the same file where I use Class, I get an unknown keyword error.
If I don't import { Post } in main.js, then Post is undefined.
Not able to load app into Meteor shell.
To access exported objects in Meteor shell, use require:
> require('server/schema.js').Posts.findOne();
To access objects exported by packages, use the package name:
> require('react').PropTypes;
The reason you can't access an object that's imported by another js file is that each file has its own scope here. When Meteor builds your app, it doesn't just concatenate js files like many other build systems do, and that's really a good thing.
Basically a Javascript object gets created for every js file you write. Anything you export in a js file becomes a field in this object which you can access by using require. And import is just a nicer version of the same thing.

Load JSON data in my initital state

I try to learn React.js but I have some difficulties to load data from my server with redux. For the moment my global state is loaded with static json like this:
My store.js
import ...
// Load my static json data
import tickets from '../data/tickets.js'
import interventions from '../data/interventions.js'
const defaultState = {
tickets,
interventions
}
const store = createStore(rootReducer, defaultState);
export const history = syncHistoryWithStore(browserHistory, store);
export default store;
My application works perfectly. Now I want to fill my components with the data returned from my node.js server. However I have no idea how to corretly do it.
I have to fill my defaultState only once ? After the reducers will change the values ?
Is isomorphic-fetch is a good way to call a url from my server ? (I use react-router on my client)
Thanks for your answers.
if you try to do this on client side you would be asynchronous.
On your entry point, where you are importing the store, you can fetch your data then dispatch an action that will init your tickets or interventions.
You can also use redux-async-connect to give your app all required data before it's will be mounted, this can be use also on server rendering side

Categories