How to use Pinia with Nuxt, composition-api (vue2) and SSR? - vuejs2

I'm trying to get Pinia to work in Nuxt with SSR (server-side rendering).
When creating a page without Pinia, it works:
<script>
import { reactive, useFetch, useContext } from '#nuxtjs/composition-api'
export default {
setup() {
const { $axios } = useContext()
const invitesStore = reactive({
invites: [],
loading: true,
})
useFetch(async () => {
invitesStore.loading = true
await $axios.$get('invite/registermember').then((result) => {
invitesStore.loading = false
invitesStore.invites = result.invites
})
})
return {
invitesStore,
}
},
}
</script>
But when introducing Pinia, I get the error "Converting circular structure to JSON --> starting at object with constructor 'VueRouter'"
I'm using Pinia this way:
// /store/invitesStore.js
import { defineStore } from 'pinia'
// useStore could be anything like useUser, useCart
export const useInvitesStore = defineStore({
// unique id of the store across your application
id: 'storeId',
state() {
return {
invites: [],
loading: true,
}
},
})
<script>
import { useInvitesStore } from '#/store/invitesStore'
import { reactive, onMounted, useFetch, useContext } from '#nuxtjs/composition-api'
export default {
setup() {
const { $axios } = useContext()
const invitesStore = useInvitesStore()
useFetch(async () => {
invitesStore.loading = true
await $axios.$get('invite/registermember').then((result) => {
invitesStore.loading = false
invitesStore.invites = result.invites
})
})
return {
invitesStore,
}
},
}
</script>
Is it possible to get this to work? How?

Related

vuex unknown mutation type: setPatient

I'm using vue 3 with composition api and vuex 4, I've done it this way before but now its throwing that error.
Here's my store/index.js
import { createStore } from "vuex";
export const store = new createStore({
state: {
patient: [],
},
mutations: {
setPatient(state, payload) {
state.patient = payload;
},
},
getters: {
getPatient(state) {
return state.patient;
},
getAppointment(state) {
return state.patient.appointments;
},
},
})
app.js
require('./bootstrap');
import { createApp, h } from 'vue';
import { createInertiaApp } from '#inertiajs/inertia-vue3';
import { InertiaProgress } from '#inertiajs/progress';
import {store} from './Store'
const { RayPlugin } = require('vue-ray');
window.$ = window.jQuery = require("jquery");
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => require(`./Pages/${name}.vue`),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.use(store)
.use(RayPlugin, { interceptErrors: true, host: '127.0.0.1', port: 23517 })
.mixin({ methods: { route } })
.mount(el);
},
});
InertiaProgress.init({ color: '#4B5563' });
And following the documentation, on my component I did the following:
import { useStore } from 'vuex'
import {onMounted, reactive, ref} from "vue";
export default {
props: {
patient: {
type: Object,
required: true
}
},
setup(props) {
const store = useStore();
onMounted(() => {
store.commit('setPatient', props.patient);
})
}
}
So far I've done this before, but using the composition api is new for me, so I couldn't find where the error is

API Call is not updating refs in vue composables

I am trying to make an api call with a vue composable.
This is the composable
import { isRef, ref, unref, watchEffect } from "vue";
import axios from "axios";
export function getData(url){
const data = ref(null)
const error = ref(null)
function doAxios(){
data.value = null
error.value = null
axios.get(unref(url))
.then(res => {
data.value = res.data
})
.catch(err => {
error.value = err
})
}
if(isRef(url)){
watchEffect(doAxios)
}else{
doAxios()
}
return { data, error }
}
However, when I import and try to use this composable in a component, the ref does not get updated even after the api call is successful
<script>
import { ref } from '#vue/reactivity'
import { computed, onMounted } from '#vue/runtime-core';
import { useStore } from 'vuex';
import { getData } from '#/composables/getData';
export default {
setup(props, context){
const store = useStore()
let pageConfig = ref({ title: 'Dashboard' })
let cards_data = ref([
{ name: 'Open orders', value: 16 },
{ name: 'Closed orders', value: 21 },
{ name: 'Businesses', value: 10 },
{ name: 'Funds', value: 2002 },
]);
const { data: config, error } = getData('config')
console.log(config.value)
onMounted(() => {
context.emit('pageConfig', pageConfig)
})
return{ cards_data, config, error }
}
}
</script>
The config still remains null even after...

