Pinia action not updating state - vue.js

When I call the login action in the login component I get the err Cannot set properties of undefined (setting 'user'). Why is user undefined when its defined in the state? Below is my Pinia store code
import {defineStore} from "pinia";
import axios from "axios";
import router from "#/router";
import {createToaster} from "#meforma/vue-toaster";
const toaster = createToaster({ position: "top-right"});
export const useStore = defineStore("main", {
state: () => ({
user: {},
token: null
}),
actions: {
login (email, password) {
axios.post('http://127.0.0.1:8000/api/login', {
email: email,
password: password
})
.then(function (response) {
const user = response.data.data.user
const token = response.data.data.access_token
// update pinia state
this.user = user
this.token = token
// store user details and jwt in local storage to keep user logged in between page refreshes
localStorage.setItem('user', JSON.stringify(user))
localStorage.setItem('token', token)
toaster.success(response.data.message)
// redirect to previous url or default to home page
router.push('/dashboard/home')
})
.catch(function (error) {
console.log(error)
//toaster.error(error.response.data.message)
})
}
}
})

Related

how to get a user by id with a get request to express using react native?

I am trying to render One users info for the profile page in react native using express and axios.
This is the controller responsible for getting a user.
// GET ONE USER BY ID
module.exports.findOneSingleUser = (req, res) => {
User.findOne({ _id: req.params.id })
.then(oneSingleUser => res.json({ user: oneSingleUser }))
.catch(err => res.json({ message: 'Something went wrong', error: err }));
}
this is the code im using to make the axios request to the server and I am able to get all the users in the DB but I want to be able to render one user by id or the token that is stored for login and for login to persist which is working.
const ProfileInfo = (props) => {
const { navigation, route } = props
const authCtx = useContext(AuthContext);
const token= authCtx.token
const [userInfo, setUserInfo] = useState([]);
useEffect(() => {
axios.get(`http://localhost:3000/api/users/`)
.then(res => console.log(res.data))
.catch(err => console.log(err))
}, [])
this is the code in util folder that gets the token from the backend
import axios from 'axios'
import { useNavigation } from '#react-navigation/native';
const BASE_URL = 'http://localhost:3000'
// ! LOGIN FUNCTION
export async function authenticate(email,password ){
const token = await axios.post(BASE_URL + '/api/login',
{
email: email,
password: password,
},{ withCredentials: true }
);
return token;
}
// ! REGISTER NEW USER FUNCTION
export async function createUser(email, password) {
const token = await axios.post(BASE_URL + '/api/register',
{
email: email,
password: password,
},{ withCredentials: true }
);
return token;
}
this is the screen where I have the profile info component being used
import React,{useEffect} from 'react'
import ProfileInfo from '../components/Profile/ProfileInfo';
import Statistics from '../components/Profile/Statistics';
const ProfileScreen = (props) => {
const {navigation} = props
return (
<>
<ProfileInfo navigation={navigation}/>
<Statistics />
</>
)
}
export default ProfileScreen
How do or What do I need to pass into the url of the axios request to get the data for the user that is logged in? thanks in advance.
when I change the server side to
User.findOne({ token: req.params.token})
&
useEffect(() => {
axios.get(`http://localhost:3000/api/users/${token}`)
.then(res => console.log(res.data))
.catch(err => console.log(err))
}, [])
I get a user but it is only the first user in DB not the user that is logged in... not sure how to get the one user that is logged in.

Catch(error) on dispatched method in store not working in Vue 3

I am working on login of a vue 3 app, both the login and registration work fine, but i still need to throw send back a meaningful response to user if login in credentials are rejected by the back-end, i have tried every possible means to log the rejection response from server to console but to no avail, the login is fine when credential is correct, but the console just stay mute when incorrect credential is entered
this is my login.vue
import store from "../store"
import { useRouter } from "vue-router";
import { ref } from "vue";
const router = useRouter()
const user = { email: '', password: '', remember : false }
let errorMsg = ref('');
async function login(ev) {
ev.preventDefault();
await store.dispatch('login', user)
.then(()=> {
router.push({
name: 'Dashboard'
})
})
.catch((err) => {
errorMsg = err.response.data.error
console.log(err)
})
}
and this is my vuex store
import {createStore} from 'vuex'
import axiosClient from "../axios";
const store = createStore({
state: {
user: {
data: {},
token: sessionStorage.getItem('TOKEN')
}
},
getters: {},
setters: {},
actions: {
register({commit}, user) {
return axiosClient.post('/register', user)
.then(({data}) => {
commit('setUser', data);
return data;
})
},
login({commit}, user) {
return axiosClient.post('/login', user)
.then(({data}) => {
commit('setUser', data);
return data;
})
},
},
mutations: {
logout: state => {
state.user.data = {};
state.user.token = null;
},
setUser: (state, userData)=> {
state.user.token = userData.token;
state.user.data = userData.user;
sessionStorage.setItem('TOKEN', userData.token)
}
},
modules: {}
})
export default store;
And here is my axios js file
import axios from "axios";
import store from "./store";
const axiosClient = axios.create({
baseURL: 'http://localhost:8000/api'
})
axiosClient.interceptors.request.use(config=> {
config.headers.Authorization = `Bearer ${store.state.user.token}`
return config;
})
export default axiosClient;
Response from backend as seen from Network Tab
{"error":"The provided credentials are incorrect","0":422}
After checking through my controller in my Laravel project, I discovered that I did not set the status code for the response properly.
Incorrect code:
if (!Auth::attempt($credentials, $remember)) {
return response([
'error'=> 'The provided credentials are incorrect',
422
]);
}
Corrected code:
if (!Auth::attempt($credentials, $remember)) {
return response([
'error'=> 'The provided credentials are incorrect',
], 422);
}
Axios does not treat the response received as a rejection; which needs to get its catch triggered.
Therefore my console.log that I had in my try/catch does not run at all.
I'm very happy we got this solved, big thanks to every one.

Vuejs 3 Router Cannot read property 'push' of undefined

I am currently trying to start a Vue app which will contain a user login.
For some reason the I have been struggling with getting this router redirect to work.
The implementation is straight from the vueschool page and specifically aimed at the composition API.
What am I missing here?
Every time I run the registration script it registers the user correctly and logs the error that it can't find the 'push' property on of undefined.
My code completion is telling me it's there, the linting works fine and the IDE Webstorm isn't giving any errors.
<script>
import firebase from "firebase/app";
import "firebase/auth";
import { defineComponent, ref } from "vue";
import { useRouter } from "vue-router";
export default defineComponent({
name: "Register",
setup() {
const form = ref({
email: "",
password: "",
error: "",
});
const pressed = () => {
const user = firebase
.auth()
.createUserWithEmailAndPassword(form.value.email, form.value.password);
user
.then((user) => {
console.log(user);
const router = useRouter();
router.push("/about");
})
.catch((e) => console.log(e.message));
};
return {
form,
pressed,
};
},
});
</script>
Hope it is just something simpel
const router = useRouter(); must be declared in the scope of the setup hook not inside any inner scope :
setup(){
const router = useRouter();
const form = ref({
email: "",
password: "",
error: "",
});
const pressed = () => {
const user = firebase
.auth()
.createUserWithEmailAndPassword(form.value.email, form.value.password);
user
.then((user) => {
console.log(user);
router.push("/about");
})
.catch((e) => console.log(e.message));
};
return {
form,
pressed,
};
},

Get user on initial page load - Nuxt middleware

I'm using Firebase Auth in my Nuxt app and having some trouble with protected routes. I have the nuxt middleware enabled that handles redirection based on whether or not a user a logged in on page navigation.
My issue is, if you visit a protected route from the address bar and you were previously logged out, you'll be given access to the route. However if you try to visit another protected route from that route you'll be redirected. I am using Vuex to handle the user logged in state.
How can I get the middleware to kick in on a 'cold start' for a protected route.
Middleware: router-auth.js
export default function({ store, redirect, route }) {
const user = JSON.parse(store.state.Authentication.user)
console.log(user);
user !== null && route.name === '/' ? redirect('/dashboard') : ''
//user === null && route.name !== '/' ? redirect('/dashboard') : ''
}
Plugin: auth.js
import firebase from "firebase/app";
import "firebase/auth";
export default function ({store, router}) {
firebase.auth().onAuthStateChanged((user) => {
if(user){
store.dispatch('Authentication/SET_USER',JSON.stringify(user))
}else{
store.dispatch('Authentication/SET_USER', null)
}
})
}
Vuex: Authentication.js
export const state = () => ({
user: ''
});
export const getters = {
user: state => {
return state.user;
},
}
export const mutations = {
SET_USER: (state, payload) => {
state.user = payload;
},
}
export const actions = {
SET_USER({ commit }, payload) {
commit('SET_USER', payload)
},
}
You can return an error func call in middleware:
export default function({ store, redirect, route, error }) {
const accessDenied = true // ... your access route logic goes here
if (accessDenied) {
return error({
message: 'access denied',
statusCode: 403
})
}
}

How can I test actions within a Vuex module?

I want to test a vuex module called user.
Initially, I successfully registered my module to Vuex. Its works as expected.
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
user
}
})
export default store
My user module is defined as follows
store/modules/user.js
const state = {
token: getToken() || '',
}
export const getters = {
token: state => state.token,
}
const mutations = {
[SET_TOKEN]: (state, token) => {
state.token = token
}
}
const actions = {
[LOGIN] ({ commit }, body) {
return new Promise((resolve, reject) => {
login(body).then(response => { //login is an api method, I'm using axios to call it.
const { token } = response.data
setToken(token)
commit(SET_TOKEN, token)
resolve()
}).catch(error => {
reject(error)
})
})
}
}
export default {
state,
getters,
mutations,
actions
}
login api
api/auth.js
import request from '#/utils/request'
export function login (data) {
return request({
url: '/auth/login',
method: 'post',
data
})
}
axios request file
utils/request
import axios from 'axios'
import store from '#/store'
import { getToken } from '#/utils/auth'
const request = axios.create({
baseURL: process.env.VUE_APP_BASE_API_URL,
timeout: 5000
})
request.interceptors.request.use(
config => {
const token = getToken()
if (token) {
config.headers['Authentication'] = token
}
return config
}
)
export default request
When I want to write some test (using Jest), for example login action as shown above.
// user.spec.js
import { createLocalVue } from '#vue/test-utils'
import Vuex from 'vuex'
import actions from '#/store/modules/user'
const localVue = createLocalVue()
localVue.use(Vuex)
test('huhu', () => {
expect(true).toBe(true)
// implementation..
})
How can I write test for my Login action? Thanks. Sorry for my beginner question.
EDIT: SOLVED Thank you Raynhour for showing to me right direction :)
import { LOGIN } from '#/store/action.types'
import { SET_TOKEN } from '#/store/mutation.types'
import { actions } from '#/store/modules/user'
import flushPromises from 'flush-promises'
jest.mock('#/router')
jest.mock('#/api/auth.js', () => {
return {
login: jest.fn().mockResolvedValue({ data: { token: 'token' } })
}
})
describe('actions', () => {
test('login olduktan sonra tokeni başarıyla attı mı?', async () => {
const context = {
commit: jest.fn()
}
const body = {
login: 'login',
password: 'password'
}
actions[LOGIN](context, body)
await flushPromises()
expect(context.commit).toHaveBeenCalledWith(SET_TOKEN, 'token')
})
})
Store it's just a javascript file that will export an object. Not need to use vue test util.
import actions from '../actions'
import flushPromises from 'flush-promises'
jest.mock('../api/auth.js', () => {
return {
login: jest.fn()..mockResolvedValue('token')
}; // mocking API.
describe('actions', () => {
test('login should set token', async () => {
const context = {
commit: jest.fn()
}
const body = {
login: 'login',
password: 'password'
}
actions.login(context, body)
await flushPromises() // Flush all pending resolved promise handlers
expect(context.commit).toHaveBeenCalledWith('set_token', 'token')
})
})
but you need to remember that in unit tests all asynchronous requests must be mocked(with jest.mock or something else)