I've been following this guide to making a global loader component but there's an issue with setting up the interceptors with the config.
Error: TypeError: Cannot read properties of undefined (reading 'config')
I'm using vue 3.2.2, vuex 4.0.2, axios 0.21, and Laravel 8
The loader component is not working and I suspect it might be due to the config error.
app.js
<script>
import { createApp } from 'vue'
import { createStore } from 'vuex'
import router from './router';
import App from './components/App';
import { loader } from "./loader";
const store = createStore({
state() {
},
modules: {
loader,
}
})
const app = createApp(App)
app.use(router)
app.use(store)
app.mount('#app')
</script>
App.vue
<script>
import Sidebar from "../components/Sidebar";
import {mapState} from "vuex/dist/vuex.mjs";
import Loader from "./Loader";
import axios from "axios";
axios.defaults.showLoader = true;
export default {
components: {
Sidebar,
Loader
},
computed: {
...mapState('loader', ['loading'])
},
created() {
axios.interceptors.request.use(
config => {
if (config.showLoader) {
store.dispatch('loader/pending');
}
return config;
},
error => {
if (error.config.showLoader) {
store.dispatch('loader/done');
}
return Promise.reject(error);
}
);
axios.interceptors.response.use(
response => {
if (response.config.showLoader) {
store.dispatch('loader/done');
}
return response;
},
error => {
let response = error.response;
if (response.config.showLoader) {
store.dispatch('loader/done');
}
return Promise.reject(error);
}
)
}
}
</script>
Your interceptor arrow function having issues with config param
axios.interceptors.request.use((config) => {
if (config.showLoader) {
store.dispatch('loader/pending');
}
return config;
}, (error) => {
if (error.config.showLoader) {
store.dispatch('loader/done');
}
return Promise.reject(error);
});
axios.interceptors.response.use((response) => {
if (response.config.showLoader) {
store.dispatch('loader/done');
}
return response;
}, (error) => {
let response = error.response;
if (response.config.showLoader) {
store.dispatch('loader/done');
}
return Promise.reject(error);
})
Related
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
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?
In the quasar project, I have a Vuex function "asyncValidateToken" that checks whether the user is logged in to the system. It is located in the file "src/store/index.js". The file contains the following code:
import Vue from 'vue'
import Vuex from 'vuex'
import { api } from 'boot/axios'
Vue.use(Vuex)
export default function (/* { ssrContext } */) {
const Store = new Vuex.Store({
state: {
isLogin: false
},
mutations: {
changeIsLogin (state, payload) {
state.isLogin = payload;
}
},
actions: {
asyncValidateToken: async (context, payload) => {
await api.post('/accounts/token', '', {
headers: {
'Authorization': `Bearer ${localStorage.token}`,
}
})
.then(response => {
if (response.data == localStorage.userId) {
context.commit('changeIsLogin', true);
return true;
} else {
context.commit('changeIsLogin', false);
return false;
}
})
.catch(error => {
context.commit('changeIsLogin', false);
return false;
});
}
}
})
return Store
}
The page "Results.vue" where the route protection is used via the function "beforeRouteEnter"
<template>
<q-page class="flex flex-center">
<div>
<charts />
<feedback />
</div>
</q-page>
</template>
<script>
import Charts from 'src/components/Charts.vue'
import Feedback from 'src/components/Feedback.vue'
import store from 'src/store/index.js'
export default {
name: 'Results',
components: {
Charts,
Feedback
},
beforeRouteEnter (to, fromR, next) {
if (store.dispatch('asyncValidateToken')) {
next();
} else { this.$router.push('/login'); }
}
}
</script>
I get an error "src_store_index_js__WEBPACK_IMPORTED_MODULE_2__.default.dispatch is not a function
at beforeRouteEnter (Results.vue?82a0:23)
at routeEnterGuard (vue-router.esm.js?85f8:2333)". The construction "this.$store.dispatch('asyncValidateToken')" also does not work. Why?
Try
store().dispatch('')
Why?
Because your store.js module is exporting a function as default, and it returns the store.
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
I am just starting with Redux and External APIs. For learning, I wanted to consume API from NASA (https://api.nasa.gov/). I don't know what I do wrong. I can't render anything on screen. I get "Uncaught TypeError: Cannot read property '1' of undefined" in the console.
I've read several Q&A in stackoverflow... But I didn't find out where is the problem in my code.
I'd really appreciate any opinion. I need a clue... Thanks in advance.
CONTAINER
import React from 'react';
import { Component } from 'react';
import { connect } from 'react-redux'
import { showTutusers } from '../actions/index';
import { bindActionCreators } from "redux";
class TutuserListContainer extends Component {
componentWillMount() {
this.props.showTutusers()
}
render() {
return (
<div>
<h3> { this.props.tutusers.photos[0].id}</h3><br />
<h3> { this.props.tutusers.photos[1].id}</h3><br />
</div>
);
}
}
function mapStateToProps(state) {
return {
tutusers: state.tutuser.tutusers
}
}
export default connect(mapStateToProps, { showTutusers })(TutuserListContainer)
REDUCERS - INDEX
import {combineReducers} from 'redux';
import { showTutusers } from './tutusers'
const allReducers = combineReducers({
tutuser: showTutusers,
});
export default allReducers
REDUCER
import { FETCH_TUTUSERS_START, FETCH_TUTUSERS_ERROR, RECEIVED_TUTUSERS } from '../actions/action-types';
const initialState = {
fetching: false,
fetched: false,
tutusers: [],
error: null
}
export function showTutusers(state = initialState, action) {
switch (action.type) {
case FETCH_TUTUSERS_START: {
return {...state, fetching: true}
break;
}
case FETCH_TUTUSERS_ERROR: {
return {...state, fetching: false, error: action.payload}
break;
}
case RECEIVED_TUTUSERS: {
return {...state, fetching: false, fetched: true, tutusers: action.payload}
break;
}
}
return state
}
ACTION-TYPES
export const SHOW_TUTUSERS = 'SHOW_TUTUSERS';
export const FETCH_TUTUSERS_START = 'FETCH_TUTUSERS_START';
export const FETCH_TUTUSERS_ERROR = 'FETCH_TUTUSERS_ERROR';
export const RECEIVED_TUTUSERS = ' RECEIVED_TUTUSERS';
ACTIONS - INDEX
import * as types from '../actions/action-types';
import axios from 'axios';
import store from '../stores/store';
export function showTutusers() {
return (dispatch, getState) => {
store.dispatch( { type: types.FETCH_TUTUSERS_START} )
axios.get('https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?sol=1000&api_key=_____MY_KEY____')
.then((response) => {
store.dispatch( { type: types.RECEIVED_TUTUSERS, payload: response.data } )
// console.log(">>> response.data", response.data)
})
.catch((err) => {
dispatch({type: "FETCH_TUTUSERS_ERROR", payload: err})
})
}
}
STORE
import { createStore, applyMiddleware, compose } from 'redux';
import allReducers from '../reducers';
import thunk from 'redux-thunk';
// import promise from 'redux-promise';
import createLogger from 'redux-logger';
import promise from 'redux-promise-middleware';
const middleware = applyMiddleware(thunk, promise(), createLogger());
const store = createStore(
allReducers,
compose(middleware, window.devToolsExtension ? window.devToolsExtension() : f => f)
);
export default store;
You need to wait until the response is received and only then try to access the photos, something like this should help:
<div>
{this.props.tutusers.fetched && <div>
<h3> { this.props.tutusers.photos[0].id}</h3><br />
<h3> { this.props.tutusers.photos[1].id}</h3><br />
</div>}
</div>
You also need to pass all the data about the request:
function mapStateToProps(state) {
return {
tutusers: state.tutuser
}
}
I got a solution in this stackoverflow question: react redux-thunk component doesn't render this.props
Basically I only needed an if else statement:
render() {
var component;
if (this.props.tutusers) {
component = this.functionToRenderTutusers()
} else {
component = <h3>Loading...</h3>;
}
return (
<div>
{component}
</div>
);
};
functionToRenderTutusers() {
return this.props.tutusers.photos.map((tutuser) => {
return (
<div>{tutuser.id}</div>
)
})
}