I have a react app with redux state which is stored in localStorage
The problem is that data structure has changed and some older users get an error. Need a reset.
If I add localStorage.clear() to the code, it will run every time and there will be no point of using storage it at all.
Is it possible to reset localStorage only once with kind of specific flag passed with deploy? Something like css files which have versions, i.e. .css?412uisqfh9-wq different for deploys
You can run localStorage.clear() only once.
What you need to do like below:
if (localStorage.getItem("clearStorage") === "<old version number>") {
localStorage.clear();
localStorage.setItem("clearStorage", "<new version number>");
}
In first run only if condition code will execute and after that everytime this condition will be false and will never execute.
Related
I am running an application via VSCode's Live Server extension.
This application (e-commerce website) saves items to a cartItems array that gets written to localStorage whenever an addToCart button is clicked. I can see in DevTools/Application/LocalStorage that the items are being saved correctly when the specified actions are taken.
However, my localStorage is cleared whenever I refresh the page or navigate to a different page within the same application.
When writing items to localStorage, I have tried invoking the localStorage short-hand, as well as the more explicit window.localStorage. Neither has worked for this issue.
Does localStorage not persist when an application is run locally or via a live server? I suspect it should and I am doing something else incorrectly.
The MDN documentation mentions:
The read-only localStorage property allows you to access a Storage
object for the Document's origin; the stored data is saved across
browser sessions. localStorage is similar to sessionStorage, except
that while data stored in localStorage has no expiration time, data
stored in sessionStorage gets cleared when the page session ends —
that is, when the page is closed. (Data in a localStorage object
created in a "private browsing" or "incognito" session is cleared when
the last "private" tab is closed.)
Could it be that each refresh or page change on live server represents an entirely new device history? (Again, this feels plausible but still unlikely. I am happy to be wrong, however.)
Thank you in advance for any guidance you can provide.
EDIT - Here is some of the relevant code:
In the global scope:
const cartItems = [];
localStorage.setItem('cartItemsKey', JSON.stringify(cartItems));
localStorage.getItem('cartItemsKey');
const cartItemsInStorage = localStorage.getItem('cartItemsKey');
Encapsulated - this function fires when an addToCart button is clicked. Omitting most of it, except the part pertaining to localStorage, for brevity:
function addToCart (button) {
// Create newCartItem object using relevant data
cartItems.push(newCartItem);
localStorage.removeItem('cartItemsKey');
localStorage.setItem('cartItemsKey', JSON.stringify(cartItems));
}
Again, I can see that the items are successfully being saved to localStorage, per the below screen recording. It's when I refresh or change pages that I have trouble.
Update:
I figured out what the issue was.
I have the following code in the global scope:
const cartItems = [];
localStorage.setItem('cartItemsKey', JSON.stringify(cartItems));
localStorage.getItem('cartItemsKey');
const cartItemsInStorage = localStorage.getItem('cartItemsKey');
Which gets called before this code:
function addToCart (button) {
// Create newCartItem object using relevant data
cartItems.push(newCartItem);
localStorage.removeItem('cartItemsKey');
localStorage.setItem('cartItemsKey', JSON.stringify(cartItems));
}
So I was effectively re-writing an empty array into localStorage every time the page reloaded.
I have resolved this issue by removing the .setItem line as follows:
const cartItems = [];
localStorage.getItem('cartItemsKey');
const cartItemsInStorage = localStorage.getItem('cartItemsKey');
Now, I will rewrite my initial const cartItems = []; as a conditional statement, where I first check to see if cartItemsKey exists in global storage. If so, I will set cartItems = JSON.parse(localStorage.getItem('cartItemsKey'). Otherwise, I will set cartItems = [];.
Thank you very much, #Alex Rasidakis, for your kind help.
Can you provide us with some code? I don't think that the Live Server has anything
to do with the local storage malfunction you are talking about.
I'm pretty sure it's a browser issue. Maybe you are using hard refresh somehow
each time you are refreshing. Maybe you have some setting that clears the local storage or some browser extension that cleans your page every time you refresh.
Try to open your localhost address from another browser like Mozilla, Edge etc.
and tell us about its behaviour.
i am using ionic 3 and i am trying to get value on the previous page but the constructor is not working, i also tried ionViewDidEnter(){} and
ionViewDidLoad(){}
i was moving my value through local storage and was calling it in the constructor and ionViewDidEnter(){}
ionViewDidLoad(){} of the previous previous page
this.locationvalue =localStorage.getItem('locationName')
console.log(this.locationvalue);
localStorage.removeItem('locationName');
I am using google chrome to application mode to read my localStorage and i am able to read my value there.
1) Perhaps have a look at NavParams as a better solution to local storage for this.
2) If not applicable because of multiple updates, I suspect if you are using local storage and you are NOT using a Promise with NavPop inside the then().
local storage service:
removeItem(...):Promise<any> {
return new Promise((resolve) =>{
...
}
}
nested page doing return:
goBack():void {
localStorage.removeItem('locationName').then(this.navCtrl.pop());
}
You have to realise the local storage update is asynchronous and does not block. So by the time you return to the prior page and render it, the local storage update, which I presume your returned to page is relying on to bind the data you display, will not have completed its update.
The underlying JavaScript Event Loop at the foundation of this.
Take a loop at this video.
3) Finally if you are confused by the Ionic Page lifecycle, this blog post may help too.
Let me know if this helps.
componentWillUpdate() {
//Axios GET method for getting all the trades
//Adding data of db into mobx store/or syncing data of db and store
axios.get(`http://localhost:8091/trade`)
.then(res => {
this.props.store.arr = res.data;
})
}
This piece of code causing my browser to crash, my laptop to not responding.
Actually, whenever i was trying to delete the row of table containing trade by click of button then the trade was deleted but it took the need of refresh to see that the trade is deleted.
This was because my mobx store and db were not in sync.So as soon i refresh the (REST api) controller updates data in my mobx store.After this i can see that trade is deleted.
So in order to remove the need of refresh i thought to use component will update method.Within that method i tried to sync mobx store with controller data (db data).It worked but it caused the browser to take more than 2.5 gb of memory & at this point all the running applications starts getting crashed also.
So what is the good way to achieve the desired result?
Note i don't know why component will update is getting called too many times.
But i can verify the it because i can see the selection statements(of database) in spring (my server which is sending data to controller ).
Putting the above code inside component did mount is not removing the need of refresh but it is not causing the browser to crash also.
You should not be doing this type of operation in this lifecycle hook. You should use componentDidMount instead for any remote calls that need to happen. However since you are using mobx, you really should not be having these problems as they handle these type of problems for you with the observer pattern. Please read: https://mobx.js.org/getting-started.html to get up to speed and you should have no issues at that point.
Something in your component's props or state is causing it to update often, which is causing a lot of calls to the api. Your best bet would be to find out what is causing those updates and use nextState and nextProps arguments supplied to componentWillUpdate to check and send an api call only when needed. Something like:
componentWillUpdate(nextProps,nextState) {
if (nextProps.needToGetApi !== this.props.needToGetApi) {
//Axios GET here, so that unrelated prop/state change does not cause this to run
}
}
Hint: Add a breakpoint in componentWillUpdate and see what props or state mutations are happening on each call.
I have very large and complicated objects like userInfo, chatInfo, and etc as in objects & arrays with very large and nested information. The thing is in my react app every time I refresh my page the redux state gets reset and I have to call all those API's again.
I did some research on this topic. I checked Dan Abramov's egghead tutorial on redux. What he does is maintain the redux state in localStorage of the browser and updated the localStorage after every 100 or 500 ms. I feel as if this is a code smell.
Continuously watching the localStorage state and updating it, wouldn't it effect the performance of the browser. I mean wasn't this on of the reasons Angular 1 failed because it continuously kept on watching state variables and after a while if the site was kept live in the browser it just slowed down. Because our script continuously kept on checking the state of the variables. i feel as if we are doing the same thing here.
If maintaining the redux state in localStorage is the right approach can someone tell me why so? And if not is there a better approach?
This is not a duplicate of How can I persist redux state tree on refresh? because I am asking for advice whether persisting state in local storage is a code smell or not
I think using localStorage is your best option here, since it seems the data you are storing there is needed on the client. If the data is not changing, you shouldn't need to repeatedly query, or watch, the localStorage.
Another thing you can do is wrap a closure around your localStorage, so that you are not always hitting disk when retrieving your "large" data. Every browser implements localStorage differently, so there are no guarantees on consistent behaviour or I/O performance.
This also adds a simple layer of abstraction which hides the implementation, and controls everything related to your user data in one place.
Here is a simple example of user profile data closure:
// UserProfile.js
var UserProfile = (function() {
var userData = {};
var getUserData = function() {
if (!userData) {
userData = JSON.parse(localStorage.getItem("userData"));
}
return userData;
};
var setUserData = function(userData) {
localStorage.setItem("userData", JSON.stringify(userData));
userData = JSON.parse(localStorage.getItem("userData"));
};
return {
getUserData: getUserData,
setUserData: setUserData
}
})();
export default UserProfile;
Set the user data object: This will overwrite the localStorage, and set the local variable inside the closure.
import UserProfile from '/UserProfile';
UserProfile.setUserData(newUserData);
Get the user data object: This will get the data from the local variable inside the closure, or else go get it from localStorage if it is not set.
import UserProfile from '/UserProfile';
var userData = UserProfile.getUserData();
The idea here is to load data into memory, from localStorage, the first time, when your app loads, or on the first API call. Until such a time when the user profile data changes, (i.e. a user updates their profile, for example), then you would query the API again, and update the data again via the UserProfile.setUserData(..) call.
The question is at what point you need to achieve persistent. I feel that the answer in your case is on a page reload. So if you are worry about performance I'll say:
* Update the localStorage only when the state changes. Inside your reducer when you update the state.
* Read from localStorage when you boot the app.
(This way you write only when the state changes and you read only once)
P.S.
I'll recommend https://github.com/rt2zz/redux-persist package for achieving persistent in Redux apps.
I would only do this as a weak cache and would not rely on it. Local storage is limited (5mb on Chrome e.g.), and may not be available. You'd have to carefully verify that your data was written.
As others have said, you wouldn't be watching localStorage you'd be periodically flushing the store. But I would agree that it seems like a rather coarse hack to blindly assume that all of your state was appropriate to persist in local storage. As with all caching solutions, you need to carefully consider the implications (freshness, expiration, etc.). It sounds like you might want to do this incrementally - pick off a few low-hanging pieces of fruit that are appropriate for caching and consider the implications of caching that state in local storage.
Try redux-persist you can do optimistic persistence, agnostic of the underlying platform (web/mobile).
If you still find performance is a bottleneck. You can do either of the following in steps.
Cache Middleware
Create a middleware that listen's in on changes and records them but only flushes them out every 5 seconds.
Attach an event handler to window.beforeunload to detect user navigating away or closing the browser to flush changes
Persistence Strategy
To persist the data you can use a either of the two strategies below.
Store it in localStorage if there is no performance bottle neck.
Send a JSON blob to server like file-upload. Fetch last JSON state when app loads and persist locally.
I'd suggest you start of with using redux-persist. If there is still a performance bottle neck then use a cache middleware along with one of the two persistence strategies.
I think that in most cases you want to persist your state in localStorage after certain action(s) happens. At least that's always been the case in the projects where I've needed to persist it.
So, if you are using redux-saga, redux-observable or redux-cycles for orchestrating your side-effects you can easily make that side-effect (persisting the state into localStorage) happen whenever one of those actions take place. I think that's a much better approach than "randomly" persisting the data based on a time-interval.
It seems that your understanding of watching state is that you have some kind interval which will keep checking your state and updating it in localStorage. However, I think you can achieve this same thing by updating your localStorage in the react lifecycle method componentDidUpdate. This method will fire every time your component updates, so you can take advantage of it and update your localStorage every time it fires without incurring any performance hits.
One option is to load the data in INITIAL_STATE
window.__INITIAL_STATE__ = { ...state... }
and load the reducer:
window.__APP_STATE__.__REDUCERS__.push(reducer)
It is completely ok to persist state in large redux+react applications.
Regarding angular1, watchers and digest cycle though on every state change even if you rehydrate state not all components are rendered because of redux connect API and vrtualDOM of react.
You can check:
https://github.com/redux-offline/redux-offline
https://github.com/rt2zz/redux-persist
Performance should not be your primary concern. If it comes to that normalise your state and only persist important information like drafts etc(Less info-fewer rehydrations).
The bigger problem with this kind of setup is usually if you have any background sync or socket updates in the app. Having multiple tabs of browser causes async writes to local DB causing to overwrite with previous states.
Here is a thin wrapper on top of it for cross tab sync redux-persist-crosstab
You can check some of these implementations in mattermost webapp and how they use it for realtime app.
They go through some hoops for extra stability and performance - link to store file in mattermost webapp
I think you can use npm module called redux-storage. It provides Persistence layer for redux with flexible backends. You can combine redux-storage with any redux-storage-engine you want. And in my view you can combine this with redux-storage-engine-sessionstorage assuming you will only want to share and save information till browser is open. No need to bloat browser with localStorage. You will need extra javascript support for this to get it clear.
Redux-storage will trigger load and save actions after every state changes. Providing all more flexibility about what to do in case of load and save. Also if you don't to save some state changes then you define array of state which you want to filter out.
I am using the 'chrome.storage.sync.get/set' to save my data. My question is, will the data saved using this API will get reset when I close chrome browser window and open it again. Is there any method to clear the data saved when we exit from chrome? I know there is a method 'chrome.storage.sync.clear()'. But this should happen only when the user exits from chrome.
will the data saved using this API will get reset when I close chrome browser window and open it again
No, it's saved — not just locally, but also potentially synced to the user's Google account. That's the point of the sync storage area. The documentation says sync is like localStorage, but synced to the user's account.
If you're using it for transient data, don't. Use local instead (as there doesn't appear to be an extension equivalent of session storage) or perhaps just store the data in your extension's runtime environment (it's not clear what you're using this for).
I know there is a method chrome.storage.sync.clear(). But this should happen only when the user exits from chrome.
You'd have to trigger clear from an event. Unfortunately, there is not currently an official way for an extension to get notified when Chrome closes (there's an open issue on it). Hopefully there will be a way at some point, but there isn't currently.
You're right chrome.storage.sync.clear(), removes all storage from sync. However, if you want to use that function when the browser 'exists', you'll have to look at the Processes Api.
You would probably want to use this function (from the documentation) :
chrome.processes.onExited.addListener(function(){...});
It takes processId as one of the arguments. So you would just have to get hold of the processId for the browser process. I found a relevant answer for this on SO only. It suggests you do something like:
//Basically loop through all the processes to get the browser's Process Id
var browserId;
chrome.processes.getProcessInfo([], false, function(processes) {
processes.forEach(function(process) {
if (process.type === 'browser') {
browserId = process.id;
}
});
});
Once you have the browser's Process Id, you can add a listener to the exit event and do the needful. I hope it gets you started in the right direction.