I have got a component which might not request an ajax call if some data has been passed into it. However if the data hasn't been passed in I need to fetch it, so I want to import Axios then, save importing it for no reason.
How can I wait for the script to be imported before attempting to use it, as the below doesn't work:
export default {
props: {
vehicleId: {
type: Number|String,
required: true,
default: () => null
},
settings: {
type: Object,
default: () => null
}
},
beforeCreate() {
if (!this.settings) {
const Axios = () => import('../../../axiosConfig');
Axios.get('/api/v1/media-slider-settings').then(response => {
this.settings = response.data;
});
}
},
Dynamic import return a Promise, so you must to use then function.
Try something like that:
<script>
export default {
beforeCreate() {
if (!this.settings) {
import('../../../axiosConfig').then(axios => {
axios.get('/api/v1/media-slider-settings').then(response => {
this.settings = response.data;
});
});
}
},
};
</script>
Avoid the approach with async/await because the lifecycle functions don't support asynchronous in Vue.js.
You're almost there, import() is async, so just do:
// or use .then if you're not in an async function
const Axios = (await import('../../../axiosConfig')).default
Axios.get('/api/v1/media-slider-settings').then(response => {
this.settings = response.data;
});
and notice that import() returns the module, so you need to get the .default property if you need the default export (like in your case) or just call .someExportedName for importing a named export (i.e. non-default export from the module)
Related
I have a mock file of my api.js located in __mocks__/api.js with default implementation like this:
export function api() {
return {
systemInfo: jest.fn(() => Promise.resolve({ system_info: { mi_workers_count: 3 } }))
}
}
however I want to change this default behaviour in one test, so I've tried to do it following way which doesn't work:
import { api as mockApi } from "__mocks__/api" //tried also from original impl. file "api"
test("something", async () => {
console.log(mockApi().systemInfo.getMockImplementation()())
mockApi().systemInfo.mockImplementationOnce(() =>
Promise.resolve({ system_info: { mi_workers_count: 1 } })
)
console.log(mockApi().systemInfo.getMockImplementation()())
})
both console.log will return same, origin promise response implemented in __mocks__/api
In order to do this, a spy should be accessible outside the scope it's defined. Since it's defined inside api, this cannot be done, mockApi().systemInfo !== mockApi().systemInfo.
The spy can be exported separately:
export const mockSystemInfo = jest.fn(() => Promise.resolve({ system_info: { mi_workers_count: 3 } }));
export function api() {
return {
systemInfo: mockSystemInfo
}
}
The implementation can be changed with:
mockSystemInfo.mockImplementationOnce(...)
I have 2 actions that make GET requests and save the response in the Vuex store. The first action getVersion() gets the most recent version of the game and that version is required in order to make the second GET request. Right now I've hard coded the version in the second action, however, my goal is to concatenate it inside the URL.
Sadly I'm not sure how to access it from inside the function. Console.log(state.version) returns null for some reason even though it shouldn't be. I call these functions from inside App.vue like this:
mounted(){
this.$store.dispatch('getVersion')
this.$store.dispatch('getChampions')
}
Vuex store
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
version: null,
champions: null
},
mutations: {
version(state, data){
state.version = data.version
},
champions(state, data){
state.champions = data.champions
}
},
actions: {
getVersion({commit}){
axios.get("http://ddragon.leagueoflegends.com/api/versions.json")
.then((response) => {
commit('version', {
version: response.data[0]
})
})
.catch(function (error) {
console.log(error);
})
},
getChampions({commit, state}){
axios.get("https://ddragon.leagueoflegends.com/cdn/9.24.1/data/en_US/champion.json")
.then((response) => {
commit('champions', {
champions: response.data.data
})
})
.catch(function (error) {
console.log(error);
})
}
},
getters: {
version: (state) => {
return state.version;
},
findChampion: (state) => (id) => {
let championId = id.toString();
let champion = Object.values(state.champions).find(value => value.key === championId);
return champion
}
}
})
With this part:
this.$store.dispatch('getVersion')
this.$store.dispatch('getChampions')
The second dispatch doesn't wait for the first one to finish. Meaning that it is firing before the first one has had a chance to finish getting the version.
You need to create a promise that should resolve before the second dispatch is called.
You could try doing it this way:
async mounted(){
await this.$store.dispatch('getVersion')
await this.$store.dispatch('getChampions')
}
or if you don't want to use async/await
this.$store.dispatch('getVersion').then(() => {
this.$store.dispatch('getChampions');
});
And in the action you should add return to the request (this is important):
return axios.get(...
dispatcher returns a promise
this.$store.dispatch('getVersion').then(()=>{
this.$store.dispatch('getChampions');
});
In react I am trying to make a "Rest API call via Axios". I made a service file and then when reducer is trying to console.log the output of the service. it is giving error. Please help.
someReducer.js
import getItemsAPI from '../../services/service1';
...
case "GET_ITEM_LIST": {
let data = getItemsAPI.getItems();
console.log(data);
return {
...state,
items: data
}
}
service1.js
class getItemsAPI {
getItems() {
return this.axiosInstance
.get('https://jsonplaceholder.typicode.com/users/')
.then((response) => response.data);
}
}
export default getItemsAPI;
Error:
If you use a class, you must use the new keyword in order to create an instance. Then, you can use its methods:
import getItemsAPI from '../../services/service1';
const getItemsInstance = new getItemsApi();
...
case "GET_ITEM_LIST": {
let data = getItemsInstance.getItems();
console.log(data);
return {
...state,
items: data
}
}
You don't need to use a class in order to export a function. You can export the function itself (in this case, inside an object):
const getItemsAPI = {
getItems: () => {
return axiosInstance
.get('https://jsonplaceholder.typicode.com/users/')
.then((response) => response.data);
}
}
export default getItemsAPI;
If you use the code above, you don't need to create an instance. You can simply use the object (like your doing in the OP).
Just a note as well. getItems will return a Promise. In order to get data, you must await or resolve the Promise before reducing.
I'm new into the React and Redux worlds and after a lot of research, I haven't found a way to handle the problem I have:
I need to perform an api call on app init, but the endpoint is in a configuration file. This configuration in the server so it has to be downloaded and read. This is because I need to distribute the app into many servers and each server has a different configuration.
Therefore the api call has to wait until the configuration has been loaded, they must be chained.
I'm using Redux to manage the state of the app so I have an action which downloads the configuration and an other action which performs the api call.
// Config action
export function fetchConfigRequest() {
return {
type: types.FETCH_CONFIG_REQUEST
}
}
export function fetchConfigSuccess(config) {
return {
type: types.FETCH_CONFIG_SUCCESS,
config
}
}
export function fetchConfig() {
return dispatch => {
dispatch(fetchConfigRequest());
return axios.get('config.json')
.then(response => {
dispatch(fetchConfigSuccess(response.data));
})
;
};
}
// Api client action
export function fetchDataRequest() {
return {
type: types.FETCH_DATA_REQUEST
}
}
export function fetchDataSuccess(data) {
return {
type: types.FETCH_DATA_SUCCESS,
data
}
}
export function fetchDataError(error) {
return {
type: types.FETCH_DATA_ERROR,
error
}
}
export function fetchData(filters = {}) {
return dispatch => {
dispatch(fetchDataRequest());
const apiClient = new apiClient({
url: state.config.apiEndpoint
});
return apiClient.Request()
.then(response => {
dispatch(fetchDataSuccess(data));
})
;
};
}
The only way that I got it working is by waiting until config action promise resolves in App component like this:
// App.component.js
componentDidMount() {
this.props.fetchConfig().then(() => {
this.props.fetchData();
});
}
But I don't think this is the best and the most "Redux style" way to do it, so how should I do it?
I've some ideas in my mind but I don't know what would be the best one:
Keep it as it is now
Create an 'app' action which dispatches the fetch config action, waits until config is loaded, and then dispatches the fetch data action
Do it into a custom middleware
Thanks!
I am trying to make my Vue app have server-side rendering. I am using vue-server-renderer (https://www.npmjs.com/package/vue-server-renderer). Client-side rendering is working fine.
My app use vue-router and axios
Here is my server.js:
server.get('*', (request, response) => {
bundleRenderer.renderToString({ url: request.url }, (error, htmlPromise) => {
if (error) {
// Log the error in the console
console.error(error)
// Tell the client something went wrong
return response
.status(500)
.send(error)
}
response.send(layout.replace('<div id=app></div>', htmlPromise))
})
})
getInfo() is the method to fetch server data.
Here is getInfo():
export default {
methods: {
getInfo(api) {
return axios
.get(api || this.$route.params.path)
.then((data) => {
this.data = data
this.$set(this, 'isLoading', false)
})
},
},
}
My server entry is:
import { app, router, store } from './index'
export default context => {
let componentPromises = router.getMatchedComponents().filter((component) => {
return component.methods && component.methods.getInfo
}).map((component) => {
return component.methods.getInfo()
})
return Promise.all(componentPromises).then(() => {
return app
})
}
However, I soon realize that all the components from router.getMatchedComponents() does not have $route or $set. Therefore, the method getInfo() stops working.
The document from https://router.vuejs.org/en/api/router-instance.html is very short and does not provide much information:
router.getMatchedComponents()
Returns an Array of the components (definition/constructor, not
instances) matched by the current route. This is mostly used during
server-side rendering to perform data prefetching.
How can I fix the problem?
I have previously incurred into a similar problem and managed to successfully prefetch data by doing the following:
app.$router.onReady(() => {
const matchedComponents = app.$router.getMatchedComponents()
if (!matchedComponents.length) { /* ... */}
Promise.all(matchedComponents.map((Component: any) => {
if (Component.options.methods.asyncData) {
return Component.options.methods.asyncData({
store: app.$store,
route: app.$router.currentRoute
});
}
})).then(() => { /* your callback here ... */ });
}
According to vue ssr documentation (https://ssr.vuejs.org/en/data.html) the suggested way is to use a custom asyncData method in your component to perform data fetching rather than calling component methods directly:
export default {
asyncData ({ store, route }) {
// return the Promise from the action
return store.dispatch('fetchItem', route.params.id)
}
},