vuex error with quasar $store.auth is undefined

I am trying to use vuex with Quasar. I have created an authentication module as below.
// src/store/auth/index.js
import { api } from 'boot/axios';
export default {
state: {
user: null,
},
getters: {
isAuthenticated: state => !!state.user,
StateUser: state => state.user,
},
mutations: {
setUser(state, username){
state.user = username
},
LogOut(state){
state.user = null
},
},
actions: {
LOGIN: ({ commit }, payload) => {
return new Promise((resolve, reject) => {
api
.post(`/api/login`, payload)
.then(({ data, status }) => {
if (status === 200) {
commit('setUser', data.refresh_token)
resolve(true);
}
})
.catch(error => {
reject(error);
});
});
},
}
}
I imported it in the store
// src/store/index.js
import { store } from 'quasar/wrappers'
import { createStore } from 'vuex'
import auth from './auth'
export default store(function (/* { ssrContext } */) {
const Store = createStore({
modules: {
auth:auth
},
// enable strict mode (adds overhead!)
// for dev mode and --debug builds only
strict: process.env.DEBUGGING
})
return Store
})
And I imported it into MainLayout to check if the user is logged in.
// src/layouts/MainLayout
<template>
</template>
<script>
import { ref, onMounted } from 'vue'
import packageInfo from '../../package.json'
import { useStore } from 'vuex'
export default {
name: 'MainLayout',
setup () {
const $store = useStore;
const connected = ref(false);
function checkLogin(){
//console.log($store)
return connected.value = $store.auth.isAuthenticated
};
onMounted(()=> {
checkLogin();
});
return {
appName: packageInfo.productName,
link:ref('dashboard'),
drawer: ref(false),
miniState: ref(true),
checkLogin,
}
}
}
</script>
But every time, I get the same error :
$store.auth is undefined
I tried to follow the quasar documentation, but I can't. Can anyone tell me what I am doing wrong please?
Thank you.
Someone helped me to find the solution. My error is to have written const $store = useStore instead of const $store = useStore(). Thanks

Vuex action not works with namespaced inside Nuxt fetch

I am building my own small project. When i try to access states from main store (index.js) inside of nuxt fetch method all works fine, but while i am trying to access from namespaced(store/photos.js) store it wont work. Here is my code.
store/index.js ( Works )
export const state = () => ({
fetchedData: []
})
export const mutations = {
setData: (state, data) => {
state.fetchedData = data;
}
}
export const actions = {
async get(vuexContext) {
const requestedData = await this.$axios.get("https://jsonplaceholder.typicode.com/users");
vuexContext.commit('setData', requestedData.data);
},
}
my Component:
<script>
import { mapState, mapActions } from 'vuex'
export default {
async fetch({ error,store })
{
try {
await store.dispatch('get');
} catch (error) {
console.log(error);
}
},
computed: {
...mapState(['fetchedData'])
}
};
</script>
store/photos.js ( Does not works )
export const state = () => ({
list: []
});
export const mutations = {
setPhotos(state, data) {
state.list = data;
}
};
export const actions = {
async getPhotos(vuexContext, context) {
const requestedData = await this.$axios.get(
"https://jsonplaceholder.typicode.com/photos"
);
vuexContext.commit("setPhotos", requestedData.data);
}
};
Same Component but modified
<script>
import { mapState, mapActions } from 'vuex'
export default {
async fetch({ error,store })
{
try {
await store.dispatch('photos/getPhotos');
} catch (error) {
console.log(error);
}
},
computed: {
...mapState({
list : 'photos/list'
})
}
};
</script>
Thanks in advance.
namespaced: true,
You can add this to your index.js file. Hope this will work.
Reference link:
Try this

How to use router in vue composition api?

