-
Notifications
You must be signed in to change notification settings - Fork 15
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
feat: track 'last online' time in localStorage #974
Conversation
) | ||
} | ||
}, [online]) | ||
|
||
return { online, offline: !online } |
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.
Should we return lastOnline
in this hook?
// Only fetch if `online === false` as local storage is synchronous and disk-based
const lastOnline = !online ? localStorage.getItem(lastOnlineKey) : null
return {
online,
offline: !online,
lastOnline: lastOnline && new Date(lastOnline)
}
Or have a separate useLastOnlineHook
?
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.
oh yeah lol 🤦♂️
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 like adding it to this one 👍
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.
Interesting note: the way this is implemented currently using useEffect
to set the lastOnline
value in localStorage, that suggested code doesn't work to return the most recent lastOnline
value from the hook because there isn't a rerender once the value in localStorage is changed. I'll have to refactor a bit to couple the localStorage change with the online
state change so that the hook returns the most up-to-date lastOnline
value, ideally without another rerender
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.
What about using the global store instead of local storage?
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.
Using the global store would be a useful part of 'Single online status provider' solution that Austin mentions below, but using just the global store alone wouldn't be able to track the status across sessions
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.
When useOnlineStatus
is used potentially dozens of times across the app, this useEffect
will run for each instance and update the localStorage at that time. This isn't practical and could maybe even cause race conditions / stale data / performance issues (localStorage is synchronous).
Instead, I would recommend setting this localStorage value only if the saved state is out of sync (i.e. if state is null
when responding to type === 'offline'
, then and only then set the value to the last online time) in the debounced updateState callback. It should be read from localStorage in the render loop. (note there could possibly be performance implications from this on low-spec machines)
The best solution might be to implement a single global online/offline event handler which sets both the online status and the last online time in a global state provider (reading and writing to localStorage at that time) and which would then be consumed by the useOnlineStatus
hook. This should reduce the possibility of race conditions. I realized that's a bit of a refactor though, might be something to look into in the future. It would also possibly allow for manually setting online/offline time from that central state handler in the future
Thanks for the feedback @amcgee! What you describe in the second paragraph sounds like the direction I was going with a refactor I mentioned above, and I pushed a change that I think implements what you describe -- did I understand you correctly?
I agree that a single handler/provider would be best and I think we should go for that in another iteration 👍 |
@@ -50,5 +66,12 @@ export function useOnlineStatus( | |||
} | |||
}, [updateState]) | |||
|
|||
return { online, offline: !online } | |||
// Only fetch if `online === false` as local storage is synchronous and disk-based | |||
const lastOnline = !online && localStorage.getItem(lastOnlineKey) |
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.
minor: useMemo
might help mitigate possible performance issues here - without it any time the parent rerenders it might create a new options object and cause an unnecessary localStorage lookup - so long as we can assume the value of localStorage.getItem(lastOnlineKey) will never change once offline
is set.
const lastOnline = !online && localStorage.getItem(lastOnlineKey) | |
const lastOnline = useMemo(() => !online && localStorage.getItem(lastOnlineKey), [online]) |
So here's something I noticed: if you're offline and open and app, and useEffect(() => {
if (!online && !localStorage.getItem('lastOnline')) {
localStorage.setItem(etc.)
}
}, []) but then I'm not sure what the most ideal way of updating the |
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.
Haven't tested but code looks good!
@KaiVandivier but what would we even set |
I was thinking just |
# [2.10.0](v2.9.2...v2.10.0) (2021-08-30) ### Features * track 'last online' time in localStorage ([#974](#974)) ([98d7cd3](98d7cd3))
🎉 This PR is included in version 2.10.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
This feature can be used by apps or the header bar to display the 'last online' status. It's stored in
localStorage
so that the value can persist between sessions and be used between apps (although it will only be updated in apps that use PWA or use the most recent UI with the new header bar).In the tests, I tried mocking the
Date
class andDate.now()
, but both approaches either had issues with typescript or would make the test fail for mysterious reasons :(