LocalStorage with react-redux - javascript

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

Related

Nuxt, Vuex data clears on browser refresh

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.

Reload component with vue-router

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.

How can we use the same state across multiple tabs (pages)?

I have a page in my application that is an interactive chart with a bunch of settings (filters, time ranges, etc). I'd like to store them in-app state because some of the settings may be used by other components on another page, but right now if I click on any other tab and come back to the previous tab then the page shows the initial state(chart is gone, filtered data gone, date range showing the default value, dropdowns shows default ass well). And the state is also showing null.
Anything in your component state is dynamic; i.e., it is temporary. If you refresh the window, the old state is lost. A similar thing happens when you open a fresh tab—you get the state declared in your constructor. You can use any of the following if you want the state data in another tab:
Simply using redux won't solve your problem as redux store is also related to a specific browser tab. If you would like to persist your redux state across a browser refresh, it's best to do this using redux middleware. Check out the redux-persist, redux-storage middleware.
If you are using react-router you can simply pass required state through the link when you open a fresh tab. Here's an example:
<Link to={{
pathname: '/pathname',
state: { message: 'hello, im a passed message!' }
}}/>
Simply store the data in localStorage and access localStorage in other tabs.
If you are looking to use a variable across the entire application you can also use localStorage
localStorage.setItem('move', this.state.move);
also don't forget to unset it when you are done!
localStorage.removeItem('move');
The straight forward solution to this is redux-state-sync. It will just work and the store will be updated on all tabs and even windows on the browser.
I think you should implement react-redux into your app.
React-redux is a state management tool that manage the state of your application at a centralized location and you access data from store at anytime from anywhere.
React-redux:
https://redux.js.org/basics/usage-with-react
You some library for state management. The most popular one that's used with React is redux.
https://redux.js.org/introduction

react redux change query params with components

I have an isomorphic react application using redux,react-router, react-router-redux, I want to bind my components with url query params and when the query params changed, send request to an API and update my state and finally fetch data.
I know with react-router-redux it automatically update my state.
Also I want to show a pre-loader (spinner) to user when data is fetching.
what is the best way to write an action creator for this issue ? thanks
I want to show a pre-loader (spinner) to user when data is fetching
Pass a data prop to your component. When it's undefined, show a spinner.
const MyComponent = (props) => {
if (!props.data) return <Spinner />
...
}
When the query params changed, send request to an API and update my state and finally fetch data
The most simple solution (this can get quite complex depending on your needs) is to fire this logic on the Component's lifecycle hooks.
When the route changes react-router, with a typical configuration, will cause the component to render if you were previously on a different route, and update if you were previously on the same route (i.e. the same path, but with different query params).
So for the first case, use componentDidMount, for the second case, use comonentDidUpdate(prevProps, prevState) lifecycle hook.
To map the asynchronous API call to redux action(s), use a library like redux-thunk or probably more tailored to this use case, redux-promise-middleware. How to use those is a bit deep in scope for me to go into here, but they both have excellent documentation.
In the end, you'll want to map those actions in a reducer put the response data somewhere in your state tree, then using react-redux bind that location in the state tree to the data prop on your Component, as I mentioned before.

Handling long lived state in Redux

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.

Categories