-
Notifications
You must be signed in to change notification settings - Fork 19
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
Feedback + Suggestions for React Interface #631
Comments
Hello @leggomuhgreggo, Thank you for the feedback. For some of these items I can provide background and insight. The main thing that will drive a number of these items is driving LaunchDarkly features. The evaluated value of a flag is what you consume in your application, but the side-effects of those evaluations drive features (experimentation, flag insights, stale flags, fallback values, etc.). Some aspects of the interface allow these features to be driven better than they were in the previous react SDK. The first item is heavily influenced by this. The old react SDK When you evaluate a flag we drive features by knowing:
Eventually we figured out complex ways to get this data from In the new SDK we removed all flags from the path of least resistance and instead provide individual flags as the path of least resistance. You still can get all the flags from the client, but doing so means you need to be aware of the potential problems and features that do not work. This is also the answer to another of the questions For the async/sync aspects, there it may be unclear what the SDK does. We connect to LaunchDarkly and receive updates as they happen. If a flag changes then the hook result will be different and changes propagate. There is a "stale" period between identify of one context and the next context. If you prefer the most recent results, once that identify completes, then you can use the As flag values change the various hooks will propagate those changes.
Yes, we generally call this bootstrap. It is currently being developed for our browser implementation which shares much of the RN code. The identify call in this case will take a In regard to the typing of the useVariation methods, this partially fits under the "The detailed reasons for the evaluation." as well as how we handle default values. It also has to do with JavaScript versus typescript. When you write generic methods the typing information exists at compile time, but it is erased at runtime. The type of the individual flag is only known at runtime. When you used typed variation methods we return the default value if the type at runtime does not match the expected type. We also include this information into analytics events. These methods also allow a boolean typed evaluation, with defaults and alanytics events, while using JavaScript. Sometimes in JavaScript you want the value to actually be a specific type, and you want the default when it is not that type. I agree that some code generation would be useful, that wouldn't be a feature of this SDK specifically though. For requesting that type of product feature our support process is a better avenue. Thank you, |
An additional note on the "up to date value". The identify promise is the thing that indicates that the operation is complete. With the caching behavior controlled by Because the identify operations can be asynchronous to other things that are happening I am unsure how we would bring this to the level of an individual evaluation. We don't know the relationship between the two operations. For instance imagine you have this set of operations. In your auth service: In some UI component: Which operation does the useVariationSomeNewThing wait for? The application managing the relationship of the identify promise, versus the component, would determine this. There are many "correct" options.
The case you demonstrated was just whatever one happens to complete, but that isn't always the situation. One option may be to provide a something like Identify is very similar to managing a login in many applications. The UI may be different, and parts may be gated, until the login is complete. In such applications it is easiest to potentially re-use the mechanism already propagated for auth. Or to make your own hook that informs you of the state you are concerned with. Thanks again, |
Overview
Initially, I intended to suggest aligning the SDK more closely with the existing React SDK's API. However, while drafting this issue, I realized there might not be enough commonality for there to be a clear case for this.
So instead I will just offer some general feedback, as someone who's been using LD on a cross-platform expo project for a few years, and who is also excited for the recent TS-first + Universal React direction y'all are taking.
I should say up front that, while I have thought a bunch about this topic, I come from a place of ignorance, and it's possible I am overlooking tradeoffs. I figure y'all are already on top of things, but hope it's relevant enough to be useful to some degree.
Thanks!
For reference
Current React SDK Hooks
The
launchdarkly-react-client-sdk
exports the following hooks:Current React Native SDK Hooks
The
@launchdarkly/react-native-client-sdk
exports:Single Flag vs All Flags Hooks
Recap
useFlags
hook from the React SDK returns all flags at once.useTypedVariation
hook (and its derivatives) from the React Native SDK returns a single flag.Preference for Single-Flag Approach
My preference is the single-flag approach, as it offers more granular control and helps avoid unnecessary re-renders. However, there are scenarios where the "all-flags" approach has its benefits—particularly when dealing with feature overrides.
Use Case for All-Flags: Feature Overrides
We use overrides extensively for various purposes, including:
If you have multiple "layers" of overrides, corresponding to different "sources," and you need to manage them as a group, having an "all-flags" hook becomes very useful. This is difficult to achieve with a single-flag interface alone.
Example: Flag Source Layer Priority
.env
-like file for local developmentdefaultValue
as a fallback argumentThis "maximalist" approach isn't unrealistic
Why Not Just Return All Flags?
An argument could be made that returning all flags simplifies things. However, doing so introduces patterns that can lead to re-render issues. Therefore, I believe the atomic, single-flag approach is more sensible as the primary usage pattern.
That said, having a way to access all flags is a legitimate secondary pattern, especially in scenarios involving overrides.
Type-Specific Hooks?
My impression is that the type-specific hooks are sugar around the
useTypedVariation
hook — perhaps vestiges of conventions drawn from the more "type-first" native android/ios ecosystems?I'd suggest that it is more idiomatic to control types via TS, eg
Where the return type, if not specified via generic arg, defaults to a union of the possible options
I think this simplifies the coupling to default value in resolving type [code]
And it has the advantage of reducing the surface area and making adoption a friendlier proposition.
Consider: Standard React Hooks
If we consider the standard react hooks, they enable types through generics/overloads — but still leave this optional.
This makes it easy to have strong typing, but still leaves it accessible for teams that aren't as strict about TS observances.
Here's the TS definition for
useState
as an example.Default Value Usage?
This is a nuanced question — but let's consider the differences between these approaches:
vs
The fist delegates defaultValue to user-land
The second makes the defaultValue a part of the standard API*
*even a requirement, in the current implementation
Global Defaults?
Another option is to provide a "flag defaults" config to the LD init — so that the default for a given flag is consistent across usages.
Suggestion
I'd suggest that this is probably the ideal usage pattern / interface ⭐
Personally, I would not use the
defaultValue
argument — preferring instead to have global defaults — but it's there for those who prefer it.Async vs Sync
There are certain situations where it's essential to resolve an up-to-date value from LD, and not use the cached/default version.
Currently the way to handle this is something like:
However, this approach can be cumbersome.
I'd suggest this usage indicates adding support for a sort of async style interface, similar to modern API client libraries like
react-query
andurql
Indeed it may even be worth adding support for "request policy" eg (from
urql
docs)Future Consideration: Codegen?
It would be great to generate the type definitions for flags, as part of the dev/ci workflow.
Of course, there's nothing stopping users from doing this currently, but it would be great if there were official feature support. (perhaps it's already on the roadmap!)
I mention this because I think this possible eventuality might inform design choices around type handling — and also possibly generated default values.
The text was updated successfully, but these errors were encountered: