I'm new to React and I have this function.
import Axios from "axios";
const UserService = {
getUserRole: (access_token: string = "") => {
return Axios({
method: "get",
url: "https://<url>/user/role",
headers: {
"Authorization": `Bearer ${access_token}`
}
}).then((response) => {
return response.data;
}).catch((error) => {
console.log(error);
});
}
}
export default UserService
The getUserRole is used constantly by another component, for example
import UserService from "../../../services/authentication/userService";
import { useAuth } from "react-oidc-context";
...
const auth = useAuth();
UserService.getUserRole(auth.user?.access_token);
As you can see, I have to constantly pass the access_token from useAuth. Is there any way I can call useAuth inside my UserService so I don't have to constantly pass the access_token from my component?
The premise of the question is backward, as we shouldn't try to use hooks outside of React, but instead use outside code inside of React.
Quick solution: Custom hook
If the roles are used all over the place, a quick custom hook will get you started. This is the easiest way to wrap custom logic as hooks are meant to wrap stateful logic for reuse in components.
import { useState, useEffect } from "react";
import { useAuth } from "react-oidc-context";
import UserService from "../../../services/authentication/userService";
/**
* Custom hooks that fetches the roles for the logged in user.
*/
const useRoles = () => {
const auth = useAuth();
const [roles, setRoles] = useState();
useEffect(() => {
if (!user) return; // pre-condition
UserService
.getUserRole(auth.user.access_token)
.then(setRoles);
}, [auth.user]);
return roles;
}
Then in any component:
import useRoles from "../useRoles";
const MyExampleComponent = () => {
const roles = useRoles();
if (!roles) return <span>Please login (or something) to see the roles!</span>
return <div>{/* use roles here */}</div>
}
Better solution: Service provider
If there's a lot of different methods on the user service that needs to be used all over the app, then wrapping the whole service and providing a ready-to-use version through React's context would be best in my opinion.
But first, let's rework the UserService a little so that it uses a local axios instance instead of the global axios instance.
// I also made it a class, but it would also work with an object.
class UserService {
constructor(axios) {
this.axios = axios;
}
getUserRole(){
// use the local axios instance
return this.axios({
method: "get",
// Use the default URL from local axios instance
url: "user/role",
})
.then(({ data }) => data)
.catch(console.log),
}
getSomethingElse() {
// ...
}
}
Then, we can setup the React's context for the user service.
// UserServiceContext.js
import React from 'react';
import { useAuth } from "react-oidc-context";
import UserService from "../../../services/authentication/userService";
// Local axios instance
const axiosInstance = axios.create({
baseURL: 'https://<url>', // set the base URL once here
});
const userServiceInstance = new UserService(axiosInstance);
const UserServiceContext = React.createContext(userServiceInstance);
// Convenience hook
export const useUserService = () => useContext(UserServiceContext);
export const UserServiceProvider = (props) => {
const auth = useAuth();
useEffect(() => {
// If the user changes, update the token used by our local axios instance.
axiosInstance.defaults.headers
.common['Authorization'] = `Bearer ${auth.user?.access_token}`;
}, [auth.user]);
return (
<UserServiceContext.Provider value={userServiceInstance} {...props} />
);
}
Then anywhere, but commonly at the App's root:
import { AuthProvider } from "react-oidc-context";
import { UserServiceProvider } from "./UserServiceContext";
const App = () => (
<AuthProvider>
<UserServiceProvider>
<Content />
</UserServiceProvider>
</AuthProvider>
);
Now everything is ready to be used in any component!
import { useUserService } from '../UserServiceContext';
const MyExampleComponent = () => {
const userService = useUserService();
const [roles, setRoles] = useState();
// e.g. load roles once on mount.
useEffect(() => {
userService // use the service from the context
.getUserRole() // no auth token needed anymore!
.then(setRoles);
}, []);
if (!roles) return <span>Please login (or something) to see the roles!</span>
return <div>{/* use roles here */}</div>
}
Note that a custom hook could still be used to wrap the roles fetching logic. Both the context and hooks can be used together to wrap logic to each's own preferences.
// Here's what the hook could look like if it used the new provider above.
const useRoles = () => {
const userService = useUserService();
const [roles, setRoles] = useState();
// e.g. load roles once on mount.
useEffect(() => {
userService // use the service from the context
.getUserRole() // no auth token needed anymore!
.then(setRoles);
}, []);
return roles;
}
I consider the provider solution to be better since it provides more flexibility while keeping control over the exposed API.
In my solution, I suggest using the UserService instance as the provided value, but the provider could be changed to expose only parts of the API, or it could provide the roles and other data automatically. It's up to you!
Disclaimer: I've used minimal code to demonstrate a working solution and my answer may not address all constraints of your situation. For example, the axios instance could be created inside the provider as a lazy initialized useRef, same thing goes for the UserService instance, etc.
Related
I would like to have following, but working code in my http-axios file:
import axios from "axios";
import {useAuthContext} from './services/useAuthContext'
const {user} = useAuthContext();
export default axios.create({
baseURL: "http://localhost:4000/api/",
headers: {
"Content-type":"application/json",
"Authorization": `Bearer ${user.token}`
}
});
I can not understand how to make my code working because of the continuous dependencies i have.
I call this axios.create export later in an ./services/imgs.js file:
import http from "../http-axios";
class VMsDataService {
getLatest(page=0){
return http.get(`vms/latest`);
}
}
which i use in App.js file:
import VMsDataService from "./services/vms";
VMsDataService.getLatest()
.then(response => {
//
}).catch(e => {
//
});
So my question is. How can i restructure my code to be able to retrieve dynamic data within my axios config and also have this class based functionality i'm having right now. Main point of this is to have dynamic Authorization header taken from useAuthContext component
I tried to turn everything into components but it wasn't successful.
A way to create a reusable component, is to create a custom hook.
For example a custom hook that will return user object and set Authorization :
import axios from "axios";
import {useAuthContext} from './services/useAuthContext'
const useAxios = () => {
const {user} = useAuthContext();
return axios.create({
baseURL: "http://localhost:4000/api/",
headers: {
"Content-type": "application/json",
"Authorization": `Bearer ${user.token}`,
},
});
};
export default useAxios;
Then you can use this custom hook in VMsDataService.
I am not used to class component, so I wrote it in functional component. I think it is a lot easier :
import React from "react";
import useAxios from "../hooks/useAxios";
const VMsDataService = () => {
const http = useAxios();
const getLatest = (page = 0) => {
return http.get(`vms/latest`);
};
return {getLatest};
};
export default VMsDataService;
Hope I understand clearly what you were asking for !
I am using axios in my create-react-app. Which is the best way to use axios:
Method 1:
ajax.js
import axios from 'axios';
const axiosInstance = axios.create({});
export default axiosInstance;
app.js
import ajax from './ajax.js';
ajax.post('url');
Method 2:
ajax.js
import axios from 'axios';
class AjaxService{
constructor(apiConfig){
this.service = axios.create(apiConfig);
}
doGet(config){
return this.service.get(config.url);
}
...
}
export default AjaxService;
app.js:
import AjaxService from './ajax';
const service1 = new AjaxService();
service.doGet({url:'url'});
app2.js
import AjaxService from './ajax';
const service2 = new AjaxService();
service.doGet({url:'url'});
In method 2, we have to initialize the service wherever we make a call, which may or may not be a best practice. If we follow method 2, Is there a way to make it as a common service across the application?
i've seen a way in here and i came up with another solution like i explained below:
1 - i created my service with axios
import axios from 'axios';
const instance = axios.create({
baseURL: process.env.REACT_APP_BASE_URL
// headers: { 'X-Custom-Header': 'foobar' }
});
// Add a request interceptor
instance.interceptors.request.use(
(config) => {
// Do something before request is sent
return config;
},
(error) => {
// Do something with request error
return Promise.reject(error);
}
);
// Add a response interceptor
instance.interceptors.response.use(
(response) => {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
},
(error) => {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
}
);
export default instance;
2- i use that service to create a function for api call.
in here i can add a new AbortController for later use in useEffect.
import axios from 'services/request';
export function getMarket(type, callBack) {
const controller = new AbortController();
axios
.get(`https://dev.zh1.app/api/market/map?type=${type}`, {
signal: controller.signal
})
.then((res) => {
callBack(true, res.data);
})
.catch((res) => {
callBack(false, res.response);
});
return controller;
}
export default {
getMarket
};
3- in the hooks folder i created a hook called useApi. the controller from step 2 used in here. if you check the link above you can see the author add request function because you may have some props to pass to api call. i think it is valid but ugly. so i decided to create a closure for useApi to pass any params i want to the Axios in step 2.
import { useEffect, useState } from 'react';
// in useStrict mode useEffect call twice but will not in production
export default function useApi(apiFunc) {
return function useApiCall(...params) {
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
const apiCall = useCallback(() => {
setLoading(true);
// controller is new AbortController which set each api function
const controller = apiFunc(...params, (ok, data) => {
setLoading(false);
if (ok) {
setResult(data);
} else {
setError(data.message);
}
});
return () => {
controller.abort();
};
}, []);
useEffect(() => {
apiCall();
}, []);
return {result, loading, error, [apiFunc.name]: apiCall};
};
}
4- finally in my react component
import { IconButton } from '#mui/material';
import useApi from '#share/hooks/useApi';
import { Refresh } from '#share/icons';
import { getCaptcha as CaptchaApi } from 'api/oauth/captcha';
import CaptchaStyle from './style';
export default function Captcha() {
const { result: captcha, getCaptcha } = useApi(CaptchaApi)();
return (
<CaptchaStyle>
<img src={`data:image/png;base64,${captcha?.base64}`} alt="captcha" />
<IconButton onClick={getCaptcha}>
<Refresh />
</IconButton>
</CaptchaStyle>
);
}
i think this approach i quite good and if you dont need to pass any props just call useApi([yourfunction])() with empty function.
and you can have access to the function inside of useApi if you need to call it again.
It totally depends on your project. If your project relies more on the function component then go ahead and use the first approach.
If you use classes for the majority of your components go for the second approach.
I generally use the first approach, it's easy and avoids this altogether. Also, it's easy to target multiple instances.
// Ajax.js file
import axios from "axios";
export function updateData=(body,callback){
le url= 'your api to call'
axios
.put(url, body)
.then((response) => response.data)
.then((res) => {
callback(res);
})
.catch((error) => {
console.log(error);
callback('error occurred');
});
}
// app.js file
import {updateData} from './ajax.js'
//Place your code where you need
updateData(yourBodyToPass,res=>{
//Stuff after the response
)
Note:- pass your data as first argument and get response of api from second
I have a React app that is making calls to an API. I have a Client component to handle the calls, and the Components can access it like this (this example is in the componentDidMount function of the Home page, where I want to get a list of all this user's items):
componentDidMount() {
let userId= this.context.userId;
var url = "items/getAllMyItems/" + userId;
Client.fetchData(url, data => {
this.setState({items: data});
});
}
The current setup has no security (just for testing purposes) and the Client is defined like this (this is index.js):
function fetchData(fetchPath, cb) {
return fetch(`https://api.url/${fetchPath}`, {accept: "application/json"})
.then(cb);
}
(there are a couple of other functions which check the results etc, but I've left them out for brevity).
Now, my app connects to Firebase for handling authentication. I have A Firebase component which has 3 files:
firebase.js:
import app from 'firebase/app';
import 'firebase/auth';
const config = {
apiKey: /* etc */,
};
class Firebase {
constructor() {
app.initializeApp(config);
this.auth = app.auth();
}
// *** Auth API ***
doSignInWithEmailAndPassword = (email, password) =>
this.auth.signInWithEmailAndPassword(email, password);
doSignOut = () => this.auth.signOut();
}
export default Firebase;
context.js:
import React from 'react';
const FirebaseContext = React.createContext(null);
export const withFirebase = Component => props => (
<FirebaseContext.Consumer>
{firebase => <Component {...props} firebase={firebase} />}
</FirebaseContext.Consumer>
);
export default FirebaseContext;
index.js:
import FirebaseContext, { withFirebase } from './context';
import Firebase from './firebase';
export default Firebase;
export { FirebaseContext, withFirebase };
We're now implementing backend security, and I need to pass the Firebase token to the API when making calls. I can't figure out how to do it properly.
I know I need to call
firebase.auth().currentUser.getIdToken(true).then(function(idToken) {
// API call with Authorization: Bearer `idToken`
}).catch(function(error) {
// Handle error
});
so I figured that Client/index.js would need to change to something like:
import react from 'react';
import { FirebaseContext } from '../Firebase';
function fetchData(fetchPath, cb) {
<FirebaseContext.Consumer>
{firebase => {
firebase.auth().currentUser.getIdToken(true)
.then(function(idToken) {
// API call with Authorization: Bearer `idToken`
return fetch(`https://api.url/${fetchPath}`, {accept: "application/json"})
.then(cb);
}).catch(function(error) {
// Handle error
});
}}
</FirebaseContext.Consumer>
}
but if I do this I get the error "Expected an assignment or function call but instead saw the expression". I realize this is because it's expecting me to return a component, but I don't want to do that as there's nothing to return. I also tried using useContext, and changing fetchData to:
const Client = () => {
const firebase = useContext(FirebaseContext);
firebase.auth().currentUser.getIdToken(true)
.then(function(idToken) {
// API call with Authorization: Bearer `idToken`
fetch(`https://api.url/${fetchPath}`, {accept: "application/json"})
.then(cb);
}).catch(function(error) {
// Handle error
});
}
but I got an error about an Invalid Hook Call.
Can anyone point me in the right direction?
The code you have to get the ID token looks fine to me.
How to pass it to the API depends on what that API expects, but since you mention ```Authorization: Bearer idToken `` that would typically look like this:
fetch(`https://api.url/${fetchPath}`, {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer ' + idToken
}
})
I'm using the context API and I have this in my context file:
useEffect(() => {
async function getSingleCountryData() {
const result = await axios(
`https://restcountries.eu/rest/v2/alpha/${props.match.alpha3Code.toLowerCase()}`
);
console.log(result)
setCountry(result.data);
}
if (props.match) getSingleCountryData();
}, [props.match]);
In the component I'm using, it doesn't work because it doesn't know what the props.match.alpha3Code is. How can I can pass the value? The alpha3Code is coming from the URL: localhost:3000/country/asa where asa is the alpha3Code, how can I get this value?
Basically, what I'm trying to do is. I have a list of countries I listed out on the home page. Now I'm trying to get more information about a single country. The route is /country/:alpha3Code where alpha3Code is gotten from the API.
FWIW, here is my full context file:
import React, { useState, createContext, useEffect } from 'react';
import axios from 'axios';
export const CountryContext = createContext();
export default function CountryContextProvider(props) {
const [countries, setCountries] = useState([]);
const [country, setCountry] = useState([]);
useEffect(() => {
const getCountryData = async () => {
const result = await axios.get(
'https://cors-anywhere.herokuapp.com/https://restcountries.eu/rest/v2/all'
);
setCountries(result.data);
};
getCountryData();
}, []);
useEffect(() => {
async function getSingleCountryData() {
const result = await axios(
`https://restcountries.eu/rest/v2/alpha/${props.match.alpha3Code.toLowerCase()}`
);
console.log(result)
setCountry(result.data);
}
if (props.match) getSingleCountryData();
}, [props.match]);
return (
<CountryContext.Provider value={{ countries, country }}>
{props.children}
</CountryContext.Provider>
);
}
In the component I'm using the country, I have:
const { country } = useContext(CountryContext);
I know I can do this from the component itself, but I'm learning how to use the context API, so I'm handling all API calls in my context.
The API I'm making use of is here
Codesandbox Link
Project Github link
You can update the context from a component using it by passing down a setter function which updates the context state.
export default function CountryContextProvider({ children }) {
const [countries, setCountries] = useState([]);
const [country, setCountry] = useState([]);
const [path, setPath] = useState('');
useEffect(() => {
async function getSingleCountryData() {
const result = await axios(`your/request/for/${path}`);
setCountry(result.data);
}
if(path) getSingleCountryData();
}, [path]);
return (
<CountryContext.Provider value={{ countries, country, setPath }}>
{children}
</CountryContext.Provider>
);
}
Now use setPath to update the request endpoint with the route match once this component is mounted.
const Details = ({ match }) => {
const {
params: { alpha3Code }
} = match;
const { country, setPath } = useContext(CountryContext);
useEffect(() => {
setPath(alpha3Code);
}, [alpha3Code]);
return (
<main>Some JSX here</main>
);
};
export default withRouter(Details);
Linked is a working codesandbox implementation
In the component I'm using, it doesn't work because it doesn't know
what the props.match.alpha3Code is. How can I can pass the value? The
alpha3Code is coming from the URL: localhost:3000/country/asa where
asa is the alpha3Code, how can I get this value?
I guess the root of your problem is this one. You have no idea which the aplha3Code parameter comes from. I have dived into your GitHub repo to make it clearer.
First, match is one of react-router provided terms. When you use something like props.match, props.history, props.location, you must have your component wrapped by the withRouter, which is a Higher Order Component provided by react-router. Check it out at withRouter. For example, below is the withRouter usage which is provided by react-router:
// A simple component that shows the pathname of the current location
class ShowTheLocation extends React.Component {
render() {
const { match, location, history } = this.props;
return <div>You are now at {location.pathname}</div>;
}
}
const ShowTheLocationWithRouter = withRouter(ShowTheLocation);
ShowTheLocation is wrapped by the withRouter HOC, which will pass all the route props (match, history, location...) to ShowTheLocation through props. Then inside ShowTheLocation, you are able to use something like props.match. Clear enough?
So back to your problem! You have not wrapped any components by withRouter yet, have you? Stick to it and have some fun! You will figure it out soon!
Also, please be aware that you must place your component under the BrowserRouter to be able to use the react-router things
If you want to go with Hooks, please take a look at this super useful one:
https://usehooks.com/useRouter/
It wraps all the useParams, useLocation, useHistory, and use useRouteMatch hooks up into a single useRouter that exposes just the data and methods we need. Then, for example, inside your component, do it like this:
import { useRouter } from "./myCustomHooks";
const ShowMeTheCode = () => {
const router = useRouter();
return <div>This is my alpha3Code: {router.math.params.alpha3Code}</div>;
}
Update 1 from Peoray's reply:
This is where the problem occurs:
https://github.com/peoray/where-in-the-world/blob/cb09871fefb2f58f5cf0a4f1db3db2cc5227dfbe/src/pages/Details.js#L6
You should avoid calling useContext() straightly like that. Have a look at my example below:
// CountryContext.js
import { useContext, createContext } from "react";
const CountryContext = createContext();
export const useCountryContext = () => useContext(CountryContext);
Instead, you should wrap it by a custom hook like useCountryContext above. And then, inside your Details component, import it and do like:
import React, from 'react';
import { useCountryContext } from '../contexts/CountryContext';
const Details = (props) => {
const { country } = useCountryContext();
...
}
Update 2 from Peoray's reply:
Although I have stated it in advance for you, I just feel like you did not make enough effort to go through what I said.
Also, please be aware that you must place your component under the
BrowserRouter to be able to use the react-router things
In your codesandbox, it shows the Cannot read property 'match' of undefined error. Okay, as I said above, you have not moved the ContextCountryProvider to under the BrowserRouter to get the useRouter work.
I have fixed it for you, and the screen popped out, please check it at updated codesanbox here. You will get what you need at App.js file.
Although it still throws some Axios bugs there, I think my job is done. The rest is up to you.
You might use useParams hook to get everything you need inside your context provider. Docs
Something like this:
import useParams in file where your Provider component is
in your CountryContextProvider add this at the top of the component:
const { alpha3Code } = useParams();
update useEffect which needs props.match
useEffect(() => {
async function getSingleCountryData() {
const result = await axios(
`https://restcountries.eu/rest/v2/alpha/${alpha3Code.toLowerCase()}`
);
console.log(result)
setCountry(result.data);
}
if (alpha3Code) getSingleCountryData(); // or if you need `match` - do not destructure useParams()
}, [alpha3Code]);
Built API with NodeJS, Express & MongoDB, used JWT and Cookies for user authentication.
Fetched user data from API with axios service using store (vuex). Created auth.js in store folder, created fetchData action which GETs the data from backend (axios.get(apiRoute)) and sets the user to state.
Wanted to do this using nuxtServerInit, so i craeted index.js file in store folder. Added empty state & actions. Action containts nuxtServerInit which uses dispatch() to call fetchData method in auth.js.
Yet after all of this, it doesn't work at all. For example: User is logged in, but account page is not rendering with user data (name, email, image etc.).
I tried returning a promise from fetchData action in auth.js, and it didn't work.
Also i tried setting up fetchData action insite of the index.js file and calling dispatch directly on it.
store/auth.js
// Importing Files
import axios from 'axios';
// State
export const state = () => ({
user: null
});
// Mutations
export const mutations = {
SET_USER (store, data) {
store.user = data
},
RESET_USER (store) {
store.user = null
}
};
// Actions
export const actions = {
// Fetch User Account
async fetchData ({ commit }) {
try {
const response = await axios.get('http://localhost:3000/api/v1/users/account');
commit('SET_USER', response.data.doc);
return response;
} catch (err) {
commit('RESET_USER');
}
}
};
store/index.js
// State
export const state = () => ({
});
// Actions
export const actions = {
async nuxtServerInit({ dispatch }) {
console.log('Testing');
const res = dispatch('auth/fetchData');
return res;
}
};
components/Settings.vue
<template>
<section class="data-block-wrap" v-if="user">
<BlockHeader :blockHeaderName="`Welcome Back, ${user.name.split(' ')[0]}`" btnText="More Details" />
<img :src="getPhotoUrl(user.photo)" alt="User Photo" class="user-data__image">
<p class="user-data__short-bio">{{ user.shortBio }}</p>
</section>
</template>
<script>
export default {
// Computed
computed: {
user() {
return this.$store.state.auth.user;
}
}
...
};
</script>
I expect to render user data properly on Vue components but currently it doesn't work at all. The render is static, no data from database / api showing.
EDIT / UPDATE
App renders user data properly when calling fetchData on created() hook in default.vue file ('Parent' file for all of the components).
default.vue
<template>
<div class="container">
<TopNav />
<SideNav />
<nuxt />
</div>
</template>
// Importing Components
import TopNav from '#/components/navigation/TopNav';
import SideNav from '#/components/navigation/SideNav';
import axios from 'axios';
import { mapActions } from 'vuex';
export default {
components: {
TopNav,
SideNav
},
methods: {
// Map Actions
...mapActions('auth', ['fetchData']),
async checkUser() {
const user = await this.fetchData();
},
},
// Lifecycle Method - Created
created() {
this.checkUser();
}
}
</script>
It seems that something very interesting is happening here. The problem is calling axios.get('http://localhost:3000/api/v1/users/account') from within nuxtServerInit().
This is causing what is essentially an infinite recursion. nuxtServerInit makes a call to http://localhost:3000, which hits the same server, runs nuxtServerInit again, and calls http://localhost:3000, and so on until the javascript heap is out of memory.
Instead of using nuxtServerInit for this, use the fetch method:
The fetch method is used to fill the store before rendering the page,
it's like the asyncData method except it doesn't set the component
data.
Note: You do not have access to the Nuxt component in fetch, so you must use the context object instead of "this"
// inside your page component
export default {
fetch (context) {
return context.store.dispatch('auth/fetchData');
}
}
As a general rule:
Use fetch to fill store data on the server or client
Use asyncData to fill component data on the server or client
Use nuxtServerInit for things like setting up the store with values on the request object, like sessions, headers, cookies, etc, which is only required server side
The solution to this question is to use the NuxtServerInt Action this way inside your store.js
1. you will need to run npm install cookieparser and npm install js-cookie
const cookieparser = process.server ? require('cookieparser') : undefined
export const state = () => {
return {
auth: null,
}
}
export const mutations = {
SET_AUTH(state, auth) {
state.auth = auth
},
}
export const actions = {
nuxtServerInit({ commit }, { req }) {
let auth = null
if (req.headers.cookie) {
try {
const parsed = cookieparser.parse(req.headers.cookie)
auth = parsed.auth
} catch (err) {
console.log('error', err)
}
}
commit('SET_AUTH', auth)
},
}
Then in your login page component, you call your backend API, just like this
import AuthServices from '#/ApiServices/AuthServices.js'
import swal from 'sweetalert'
const Cookie = process.client ? require('js-cookie') : undefined
async onSubmit() {
try {
const body = {
email: this.email,
password: this.password,
}
const res = await AuthServices.loginUrl(body)
console.log('res', res)
console.log('res', res.data.message)
setTimeout(() => {
// we simulate the async request with timeout.
const auth = {
accessToken: res.data.payload.token, // from your api call, you get the user token
userData: res.data.payload.user,
}
swal('Logged in', `${res.data.message}`, 'success')
this.email = this.password = ''
this.$refs.loginForm.reset()
this.$store.commit('setAuth', auth) // mutating to store for client rendering
Cookie.set('auth', auth) // saving token in cookie for server rendering
this.$router.push('/')
}, 1000)
} catch (error) {
console.log('error', error)
swal('Error!', `${error.message}`, 'error')
}
},
your AuthServices.js looks like this
import axios from 'axios'
const apiClient = axios.create({
baseURL: `http://localhost:3000`,
})
export default {
loginUrl(body) {
return apiClient.post('/login', body, {
headers: {
'Content-Type': 'application/json',
},
})
}
}
then you get the user data using computed in the navbar or say dashboard e.g to say Hi,Xavier
inside where you want place the user data, just add this
<template>
<section>
<p class="firtname_data">Hi, {{ user.firstnam }}</p>
</section>
</template>
<script>
export default {
// Computed
computed: {
user() {
return this.$store.state.auth.userData
}
...
};
</script>
Hope this help... it worked for me
I think you forgot to write await before dispatch
export const actions = {
async nuxtServerInit({ dispatch }) {
console.log('Testing');
const res = await dispatch('auth/fetchData');
return res;
}
}