-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix use local storage #786
Conversation
Hihi, Although I only filed the one bug (#785) I was also running into other issues while trying to use this, like it over-serializing values ( Running my test suite over the original hook gave these results: Not sure why so many of the earlier ones are ❌, perhaps as a consequence of I'm more than happy to take a couple days to work with you to tighten the bolts on this one and get it ready to go! Let me know what else you'd like to see from me. I also previously contributed #557 so I'm a little oversensitive to the |
src/useLocalStorage.ts
Outdated
[state, raw] | ||
); | ||
|
||
useEffect((): void => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could probably swap this out for useEffectOnce
Hi @TylerR909 , Could you please rebase your branch from master. It seems like there have been more changes to the Thanks |
Just ran into #785 and unfortunately have to find another implementation until this is merged. Be great to get it in there - let me know if I can help. |
I'll try to check this out in the next couple days and get the PR updated. Need to remind myself what I was doing when I opened it 2 months ago 🤷♂ |
src/useLocalStorage.ts
Outdated
return [initialValue as T, () => {}]; | ||
} | ||
if (!key && (key as any) !== 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can it be a boolean false?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I can protect against that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what I was asking here looking back.
src/useLocalStorage.ts
Outdated
}); | ||
let localStorageValue: string | null = null; | ||
try { | ||
localStorageValue = localStorage.getItem(key); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this expensive to be reading local storage every render?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really. If you're trying to hit 60fps that gives you 16ms between rerenders for all your code to run.
From various benchmarks I'm seeing, a single read takes 0.0004-0.0010ms. Completely negligible.
If reading localStorage is someone's bottleneck, they have other issues they need to figure out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah interesting. I've seen multiple tutorials making a hook like this that cite reading local storage as being synchronous and expensive as the reason for wanting to make a hook. I assumed that was based on reality but it must have just been used as a contrived example.
I started on a PR that used an object store to keep all the hooks in sync while caching the data but if it's not actually an issue that is over-optimization
IMO because reading from local storage is expensive, we should not be reading it on every rerender. I think since we want to keep it in sync we should cache the keys in an redux-like object shared by all instances of the hook rather than local state. |
Most of the way through updating this locally. I had added a bunch of tests cases back in November and some of the new Serializer stuff is missing the marks (feeding me back [object Object] instead of just... the string) and I've already spent a couple hours on a Saturday trying to resolve merge conflicts. I'll circle back on this tomorrow and try to finish updating it. |
@TylerR909 One test case you can add that I think your diff fixes is that setting local storage works if it is called after unmount.
|
Okie doke. Think this is mostly updated for now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I've rebased this onto v14.0
branch, I've kept the tests and updated the useLocalStorage
in v14.0
branch to pass those.
The exception is one test, which arguably is the crux of this PR, to test that two hooks stay in sync as one updates. The reason is, because I could not understand the sync logic. As I understood it, the useLocalStorage
hooks would somehow re-render and every time pick the latest value from localStorage
—that would keep them in sync. I'm not sure that would work in general case, I would expect that useLocalStorage
use some pub/sub or event emitter mechanism under the hood to notify all instances that some key has changed.
My concern is that someone is going to have the bright idea to come in here and "Optimize" useLocalStorage, all the tests are going to pass, and we're going to see a regression for something we specifically fixed in this PR. That is the test that is supposed to prevent this. I disagree with commenting that test out. Even if someone changes the implementation to pub/sub, that test as written should still pass.
Based on what? I did some research when someone else brought up the same concern and was seeing localStorage reads take 0.0004-0.0010ms which is basically negligible if you're targeting 60fps. If you have 20,000 components reading localStorage and it's going slow, your problem is not the speed of localStorage.
Yeah, not my best code in the world. Basically it used to use Once you break it down into I don't recall what I was trying to use this hook on back in November when I made these updates but it rendered the hook entirely useless to me because one component was setting localStorage when a query string changed (or something) and some View component absolutely refused to see the change until remounting. So yes this is an important test to include. Even if the implementation changes. |
Fixes streamich#785 - useLocalStorage not updating if many components watch the same key Previously pr streamich#786 addressed this issue by ensuring that other components watching the key would see new updates, however, those updates would not be rendered until something else triggered a re-render. This pr resolves that issue. This pull request depends on pull requests streamich#1021 streamich#979
Fixes streamich#785 - useLocalStorage not updating if many components watch the same key Previously pr streamich#786 addressed this issue by ensuring that other components watching the key would see new updates, however, those updates would not be rendered until something else triggered a re-render. This pr resolves that issue. This pull request depends on pull requests streamich#1021 streamich#979
Fixes streamich#785 - useLocalStorage not updating if many components watch the same key Previously pr streamich#786 addressed this issue by ensuring that other components watching the key would see new updates, however, those updates would not be rendered until something else triggered a re-render. This pr resolves that issue. This pull request depends on pull requests streamich#1021 streamich#979
Fixes streamich#785 - useLocalStorage not updating if many components watch the same key Previously pr streamich#786 addressed this issue by ensuring that other components watching the key would see new updates, however, those updates would not be rendered until something else triggered a re-render. This pr resolves that issue. This pull request depends on pull requests streamich#1021 streamich#979
🎉 This PR is included in version 15.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Fixes streamich#785 - useLocalStorage not updating if many components watch the same key Previously pr streamich#786 addressed this issue by ensuring that other components watching the key would see new updates, however, those updates would not be rendered until something else triggered a re-render. This pr resolves that issue. This pull request depends on pull requests streamich#1021 streamich#979
Fixes streamich#785 - useLocalStorage not updating if many components watch the same key Previously pr streamich#786 addressed this issue by ensuring that other components watching the key would see new updates, however, those updates would not be rendered until something else triggered a re-render. This pr resolves that issue. This pull request depends on pull requests streamich#1021 streamich#979
Addresses #785
Description
Basically a total rewrite of
useLocalStorage
to ensure updates to the keyed value are picked up by all components watching for it.Type of change
I don't believe this will cause any breaking changes. But I did end up tearing out almost all the old code and fundamentally changed how this hook works. 🤷♂
Checklist
yarn test
)yarn lint
). Fix it withyarn lint:fix
in case of failure.yarn lint:types
).raw=true implies value=string