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.
Related
While working on a project I noticed a weird behavior of the useLocation hook that I can`t find an explanation to.
I have a button that when clicked it will redirect you to an EditOrder page and will pass a state with it:
const navigate = useNavigate();
const handleClick = (data) => {
navigate("edit-order", {state: {order: data}})
};
In the EditOrder page I check with a UseEffect hook if a state was provided and if not the user will be redirected to a different page:
const { state } = useLocation();
const navigate = useNavigate();
useEffect(() => {
if (!state) {
navigate("some-page");
}
}, []);
The weird part is when I refresh the page I can still access it, and if I console.log(state.order) the data is still there, even when I reload with ctrl + shift + r the state stays the same, and this also happens with the empty cache and hard reload option (tried in both chrome and edge).
But when I copy the URL and past it in a new tab I immediately get redirected to "some-page"
and console.log(state) will show null.
I checked both the cookies and local storage and I can't find the state data there.
Can someone explain why is this happening and how the state data is being saved?
Edit:
Here is a youtube video that shows this behavior.
The code on the video can be found in this sandbox, when you run the code on sandbox it runs as it should, on refresh all the states reset, but when running locally this problem occurs (on 2 different computers).
A git repo about location.state
React's useLocation is based on the history library, which uses the BrowserHistory in web apps.
Some browsers, like Chrome, persist BrowserHistory state between sessions, while others (like Firefox) do not.
This is likely why you're seeing this behavior locally but not in a Sandbox. It appears that CodeSandbox's browser clears history state on refresh. It's also why, if you copy the URL into another tab, the redirect works. BrowserHistory is local to a single tab.
In short, this is intended behavior. Either you need to clear the history state manually or store your application state elsewhere (useContext could be a good choice if you want to persist across pages but not across a refresh).
I am writing a web app based on the Spotify API. Once the user logs in using NextAuth, I want the app to automatically load the user's playlists.
I currently have it set up so that there is a button to load playlists and the button does not appear unless the user is logged in - this works and is simple. However, the button is unnecessary. There is no use-case for the app that doesn't begin with loading the user's playlists.
I currently have a thunk action written which was dispatched by the "Load Playlists" button to fetch the playlists asynchronously and add the playlists to the redux state.
I can think of multiple possible options to get the automatic loading to work well, but I imagine there has to be a clean way. NextAuth has the [signIn event callback]https://next-auth.js.org/configuration/events#signin which seems like the right place to start. But since this would be run server-side, it would need some way to contact the client.
Just observe the status in. a useEffect and once the status is authenticated call the load playlist action.
import { useSession } from "next-auth/react"
export default function Component() {
const { data: session, status } = useSession()
useEffect(() => {
if (status === "authenticated") {
Load_Playlists()
}
}, [status])
return <Main>
}
what I usually do is save the Jwt on local storage then use a use effect to get it if not existing then get it from the server using the thunk action.
also, see How to wait for action to complete before redirecting in react redux? how to define the thunk
Given following middleware, whats the best way to rerun the logic when ever store.getters.authenticated changes, and not only on the initial load.
middleware/auth.js
export default function ({ store, redirect }) {
if (!store.getters.authenticated) {
return redirect({ name: "login" })
}
}
You asked how to rerun it inside of the middleware itself, which is the 2nd part of a possible middleware trigger (like if the user is not authenticated anymore while staying on the same page) and without any specific action, like when using polling or websockets I thought.
Meanwhile, the 1st part is the easiest: call the middleware globally (in nuxt.config.js) to trigger it on each page navigation.
If he stays on the same page, you can also move with the router as you did but at this point, checking if there is an error in your axios is maybe more appropriate since it's the initiator.
I also do like to use this.$nuxt.refresh() to trigger all the checks when switching accounts globally, helps re-running all those tasty fetch() hooks.
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'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