I am trying to create a VueJS component that does the following: 1) download some data (a list of options) upon mounted/created; 2) display the downloaded data in Multiselct; 3) send selected data back to parent when user is done with selection. Something like the following:
<template>
<div>
<multiselect v-model="value" :options="options"></multiselect>
</div>
</template>
<script>
import Multiselect from 'vue-multiselect'
export default {
components: { Multiselect },
mounted() {
this.getOptions();
},
methods:{
getOptions() {
// do ajax
// pass response to options
}
},
data () {
return {
value: null,
options: []
}
}
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
This is mostly straightforward if the component is only called once in a page. The problem is I may need to use this component multiple times in one page, sometimes probably 10s of times. I don't want the function to be called multiple times:
this.getOptions();
Is there a way to implement the component somehow so no matter how many times it is used in a page, the ajax call will only execute once?
Thanks in advance.
Update: I assume I can download the data in parent then pass it as prop if the component is going to be used multiple times, something like the following, but this defies the purpose of a component.
props: {
optionsPassedByParents: Array
},
mounted() {
if(this.optionsPassedByParents.length == 0)
this.getOptions();
else
this.options = this.optionsPassedByParents;
},
CodePudding user response:
Place the contents of getOptions()
in your App.vue
's mounted()
. Provide the returned data to your component through any of these:
- a state management plugin (vue team's recommendation: pinia)
- props
- provide/inject
- a reactive object (
export const store = reactive({/* data here */})
) placed in its own file, imported (e.g:import { store } from 'path/to/store'
) in bothApp.vue
(which would populate it when request returns) and multiselect component, which would read from it.
If you don't want to request the data unless Multiselect component is rendered, you should definitely use a state management plugin:
- multiselect calls an action on the store, requesting the data
- the action only makes the request if the data is not present on the store's state
- additionally, the action might have a
forceFetch
param which allows re-fetching (even when the data is present in state)
Note: you don't need a state management plugin to do all this, you can write your own bespoke functions. But most developers use pinia
or vuex
as they have been developed specifically for this purpose and have all the bells and whistles you might want, as a developer (time-travel, intuitive debugging, replaying/undoing actions/mutations, state persistence via localstorage
, etc...)
If you want to avoid using a state management plugin, you should note a reactive({})
can also hold "methods/actions". Proof of concept:
const store = reactive({
users: [],
fetchUsers: (forceFetch = false) =>
new Promise((resolve, reject) => {
if (forceFetch || !store.users.length) {
axios
.get("/users")
.then(({ data }) => {
store.users = data
resolve()
})
.catch(reject)
} else {
resolve()
}
})
})
I definitely don't recommend this type of stores. Such "simple" stores have the tendency to get complicated fast and typically end up being harder to manage and extend upon than a typical pinia
store. Probably their biggest problem is they become increasingly harder to debug as they grow.
I strongly suggest pinia
for this purpose.