Closed filipef101 closed 8 months ago
Some people may have mmkv instead of async storage, posthog could detect if async storage not present fallback to mmkv + option to prefer mmkv
We didn't want to end up with 100s of if statements trying to cover every possible dependency 😅 But you can just configure your own interface like so:
const options: PostHogOptions = {
customAsyncStorage: {
getItem: (key: string) => Promise<string | null>,
setItem: (key: string, value: string) => Promise<void>
}
}
And then you can use whatever storage you like :)
mmkv is sync, any chance to get a sync storage provider? to be able to have feature flags on first render/paint @benjackwhite
I'm not 100% certain but I would expect if you simply resolve immediately in those custom functions then it basically will be sync, especially from a first-render point of view?
The bridge is async by design, so even if you resolve/await a function, it does not guarantee the order of execution, it might be that something is executed before your awaited function (it's an event loop/single thread), that has also been awaited for.
A solution for all the async problems is to migrate to the RN new architecture and define when a method is async/sync or even have both. Since all the storage plugins are Native libs (android/ios), this would be the best/most reliable option.
For most cases, just awaiting the methods is enough, showing loading progress. At the same time, the calls get resolved, and after you decide what to do depending on the resolved feature flags, it's not necessary to be before the first render/paint, you can work around that by just using this suggestion for now.
@marandaneto I don't see any native code, so what needs to be migrated or am i missing something?
I'm not 100% certain but I would expect if you simply resolve immediately in those custom functions then it basically will be sync, especially from a first-render point of view?
@benjackwhite doesnt matter how fast it is, if you need to await it wont be available on first paint/render
@marandaneto I don't see any native code, so what needs to be migrated or am i missing something?
Right now we try to use expo-file-system
or AsyncStorage
, both are async by design.
We'd need to either change SemiAsyncStorage
or create an SyncStorage
type where all methods are sync. including the preloadAsync
.
Also create a customSyncStorage
instead of customAsyncStorage
, etc.
Also, use another file system plugin that supports the new architecture/JSI with only sync. methods, for example, react-native-mmkv
.
So it's a bit of refactoring on the TS code actually, not native code, plus supporting react-native-mmkv
.
Well, I am now about to use the feature flags feature, and I guess, this is not such an annoying issue?
The SemiAsync
implementation is actually sync, besides the preload async
. So if I write my own sync storage, I can follow the same API, and that function can be empty 🪄 💫
the only downside is that I stil have an unnecessaryly async "initAsync
"
If any change gets planned, I'd make the posthog-rn.ts
only have a storage implementation/declared, removing checks for "memory or semiAsyncStorage". making it configurable by passing 'async' | 'memory'
or 'custom'
or some object, or whatever web might also do for custom providers.
Move "workarounds" to the storage.ts
implementation i.e., the "removeItem
" check for nulls
here
Makes little sense that I need to call initAsync
if I'm using the memory provider, would also not make sense for a sync provider, so allowing for either initAsync
or initSync
to exist or make any "sync" setup part of the constructor when using sync providers, like memory, or JSI implementations like rn-mmkv
Making the implementation of the setupAsync
optional in the SemiAsyncStorage
type/class makes it perfectly suitable for both sync and async custom providers implementations :) (right now I'll write my own with that empty)
Maybe getting out of topic, but I don't understand the existence/use of the methods getAllKeys
and clear
on the SemiAsyncStorage
. There might be others, similarly the web cookieStore
also implements it but there is no usage. Tech debt/legacy?
This can be done with couple small changes:
new Posthog
initialization , an call it in the initAsync for async providers@filipef101 would you like to submit a draft PR with your suggestion and we take it from there? I am all in to guide/review your suggestions to make it better. I'd just try to keep compatibility with previous versions if possible.
Yes, already started!
@marandaneto are you able to take it from #170 ? Cheers
@filipef101 can you check if https://github.com/PostHog/posthog-js-lite/pull/189/ helps with your use case? It's not released yet though.
@marandaneto Step in the right direction, not there yet? Still will need to create a semiAsyncStorage with empty/immediate resolve preloadAsync to use mmkv as storage.
I feel that it is wrongly exposing a sync functionality, but in practice there will be a small delay before it is actually initialized, so for example using feature flags for conditional UI may "flicker" as first render a feature flag that we could expect to be cached is still undefined.
What happens if I try to log an event, or even access a feature flag, on first render?
Will the event sending be "delayed" until the _isInitialized
is true? or will it fail if anything happens before _isInitialized becomes true?
I'd be okay with closing. The point still stands as a very likely use case of using PH feature flags for conditional rendering on first paint, currently there is atleast 1 first render where it will always be undefined.
My current solution is to just use pg as is, and wrap the useFeatureFlag, in it's own hook that caches the flag with mmkv, ie when undefined return from cache if exists. Ideally I would want to not use async storage as I
(I also use it to nicely get the payload if exists instead of only the boolean value as afaik theres no useFFPayload hook)
If you init PostHog
with a custom customAsyncStorage
(mmkv for example), _semiAsyncStorage
is created but resolved async, indeed.
A call to isFeatureEnabled
or similar will return undefined
if not loaded yet, correct.
All calls to capture
will be executed after the _initPromise
is resolved if not yet, otherwise it's all sync calls (so just a bit of delay - probably a few ms if any).
You can call and await posthog.ready()
on a loading state or something if you wanna be sure everything is loaded.
I get your point, ideally if a sync storage is provided flags should not have returned undefined
nor you need to call ready()
on the first render.
@filipef101 would be keen to hear your input as someone with knowledge of rn-mmkv - Does it make sense to try and offer it as a built in option via peer-dependencies like we do with AsyncStorage?
I ask as from the docs it seems that there is a bit more potential config desired with mmkv in terms of scoping and namespacing it so it might not make sense to offer as a default but rather support via the new config option. Thoughts?
Closing this issue as resolved since it's possible to pass custom storage, we can raise a new issue based on the answer here.
Thank you all.
@marandaneto Thanks for considering the request and adding support for my use case. And all the time taken considering my suggestions and feedback. Very much appreciated. Rockstar work 🤘💫
@filipef101 would be keen to hear your input as someone with knowledge of rn-mmkv - Does it make sense to try and offer it as a built in option via peer-dependencies like we do with AsyncStorage?
I ask as from the docs it seems that there is a bit more potential config desired with mmkv in terms of scoping and namespacing it so it might not make sense to offer as a default but rather support via the new config option. Thoughts?
Yeah think it would make sense, nowadays RN has no async storage by default so it may become more common that people want to use it
I would not default to it if async storage is not found. Unless it was clear for users that they needed to migrate from async to mmkv. (That is also why i didnt ask for mmkv support on ph side as that introduces a whole new problem :) )
@marandaneto / @benjackwhite FYI a bunch of docs might need to be updated to use the new sync api ie:https://posthog.com/docs/feature-flags/installation?tab=React+Native
https://github.com/PostHog/posthog/pull/20854 https://github.com/PostHog/posthog.com/pull/8021 https://github.com/PostHog/posthog.com/pull/7902 We'll merge once its GA, let's give a few more days after the last beta with a few fixes.
Is this working? If so, is there any documentation on how to configure postHog for MMKV?
Is this working? If so, is there any documentation on how to configure postHog for MMKV?
Good point, https://github.com/PostHog/posthog.com/pull/8232
Just install the MMKV lib and create your own customStorage
.
Be aware that if you were using a previous storage form, the data won't be migrated so cached events, cached user id, etc won't be available.
I recommend calling identify
right away after the new storage.
Is your feature request related to a problem?
Would be nice if we could use MMKV instead of async storage
Describe the solution you'd like
Describe alternatives you've considered
Related sub-libraries
Additional context
Thank you for your feature request – we love each and every one!