I'm struggling to initialize my Vuex store with the account details of the logged in user from localStorage.
I've been through as many examples as I can of Auth using Nuxt, and none of them demonstrate how on the client side to pull an authToken from localStorage to re-use with subsequent API requests when the user refreshes the page or navigates to the App
Disclaimer: I'm not using Nuxt in SSR (this might affect your answer).
What is annoying is that I can actually load from localStorage and initialize my state but then it gets overwritten. I'll show you what I mean with this small code example:
buildInitialState () {
if (!process.server) {
// Query the localStorage and return accessToken, refreshToken and account details
return {accessToken: <valid-string>, refreshToken: <valid-string>, account: <valid-json-blob>}
} else {
// Return "empty map", we use the string "INVALID" for demonstration purposes.
return {accessToken: "INVALID", refreshToken: "INVALID", account: "INVALID"}
}
}
export const state = () => buildInitialState()
If I put a breakpoint on buildInitialState I can see that I correctly initialize the state with the value of localStorage, i.e. I get the accessToken and refreshToken, etc.. back.
All seems well.
But then .....in another part of the program I'm using Axois to send requests, and I use an axios interceptor to decorate the request with the accessToken. To do this I have to stick it into a plugin to get access to the store.
Something like so:
export default ({ app, store }) => {
axios.interceptors.request.use((config) => {
const accessToken = _.get(store.state, ['account', 'accessToken'])
if (accessToken) {
config.headers.common['x-auth-token'] = accessToken
}
return config
}, (error) => Promise.reject(error))
}
Here the store is closed over in the arrow function supplied to axios so when I go to send the request it sees if there is a valid accessToken, and if so then use it.
BUT, and here's the kicker, when a request is made, I look at the store.state.account.accessToken and low and behold its been reinitialized back to the value of "INVALID".
What? Que?
It's almost like the store was reinitialized behind the scenes? Or somehow the state in the plugin is "server side state"?? because if I try and log buildInitialState I don't get any messages indicating that the path that produced a map with INVALID is being run.
Basically, I don't thoroughly understand the initialization pathway Nuxt is taking here at all.
If any Nuxt masters could help me out understand this a bit more that would be great, it's turning into a bit of a show stopper for me.
Essentially! All I want to be able to do is save the user so that when they refresh their page we can keep on running without forcing them to re-login again....I thought that would be simple.
Thanks and regards, Jason.
I've solved this with a bit of experimentation and comments from other posters around what is called SSR and SPA.
Firstly, this https://github.com/nuxt/nuxt.js/issues/1500 thread really helped me and the final comment from #jsonberry steered my mind in the right direction, away from fetch and asyncData.
I finally had a bit more of an understanding of how NUXT.js was separating SSR and SPA calls.
I then tried #robyedlin suggestion of putting localStorage initialization in the created() method for my main layout/default.vue page.
While I made progress with that suggestion it turns out created() is also called SSR and I was still trying to initialize my store from credentials that weren't accessible.
Finally, moving the initialization to mounted() did the trick!
So in summary:
My account store is left alone, I don't try and initialize it when it is created (it's just overwritten at some point when the SSR stuff runs)
On mounted() in layout/defualt.vue I read from localStorage and initialize the account store so I can start making API requests with the appropriate accessToken.
That seems to have done the trick.
Related
I am having some issues trying to connect to a matrix server using the matrix-js-sdk in a react app.
I have provided a simple code example below, and made sure that credentials are valid (login works) and that the environment variable containing the URL for the matrix client is set. I have signed into element in a browser and created two rooms for testing purposes, and was expecting these two rooms would be returned from matrixClient.getRooms(). However, this simply returns an empty array. With some further testing it seems like the asynchronous functions provided for fetching room, member and group ID's only, works as expected.
According to https://matrix.org/docs/guides/usage-of-the-matrix-js-sd these should be valid steps for setting up the matrix-js-sdk, however the sync is never executed either.
const matrixClient = sdk.createClient(
process.env.REACT_APP_MATRIX_CLIENT_URL!
);
await matrixClient.long("m.login.password", credentials);
matrixClient.once('sync', () => {
debugger; // Never hit
}
for (const room of matrixClient.getRooms()) {
debugger; // Never hit
}
I did manage to use the roomId's returned from await matrixClient.roomInitialSync(roomId, limit, callback), however this lead me to another issue where I can't figure out how to decrypt messages, as the events containing the messages sent in the room seems to be of type 'm.room.encrypted' instead of 'm.room.message'.
Does anyone have any good examples of working implementations for the matrix-js-sdk, or any other good resources for properly understanding how to put this all together? I need to be able to load rooms, persons, messages etc. and display these respectively in a ReactJS application.
It turns out I simply forgot to run startClient on the matrix client, resulting in it not fetching any data.
I am creating a new site using NextJS, the issue i am having is in regards to a password reset verification endpoint.
When a user triggers a password reset, it goes to the API, does all the processing and then returns them to the NextJS frontend at /verifyreset, which saves a code into localstorage, does a small bit of processing and then forwards them onto another page.
The issue is that there is a Default layout wrapping the component in my _app.js like so;
function MyApp({ Component, pageProps }) {
return (
<DefaultLayout><Component {...pageProps} /></DefaultLayout>
);
}
which means that the layout shows on the /verifyreset endpoint, and I only want that endpoint to process data.
Is there any way around this? to have an endpoint that can access localstorage but not be a 'page' so to speak
I did partially understand your question, it would have been clear if you had attached more code snippets in the question.
Anyway, from your statement below:
When a user triggers a password reset, it goes to the API, does all
the processing and then returns them to the NextJS frontend at
/verifyreset, which saves a code into localstorage, does a small bit
of processing and then forwards them onto another page.
what I understood is:
User triggers a password reset [lets say from PageA]
API is invoked; some processing happen
API then, redirects user to /verifyreset page [lets say it PageB]
Navigating to the page, information is saved into localstorage
Once that is completed, user is redirected to another page [lets say it PageC]
Correct me if I am wrong, so your question is, how could you actually skip users to view /verifyreset page but do the things like save to localstorage and other background operations.
Answer 1: The api is being invoked from PageA (see 1). Instead of the api redirecting user to /verifyreset page on the frontend, send some data (JSON or XML) to the calling function (in PageA components..). Based on that data, do the processing and once every thing is complete, redirect the user to PageC. [no need to worry about PageB i.e. /verifyreset page]. Please find the code snippet below:
**API End Point**
async resetPassword(req, res) {
try {
const model = model.body || {};
let data = await PasswordBusiness.reset(model);
//data needs to have information that you require on frontend
return res.json({success: true, data: data});
} catch (error) {
return res.json({success: false, error: error});
}
}
** Frontend - pageA **
import Router from 'next/router';
const resetPassword = (model) => {
callApiEndPoint(model).then(data) {
// do what you want to do with data
//finally navigate to page c
Router.push('url-to-page-c');
});
};
return <button onClick={resetPassword}> Reset </button>
Answer 2: If you require redirecting to the page any how from the API [I think you don't necessary require this], once operation/processing is completed on API end, redirect the user directly to the pageC with some query params with data (if they are not security vulnerable data). e.g. /pagec?token=sometokens&otherinfos=otherinfos and do things on pageC itself. Once completed, remove the query string from the page without refreshing the page.
You have to put /verifyreset at the api folder.
This is what Next.js said in their documentation :
Any file inside the folder pages/api is mapped to /api/* and will be treated as an API endpoint instead of a page.
Reference : https://nextjs.org/docs/api-routes/introduction
In our VueJS application, we are having few API's which are calling each and every time whenever the page reloads. In those API's. few response will never change and very few will rarely change. I planning to cache those API calls response and store it in a variable and use it whenever needed and reduce the number of requests when page reloads.
I am new to vueJS and not having any idea how to implement it. Is there anyway to achieve this in VueJS or Javascript? Any help would be most appreciated.
Sample HTML code,
<div class="col-sm-6">
<span>Is User Available? {{userInfo[is_user_available]}} </span>
<span> User Type : {{userType}} </span>
</div>
API call will be like below,
created: function () {
this.checkForUser();
},
methods: {
checkForUser: function() {
this.api.call('user_info', { username : this.username })
.then((response) => {
if (response) {
this.userInfo = response;
this.userType = this.userInfo['user_type'];
}
})
.catch((error) => {
this.userInfo.length = 0;
})
}
}
If you store the data in a regular Vuex store you will loose it on page refresh unless you use vuex-persistedstate plugin, which saves the store data on the local storage. (here is a working example)
Elaborating on #Mysterywood answer you can simply store it on local storage by yourself.
You can achieve that by simply doing
get:
const userType = window.localStorage.getItem('userInfo')
set:
window.localStorage.setItem('userInfo', response)
and remove:
window.localStorage.removeItem('userInfo')
There are few ways of doing this depending on how deep you want to go:
If you just want state to persists during the SPA session, you can do so:
Vuex if you would like to store globally accessible state/data. This allows your state to persist regardless of whether the components are destroyed/created.
Store it on your root-level Vue instance. If you're using the Vue CLI, this will be in your main.js. You can do something like so:
new Vue({
// ...
data: {
userType: {}
}
})
You can then access it via this.$root.userType. This is fine for small projects, but generally not recommended as things can get messy very quickly.
There's also EventBus, but again, this can get messy very quickly. EventBus is also deprecated in Vue3.
If you want to cache the response and access them again even after the user close their tab/browser, you will want to look into:
Cookies
localStorage
ServiceWorkers
check this, it can help :client-side storage in vuejs
Client-side storage is an excellent way to quickly add performance gains to an application. By storing data on the browser itself, you can skip fetching information from the server every time the user needs it. While especially useful when offline, even online users will benefit from using data locally versus a remote server. Client-side storage can be done with cookies, Local Storage (technically “Web Storage”), IndexedDB, and WebSQL (a deprecated method that should not be used in new projects).
I have been recently assigned to maintain EmberJS code at work and I am complete newbie with EmberJS or Javascript, Hence, I came here for help.
Context
I have a live feed page (like a news feed) that is integrated with Infinity Loading and this feed constantly checks with the backend to see for any new messages. This is causing this code to be constantly invoked.
import InfinityRoute from "ember-infinity/mixins/route";
export default Ember.Route.extend(InfinityRoute,{
perPageParam: "perPage",
totalPagesParam: "meta.totalPages",
queryParams: { dateFrom:.... },
model(params){
const defaultParams = { perPage: 25, startingPage: 1 } ;
const modelParams = Ember.$.extend(defaultParams, params);
return this.infinityModel("message", modelParams);
}
The above code will load 25 pages with startingPage as 1 and pass it to the infinityModel which will contact the data store to get the necessary information.
Question Scenario
Above code is constantly bringing you back to page 1 and lose track of the current page
(Example, If you scroll above 25 messages and are in the 2nd page, then the 2nd page keeps disappearing and re-appearing because of the scroll position and model re-initialization setting the startingPage back to 1)
I would love if you guys can provide me with any suggestions on how to handle this model re-initialization and infinityScroll issue?
I'm not sure if I understand your problem correctly but likely we had to solve similar problem. Our backend periodically rewrites model that is used to generate recursive components. These components have some state information that was loose after each model reload. We had decided to solve this using Ember.ArrayProxy.
You want to record 'live-array' where changes will be sent to templates, like when you are using store.findAll() - on other side store.peekAll() returns standard array.
I'm new to Meteor so I've been playing around and now I'm stuck with this problem.
I'm using React Router to try to show a theme based in the URL /(:userId). If there's no userId inserted into the URL it should show the current user's theme and If there's no current user it should show a default theme.
It's working randomly. Sometimes I get correct theme, sometime it throws undefined when reading themeColor even though the data is there. I can see with console.log that it always get the right Id, but still findOne can throws undefined. It specially happens when I change the URL (/xyz) and go back to the default one (/).
I verified with the console that userId is the actual owner of themeColor and themeTextColor.
I'm using React, React-router, autopublish. I removed insecure.
getMeteorData() {
var currentViewedPageId = this.props.params.userId? this.props.params.userId:(Meteor.userId()?Meteor.userId():false);
console.log(currentViewedPageId); //Allways right
console.log(Personalization.findOne({owner: currentViewedPageId}).themeColor); //Sometimes undefined, sometimes not
if(currentViewedPageId)
{
return {
currentUser: Meteor.user(),
themeColor: Personalization.findOne({owner: currentViewedPageId}).themeColor,
themeTextColor: Personalization.findOne({owner: currentViewedPageId}).themeTextColor
};
}
return {
currentUser: Meteor.user()
}
},
Since the code is working sometime. There can be some test data which doesn't match the schema. So test all data in the collection.