I am new to vue and working with vue 3 and the composition API. It is really hard to find stuff related to the composition API, so I try to get help here. I am using axios in combination with vuex modules for consuming APIs.
How can I transfere this code from options API to composition API?
TestApi.js
import axios from 'axios'
const posts = {
namespaced: true,
state: {
posts: []
},
mutations: {
SET_POSTS(state, data){
state.posts = data
}
},
actions: {
loadPosts({commit}) {
axios
.get('https://jsonplaceholder.typicode.com/posts')
.then( res => {
commit('SET_POSTS', res.data)
} )
.catch(error => console.log(error))
}
},
getters: {
getPosts(state){
return state.posts
}
}
}
export default posts
App.vue
<template>
<div class="post" v-for="post in getPosts" :key="post.id">
<h1>{{ post.title }}</h1>
<p>{{ post.body }}</p>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { computed } from 'vue'
export default {
computed: {
...mapGetters('posts', ['getPosts'])
},
created(){
this.$store.dispatch('posts/loadPosts')
}
}
</script>
<style>
.post{
margin-bottom: 20px;
}
</style>
I tried something like this. But it does not work:
import { useStore } from 'vuex'
import { computed } from 'vue'
export default {
setup(){
getData();
const store = useStore()
function getData(){
store.dispatch('posts/loadPosts');
}
const getPosts = computed(() => store.getters['getPosts'])
return{ getPosts }
}
}
Error: Uncaught (in promise) TypeError: Cannot read property 'dispatch' of undefined
You shoul run the function after initializing the store :
setup(){
const store = useStore()
getData();
...
Related
I am a bit confused with composition API and fetching data. When I open the page, I can see rendered list of categories, but if I want to use categories in setup(), it is undefined. How can I use categories value inside setup function? You can see that I want to console log categories.
Category.vue
<template>
<div class="page-container">
<item
v-for="(category, index) in categories"
:key="index"
:item="category"
:is-selected="selectedItem === index"
#click="selectItem(index)"
/>
</div>
</template>
<script>
import { computed, ref } from 'vue'
import { useStore } from 'vuex'
import Item from '#/components/Item.vue'
export default {
components: {
Item
},
setup () {
const store = useStore()
store.dispatch('categories/getCategories')
const categories = computed(() => store.getters['categories/getCategories'])
const selectedItem = ref(1)
const selectItem = (index) => {
selectedItem.value = index
}
console.log(categories.value[selectedItem.value].id)
return {
categories,
selectedItem,
selectItem
}
}
}
</script>
<style lang="scss" scoped>
#import '#/assets/scss/general.scss';
</style>
categories.js - vuex module
import axios from 'axios'
import { API_URL } from '#/helpers/helpers'
export const categories = {
namespaced: true,
state: {
categories: []
},
getters: {
getCategories: (state) => state.categories
},
mutations: {
UPDATE_CATEGORIES: (state, newValue) => { state.categories = newValue }
},
actions: {
async getCategories ({ commit }) {
await axios.get(`${API_URL}/getCategories.php`).then(response => {
commit('UPDATE_CATEGORIES', response.data.res_data.categories)
})
}
},
modules: {
}
}
In the setup function you cannot process a computed function.
You can instead access store.getters['categories/getCategories'].value[selectedItem.value].id if you want to process that in the setup function.
I know this seems like a question that would be easy to find, but my code worked some time ago. I am using a Vuex binding to check if my sidebar should be visible or not, so stateSidebar should be set within my entire project.
default.vue
<template>
<div>
<TopNav />
<SidebarAuth v-if="stateSidebar" />
<Nuxt />
</div>
</template>
<script>
import TopNav from './partials/TopNav';
import SidebarAuth from './partials/SidebarAuth';
export default {
components: {
TopNav,
SidebarAuth
},
methods: {
setStateSidebar(event, state) {
this.$store.dispatch('sidebar/setState', state)
}
}
}
</script>
store/sidebar.js
export const state = () => ({
stateSidebar: false
});
export const getters = {
stateSidebar(state) {
return state.stateSidebar;
}
};
export const mutations = {
SET_SIDEBAR_STATE(state, stateSidebar) {
state.stateSidebar = stateSidebar;
}
};
export const actions = {
setState({ commit }, stateSidebar) {
commit('SET_SIDEBAR_STATE', stateSidebar);
},
clearState({ commit }) {
commit('SET_SIDEBAR_STATE', false);
}
};
plugins/mixins/sidebar.js
import Vue from 'vue';
import { mapGetters } from 'vuex';
const Sidebar = {
install(Vue, options) {
Vue.mixin({
computed: {
...mapGetters({
stateSidebar: 'sidebar/stateSidebar'
})
}
})
}
}
Vue.use(Sidebar);
nuxt.config.js
plugins: ["./plugins/mixins/validation", "./plugins/axios", "./plugins/mixins/sidebar"],
If you're creating a mixin, it should be in /mixins
So for example /mixins/my-mixin.js.
export default {
// vuex mixin
}
Then import it like this in default.vue
<script>
import myMixin from '~/mixins/my-mixin`
export default {
mixins: [myMixin],
}
This is not what plugins should be used for tho. And IMO, you should definitively make something simpler and shorter here, with less boilerplate and that will not be deprecated in vue3 (mixins).
This is IMO the recommended way of using it
<template>
<div>
<TopNav />
<SidebarAuth v-if="stateSidebar" />
<Nuxt />
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState('sidebar', ['stateSidebar']) // no need to use object syntax nor a getter since you're just fetching the state here
},
}
</script>
No mixin, no plugin entry.
I'm trying to render a d3 graph using stored data in vuex. but I'm not getting data in renderGraph() function.
how to get data in renderGraph()?
Following is store methods.
store/index.js
import Vue from "vue";
import Vuex from "vuex";
import * as d3 from "d3";
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
subscribers: []
},
getters: {
getterSubscribers: state => {
return state.subscribers;
}
},
mutations: {
mutationSubscribersData: (state, payload) => {
state.subscribers = payload;
}
},
actions: {
actionSubscribersData: async ({ commit }) => {
let subsData = await d3.json("./data/subscribers.json"); // some json fetching
commit("mutationSubscribersData", subsData);
}
}
});
Below is parent component
Home.vue
<template>
<div>
<MyGraph /> // child component rendering
</div>
</template>
<script>
import MyGraph from "./MyGraph.vue";
export default {
components: {
MyGraph
},
};
</script>
Below is child component.
MyGraph.vue
<template>
<div>
<svg width="500" height="400" />
</div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
computed: {
...mapGetters(["getterSubscribers"])
},
methods: {
...mapActions(["actionSubscribersData"]),
renderGraph(data) {
console.log(data); // DATA NOT COMING HERE
// MyGraph TO BE RENDERED WITH THIS DATA
}
},
mounted() {
this.actionSubscribersData();
this.renderGraph(this.getterSubscribers);
}
};
</script>
I have tried mounted, created lifecycle hooks. but did not find data coming.
There is race condition. actionSubscribersData is async and returns a promise. It should be waited for until data becomes available:
async mounted() {
await this.actionSubscribersData();
this.renderGraph(this.getterSubscribers);
}
There must be delay for the actionSubscribersData action to set value to store. Better you make the action async or watch the getter. Watching the getter value can be done as follows
watch:{
getterSubscribers:{ // watch the data to set
deep:true,
handler:function(value){
if(value){ // once the data is set trigger the function
this.renderGraph(value);
}
}
}
}
Why it doesn't display the json coming from jsonplaceholder?
Did I miss something here? This is just my first time using Vuex.
By the way, I separated the files so that I can debug it easily and I thought it's a good practice for me because I'm planning to implement vuex in a bigger project.
Here is my index.js:
import Vue from 'vue';
import Vuex from 'vuex';
import articles from './modules/articles';
//Load Vuex
Vue.use(Vuex);
//Create store
export default new Vuex.Store({
modules: {
articles
}
})
Here is my articles.js .
import axios from 'axios';
//state
const state = {
articles: []
};
//actions
const actions = {
loadArticles({ commit }) {
axios.get('https://jsonplaceholder.typicode.com/todos')
.then(response => response.data)
.then(articles => {
commit('displayArticles', articles,
console.log(articles))
})
}
};
//mutations
const mutations = {
displayArticles(state, articles) {
state.articles = articles;
}
};
//export
export default {
state,
getters,
actions,
mutations
};
and lastly my home.vue that will display the data from the vuex:
<template>
<section>
<h1>HI</h1>
<h1 v-for="article in articles" :key="article.id">{{article.id}}</h1>
</section>
</template>
<script>
import { mapState } from "vuex";
export default {
mounted() {
this.$store.dispatch("displayArticles");
},
computed: mapState(["articles"])
};
</script>
you have to dispatch the action, so in your .vue file you have to write :
mounted() {
this.$store.dispatch("loadArticles");
},
and to get the list of articles in your component you should use getter in your Store:
const getters = {
getArticles: state => {
return state.articles;
}
and the computed will be like this:
computed:{
getArticlesFromStore(){
return this.$store.getters.getArticles;
}
}
and then you call the computed leement in your HTML:
<h1 v-for="article in getArticlesFromStore" :key="article.id">{{article.id}}</h1>
You are trying to dispatch mutation. You need to use commit with mutations or move your displayArticles to actions. I imagine you meant to dispatch loadArticles?
I'm trying Quasar for the first time and trying to use the Vuex with modules but I can't access the $store property nor with ...mapState. I get the following error 'Cannot read property 'logbook' of undefined' even though I can see that the promise logbook exists on Vue Devtools. Print from Devtools
Here is my store\index.js
import Vue from 'vue';
import Vuex from 'vuex';
import logbook from './logbook';
Vue.use(Vuex);
export default function (/* { ssrContext } */) {
const Store = new Vuex.Store({
modules: {
logbook,
},
strict: process.env.DEV,
});
return Store;
}
Here is the component
<template>
<div>
<div>
<h3>RFID</h3>
<q-btn #click="logData"
label="Save"
class="q-mt-md"
color="teal"
></q-btn>
<q-table
title="Logbook"
:data="data"
:columns="columns"
row-key="uid"
/>
</div>
</div>
</template>
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
export default {
name: 'RFID',
mounted() {
this.getLogbookData();
},
methods: {
...mapActions('logbook', ['getLogbookData']),
...mapGetters('logbook', ['loadedLogbook']),
...mapState('logbook', ['logbookData']),
logData: () => {
console.log(this.loadedLogbook);
},
},
data() {
return {
};
},
};
</script>
<style scoped>
</style>
Here is the state.js
export default {
logbookData: [],
};
Error that I get on the console
Update: Solved the problem by refactoring the way I declared the function. I changed from:
logData: () => { console.log(this.loadedLogbook); }
to
logData () { console.log(this.loadedLogbook); }
Check the .quasar/app.js file. Is there a line similar to import createStore from 'app/src/store/index', and the store is later exported with the app in that same file?
I think you confused all the mapx functions.
...mapState and ...mapGetters provide computed properties and should be handled like this
export default {
name: 'RFID',
data() {
return {
};
},
mounted() {
this.getLogbookData();
},
computed: {
...mapGetters('logbook', ['loadedLogbook']),
...mapState('logbook', ['logbookData']),
}
methods: {
...mapActions('logbook', ['getLogbookData']),
logData: () => {
console.log(this.loadedLogbook);
},
}
};