15 ms·
Persisting React State in LocalStorage
- ipsum2 7y agoHeads up to the author, the code example is wrong. function useStickyState(defaultValue, key) should be function useStickyState(key, defaultValue)
- jbawn 7y agoLooks like it's called both ways: useStickyState("count", 0); Then later in the article: useStickyState('day', 'calendar-view');
- joshwcomeau 7y agoAh, good catch! Yeah, I changed this partway through, missed that! Thanks for the heads-up.
- XCSme 7y agoI was also very confused by this. Does anyone know why this article has so many votes? This is a trivial solution and there are many other articles explaining this exact thing.
- dinkleberg 7y agoBecause it brings attention to an interesting concept in a simple way. I'm starting to use React again after a year away using Vue and I've only just started to play with hooks. So it's cool to see other use cases. And even if it is a trivial solution, it brings up an interesting idea. Why this article and not the others? Because it was posted at the right time. I'm sure if another one of those articles was posted here (and was written in an equally approachable way) it would do just as well.
- XCSme 7y agoI think my opinion is biased because I know React pretty well and have a lot of JS experience so the article feels just like explaining syntax and does not cover important edge cases (eg. what if you have localStorage disabled? your hook will crash!). LE: I actually tested the site with Cookies disabled and the entire site crashed due to the localStorage calls.
- dudus 7y agoI remember people were abandoning localstorage a while ago because of some fundamental problem I can't quite remember right now. Not sure if it was difficulty in keeping in sync which the author mentions or the fact that it can't be scoped in a way to share among subdomains. I'm sure someone here will have a better memory than me. Edit: after some basic research among other complaints are: bad performance, synchronous, bad security model, only strings, limited to 5mb, no access to web workers. Localstorage has its uses but shouldn't be abused to store everything in it.
- tptacek 7y agoThe "bad security model" applies to storing things like tokens; it's an argument against using JWTs and localStorage as an alternative to secure cookies. It doesn't apply here.
- deleted 7y ago[deleted]
- Drdrdrq 7y agoRe web workers: is anyone (apart from Google) using this in practice? I assume Photopea and similar apps could benefit from it in some specific cases, but haven't had the need for them myself. Am I missing something?
- crubier 7y agoYes we use it for relatively heavy processing in our web app. To not block the UI and allow offline use
- stekern 7y agoI've experimented with using it together with TensorFlow.js to avoid blocking the UI thread while performing computationally expensive operations. Operations do take a bit longer to complete in a web worker compared to the main thread, but in return you get a smooth UI and a better user experience
- 7y ago
- the_gipsy 7y agoLocalStorage is tied to a device. The kind of settings described in the article should be tied to an account. Component state should only contain volatile data that is not important to save, the opposite of LocalStorage.
- anaclet0 7y agoTotally agree. I usually have a /userSettings api to store all the user customized UI settings (e.g. sidemenu collapsed or not, theme, etc.)
- PNWChris 7y agoEdit: I misread the parent comment, apologies for that! After re-reading, I still agree with them, but with some nuance. This article describes "somewhat" volatile state, and I think there are interesting use-cases for temporary sticky state that stays between visits (but doesn't need to be stored permanently). I see this being cool for search filter states and stuff. I'll leave my original comment as-is because I still thought that was worth sharing, too. -- I've put a lot of thought into easy, no DB persistence for a project of mine, and I have to agree with your assessment. I ended up persisting to the private fragment identifier (hash) portion of the URL. It makes bookmarking and copy-paste state sharing easier. Most major browsers have a bookmark sync feature these days, so state sync is free (a nice benefit!). While researching persistence options, I came across storage.sync[0]. It's like local storage, but synced across browsers. I've never used it, but the promised functionality would be very cool. I think storage sync is a really good idea. I just wish there was a "web browser state file system" of sorts for users to browse, copy, back-up, etc. In absence of that, I feel no user can be confident that local storage is working, and as a consequence I won't persist anything of consequence there. [0]: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/sync https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Web...
- pault 7y agoChrome has the FileSystem API, but they couldn't get the other vendors on board and it's basically dead now. https://stackoverflow.com/questions/23560090/is-the-fileapi-in-html5-dead https://stackoverflow.com/questions/23560090/is-the-fileapi-...
- kmf 7y agothis is great - i implemented something similar in a recent project, where we have some state being persisted "at the edge" (in cloudflare workers' key-value store product, workers kv) and cached in-browser using local storage. we wrote a bit about it here[1], though our approach[2] has changed a bit even since this post went out. instead of directly interfacing with window.localStorage, we opted to use lscache[3], which allows us to expire keys: `lscache.set(key, value, 5)` (expires in 5 minutes) [1]: https://blog.cloudflare.com/jamstack-at-the-edge-how-we-built-built-with-workers-on-workers/ https://blog.cloudflare.com/jamstack-at-the-edge-how-we-buil... [2]: https://github.com/cloudflare/workers.cloudflare.com/blob/master/src/components/bookmark_state.js https://github.com/cloudflare/workers.cloudflare.com/blob/ma... [3]: https://github.com/pamelafox/lscache https://github.com/pamelafox/lscache
- geocar 7y agoI'd recommend just putting this crap in the URL. That way (a) the server could see it too, and (b) users could share their state by sharing the URL (which makes supporting them easier!). If you've got so much state this doesn't make sense, then ¯\_(ツ)_/¯ I suppose caching it in localStorage is fine, but I haven't yet run into this problem. Having a setSticky((k,v) => that modifies the url with the history API is pretty easy, and if you get the results using the Context API, you can have your root monitor the URL (again using the history API) and publish the result using a context so you don't have a storm of updates.
- k__ 7y agoUser specific state like "dark mode enabled" would probably be better inside localStorage than inside a shareable URL.
- geocar 7y agoDon't browsers have media queries for things like that? I think some old browsers didn't, so I guess that's why there are all those tacky switches on blogs these days, but I don't usually worry about obsolete things like "made for netscape 4" banners... I also think for an application, I'd much prefer my settings on the server so I can get them again when I'm using another device.
- bobthepanda 7y agoCompatible with everything except IE 11, but in draft: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pref...
- ultrarunner 7y agoWe have an application that's used outdoors day and night in some cases. The good news is it makes a ton of difference in usability; the bad news is I honestly still have to search for instructions on toggling dark mode on Ubuntu. The switch is a lot simpler.
- EGreg 7y agoReact is a view layer tech You should be persisting state in the Data layer on the client The Data layer should be syncing with the servers or peer to peer once in a while
- ramenmeal 7y agoI don't think react claims to be "view" only anymore.
- capableweb 7y agoAFAIK, React does claim to solve "the view problem" and not more than that. You still need an architecture around your application if you're building something larger than a demo. Quote from the React landing page: > Design simple views for each state in your application Quite literally, one of the selling points of React is that it's focused on the view based on data, not on where that data comes from.
- brlewis 7y agolocalStorage is the data layer in this example. Components are the view. The hook is keeping the view and the data layer in sync.
- legym 7y agoThe Apollo React client does this for us
- code_biologist 7y agoCare to elaborate?
- ketzo 7y agoDamn, as someone currently trying to move from React classes to React Hooks, this does a pretty great job of showing the use case for Hooks. I have 10+ components across 5+ projects for which I'd use this hook.
- emilecantin 7y agoThe author briefly touches on a pet peeve of mine: > Every single time, I have to swap the default currency from USD to CAD. Why can't it remember that I'm Canadian?? US-based devs and English-speaking devs often forget about the rest of the world. Either it's bad or inexistent i18n, overly broad assumptions (e.g name=`${firstName} ${lastName}`) or just straight-up bad default values that can't be changed (like the example). When you're developing a SaaS, more often than not you're doing it for a global audience. Don't forget that.
- jsgo 7y ago> overly broad assumptions (e.g name=`${firstName} ${lastName}`) So an internal application I work on has communication between teams where first + last isn't a given. So I'm constantly paranoid if I'm going to get it right (usually just don't communicate name at all, just make statements). Fast forward to a month ago when I started working on a rewrite and while constructing the users (and actually making them users within a company instead of effectively companies themselves), I added an enum for NameOrder and separated the two fields. Not particularly important to the application in the grand scheme of things, but geez, it feels mildly freeing that it won't be so ambiguous in the future.
- lucasmullens 7y agoNames are pretty complicated, not sure that fully covers it. Many people have multiple last names, and how do you handle middle names? I've generally heard it recommend to just have a "Full Name" text box, but that wouldn't solve your use-case, so now I'm not sure.
- deleted 7y ago[deleted]
- ronyfadel 7y agoWouldn't it make more sense to stick this view persisted state in your Redux store? (and write it to LocalStorage with a throttle). Dan Abramov does it that way in [0] It would make handling your data much more sane (it all lives in one place, your Redux store), making it easier to sync (just sync your store's state), solve the key collision issues (with the author's implementation, separate components can overwrite each other's data). [0] https://egghead.io/lessons/javascript-redux-persisting-the-state-to-the-local-storage https://egghead.io/lessons/javascript-redux-persisting-the-s...
- jakelazaroff 7y agoNot all React applications use Redux.
- ristic 7y agoIt's a lot more setup but I've had a good experience with redux, redux-persist and localForage. localForage picks the best available browser storage API. One can select which subset of data to persist with redux-persist. It does assume a redux buy in...
- Lord_Zero 7y agoI just did this with Vuex and vuex-persistedstate. It was a drop in plugin in nuxt. Anytime a mutation happens state is written. I'm new to react but hooks look interesting.
- benatkin 7y agoI don't think key should be in the last part of here, because key should never change inside the closure: React.useEffect(() => { window.localStorage.setItem(key, JSON.stringify(value)); }, [key, value]); It's pretty neat because it has a simple and familiar API. I think it's more like sticky props than sticky state, though... Edit: here's another hook with a more accurate, less catchy name, plus error handling: https://usehooks.com/useLocalStorage/ https://usehooks.com/useLocalStorage/
- ricardobeat 7y agoThe ESLint rules for hooks enforce adding any values used inside the function to the dependency array.
- ficklepickle 7y agoI'm astonished at the lack of feature detection. I though that was common knowledge. Using feature detection solves the SSR issue and as a bonus that code won't error if localStorage doesn't otherwise exist (old browsers, tests). The best method of feat. detection of localStorage, IMO, is writing and reading back a test value in a try catch block. That will catch safari incognito which lies about support.
- longnguyen 7y agoFor SSR supports, use cookie instead of localStorage. But I would put a damn user preference flag in backend rather than solving it in the front-end.