I have a project with Vuejs and Vue-Router. When a user pay in (login) system, the server return a file Json with token, username and first name, this file is stored in localstorage for be used in following requests. The first name is used in layout for show a message welcome in the navbar. After logout, localstorage is cleared and the problem is here, when other user pay in the message welcome not reload with new first name.
<v-chip>{{bienvenida}}</v-chip>
computed: {
bienvenida() {
const user = JSON.parse(localStorage.getItem("user"));
return user ? `Bienvenido ${user.firstName}` : "";
}
}
The LocalStorage is not reactive, so the computed property does not react on a change of the localStorage. There are various ways to circumvent this, but the easiest is, to watch an internal property and just persist the values to the localStorage too.
One proper way to do this is to use Vuex (official state management of Vue) to store your data to a Vuex store. There is then a Vuex plugin to persist all of the store or parts of it in the local or session storage. The Vuex store is reactive, so you can just watch changes to the store in your computed property.
Related
I have a doubt regarding dynamically rendering the logged in user's name in the NavBar in react.
So my app is structured in the following way
<App>
<Header/>
<Router>
....
<Router/>
<Footer/>
<App/>
Now inside the header component, I need to display "Welcome <user>" whenever the user is logged in. Here are some solutions that I thought of,but Not sure if I'm missing out on anything.
To send it as a prop to the header. But it can only be dynamic if I do a setState in the App component and re render the entire app component (Which I want to avoid)
To store it in redux, so header can dynamically get the updated. But since I am using JWT, and the user needs to stay logged in, If the user refreshes the page, store content will be wiped out.
I can store the username in localstorage as well, but the entire app would still need to be re rendered in order to display the change
-) I think it can be accomplished in Angular by BehaviorSubject, but in this scenario, I'm clueless.
Is there any solution or method that I am missing?
Thanks in advance
At first if you want to keep your user logged in then you have to store the JWT somewhere. Here by 'somewhere' I mean, so that you can access it on next reload.
In my maximum project I store my JWT in local-storage. It works something like this
When user logs in I store the JWT in local-storage also in my reducer.
Then on my 'Inital State' I always take the local-storage so that if we have anything in the local-storage it means user is logged in
initial_state = {
userName: localstorage.getItem('user_name')
}
That's how you can keep your user logged in and at the time of logout erase the local-storage as well as the reducer.
You can send the user as a prop to the header. That is kind of the preferred method. Have the user login and save their app state with redux of the useState hook that is now available in React.
To your second question, JWTs preserve session data. Rerendering the page won't cause that to disappear. Well-designed apps have JWT with an expiration time (usually 20 minutes or less).
I worked on a voting application that did exactly what you're looking for and did not require any global state management. Feel free to pick through it. It's an open-source project. I think we used Auth0 or Google's Oauth library to do authentication.
Here's the code for the React header(navbar): https://github.com/poll-pal/poll-pal/blob/master/src/Components/Header/Header.js
Here's the code for the Express server that backed it: https://github.com/poll-pal/poll-pal/blob/master/server/routes/auth.js
I have an app using Nuxt on the front-end. Since Nuxt is a SSR technology the data in vuex stores gets deleted after refreshing the browser. Is there a way to save and keep vuex data in browser storage (preferably not cookies) even after refreshing the browser?
We use vuex-persist to save some store information in indexDB to provide offline feature (PWA) but you can use localstorage and cookies as well. Its pretty forward to use and also have an nuxt implementation:
// Inside - nuxt.config.js
export default {
plugins: [
{ src: '~/plugins/vuex-persist', ssr: false }
]
}
// ~/plugins/vuex-persist.js
import VuexPersistence from 'vuex-persist'
export default ({ store }) => {
new VuexPersistence({
/* your options */
key: 'vuex', // The key to store the state on in the storage provider.
storage: window.localStorage, // or window.sessionStorage or localForage
}).plugin(store);
}
You cannot save your Vuex state without any kind of browser storage. Depending on what you need to store, you could use:
localStorage if it's some small information, some setting or alike
make an API call to your backend and repopulate your Vuex store accordingly (best solution, especially if you stored some JWT token in your localStorage)
IndexedDB API, never used it but it's some kind of light database
Still, storing your whole Vuex state is not a thing to do in the browser for various reasons (security, performance, principle etc...).
In Addition to kissu's answer. You can use replaceState to restore a saved state.
But you probably shouldn't, you can create a bug that your user can't get out of by refreshing. And that's really frustrating.
The pattern to do this is fairly simple.
First, in vuex, subscribe to all mutations
https://vuex.vuejs.org/api/#subscribe
const unsubscribe = store.subscribe((mutation, state) => {
console.log(mutation.type)
console.log(mutation.payload)
})
// you may call unsubscribe to stop the subscription
unsubscribe()
Check which mutations you are interested in saving and store them in localStorage, or IndexDb or whatever you want.
On page load you need to check your storage solution for this data. If it exists. call replaceState
https://vuex.vuejs.org/api/#replacestate
store.replaceState(state: Object) // give it an object structure matching your Vuex module state structure
And this is how you can save and reload state.
I have a redux application that requires authentication. Right now I'm trying to make sure the application ensures the user is still logged in whenever they perform an application in the app. Here is the bottom of my App.jsx file:
function mapStateToProps({ offline, user }) {
return {
// we use this to know if we should redirect to auth pages
// we don't want logged in users to be hitting these static pages
loggedIn: !!user.token,
offline,
};
}
const ConnectedApp = connect(mapStateToProps)(App);
export default ConnectedApp;
I test this functionality by clearing the site local storage through the chrome developer tools. And then performing an action on the site. I've placed many different log statements. I find that mapStateToProps is being called, but the props are not changing. The application continues to think that the user is still logged in and the state hasn't changed.
I've been following all the online resources I can find, but nothing seems to be helping. Am I missing something?
That's because mapState only re-runs when an action was dispatched to the Redux store, and a new store state was calculated as a result. Modifying local storage does not involve dispatching a Redux action, so your mapState will never run.
Don't depend on values from localStorage in a mapState function - they should only extract values from the state argument, and return those.
I'm using React and Redux for my SPA and I want to store some data locally. And I need to synchronize appState with localstorage, so my data won't be lost after refreshing the page.
I'm completely new for React and Redux and have not much of understanding what's going on, but as I think Redux creates for me that state of entire app, so I can't just bind my state with localstorage in app component because it'll be just state of component and not of my app.
I would suggest storing the data in local storage with the following commands.
Set the data in localStorage can be done with the command:
localStorage.setItem('nameForData', variableNameForData);
To retrieve the data when required.
var variableNameForData = localStorage.getItem('nameForData')
To remove the data from localStorage:
localStorage.removeItem('nameForData')
These would typically be put inside action creators with a dispatch to change the state of some Boolean that tracks the applications interaction with localStorage.
For example you might have a state that is set to true when the local storage is created.
On refresh you might call an action creator that checks the local storage exists, and if it does set that boolean back to true or if it does not exist you are back to creating local storage and then set it to true.
You could put this function in componentWillMount(){} and it will be called when the component is first rendered, and thus in the case of a refresh.
Docs for component life cycle and specifically componentWillMount here
Docs for local storage here
I can suggest you to store the state after each action. For that you can use a localStorage middleware that only store the whole state object.
Then in your createStore part you will retrieve the initialState from the localStorage API.
In that case you don't need to modify any component, juste the initialState from localStorage and the middleware that save the state
I am building an application that requires a user to be a certain age before entering the site. The user is shown a form in which they must enter their date of birth, this information is then saved to local storage and then on every request check if that value is set in local storage. Currently inside main.js the checking code looks like:
let hasRememberedAge = localStorage.getItem('ageVerified')
if (hasRememberedAge !== null) {
store.dispatch(ageVerified())
}
This feels wrong, my question what is a better way to handle this? Should I fire an action on every page load which then triggers these checks? If so where should this dispatch live?
I don't see anything wrong with this. You can do this wherever you create the store.
Alternatively you can serialize a part of your state in a store.subscribe callback, and read it from the localStorage before creating the store. You can pass such persisted state as a second argument to createStore to preload it.