I defined a route in vue:
/users/:userId
Which point to UserComponent:
<template>
<div>{{username}}</div>
</template>
and I use computed from #vue/composition-api to get the data.
the problem is when the route change to another userId, by navigate to another user, the user in the html template not changed as what I expected. also it doesn't do redirect when the the user is not in the list.
So what I can do to fix that?
here is my code:
<template>
<div>{{username}}</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref, getCurrentInstance } from '#vue/composition-api';
export const useUsername = ({ user }) => {
return { username: user.name };
};
export default defineComponent({
setup(props, { root }) {
const vm = getCurrentInstance();
const userToLoad = computed(() => root.$route.params.userId);
const listOfUsers = [
{ userId: 1, name: 'user1' },
{ userId: 2, name: 'user2' },
];
const user = listOfUsers.find((u) => u.userId === +userToLoad.value);
if (!user) {
return root.$router.push('/404');
}
const { username } = useUsername({ user });
return { username };
},
});
</script>
You can just do this:
import { useRoute } from 'vue-router';
export default {
setup() {
const route = useRoute();
// Now you can access params like:
console.log(route.params.id);
}
};
From the vue-router documentation:
import { useRouter, useRoute } from 'vue-router'
export default {
setup() {
const router = useRouter()
const route = useRoute()
function pushWithQuery(query) {
if (!user) {
router.push({
name: '404',
query: {
...route.query
}
})
}
}
}
}
You can pass the parameters as props to your components. Props are reactive by default.
This is how the route configuration could look like:
{
path: '/users/:userId',
name: Users,
component: YourComponent
},
You can then use the props in your component with watchEffect()
<template>
<div>{{username}}</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref, getCurrentInstance, watchEffect } from '#vue/composition-api';
export const useUsername = ({ user }) => {
return { username: user.name };
};
export default defineComponent({
props: {userId: {type: String, required: true },
setup(props, { root }) {
const vm = getCurrentInstance();
const user = ref()
const userToLoad = computed(() => props.userId);
const listOfUsers = [
{ userId: 1, name: 'user1' },
{ userId: 2, name: 'user2' },
];
watchEffect(() => user.value = listOfUsers.find((u) => u.userId === +userToLoad.value))
if (!user) {
return root.$router.push('/404');
}
const { username } = useUsername({ user });
return { username };
},
});
</script>
watchEffect() will run immediately when defined and when reactive dependencies.change
A had the same problem. I use vue 2 and #vue/composition-api
My resolution:
Created: src/router/migrateRouterVue3.js
import { reactive } from '#vue/composition-api';
import router from './index';
const currentRoute = reactive({
...router.currentRoute,
});
router.beforeEach((to, from, next) => {
Object.keys(to).forEach(key => {
currentRoute[key] = to[key];
});
next();
});
// eslint-disable-next-line import/prefer-default-export
export function useRoute() {
return currentRoute;
}
after that, I can usage:
// import { useRoute } from 'vue-router';
import { useRoute } from '#/router/migrateRouterVue3';
Resolution for you:
// replace:
// const userToLoad = computed(() => root.$route.params.userId);
// to:
import { useRoute } from '#/router/migrateRouterVue3';
//...
const route = useRoute();
const userToLoad = computed(() => route.params.userId);
function useRoute() {
const vm = getCurrentInstance()
if (!vm) throw new Error('must be called in setup')
return vm.proxy.$route
}
https://github.com/vuejs/composition-api/issues/630
The following useRoute hook will make route reactive so that it's doable:
const route = useRoute();
const fooId = computed(()=>route.params.fooId);
let currentRoute = null;
export const useRoute = () => {
const self = getCurrentInstance();
const router = self.proxy.$router;
if (!currentRoute) {
const route = { ...self.proxy.$route };
const routeRef = shallowRef(route);
const computedRoute = {};
for (const key of Object.keys(routeRef.value)) {
computedRoute[key] = computed(() => routeRef.value[key]);
}
router.afterEach((to) => {
routeRef.value = to;
});
currentRoute = reactive(computedRoute);
}
return currentRoute;
};
The vue2-helpers package provides a useRoute function you can use in Vue 2.7 (and 2.6, 2.5 also).
Installation
# Vue 2.7
$ npm install vue2-helpers#2
# Vue 2.5 and 2.6
$ npm install vue2-helpers#1
Usage
import { useRoute } from 'vue2-helpers/vue-router';
const route = useRoute();
const id: string | undefined = route.params.id;
const { proxy } = getCurrentInstance();
then use proxy to access $router or $route
Add please this code: watchEffect(() => userToLoad);