-
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
chore: Provider and hooks #306
Changes from all commits
4454fd7
d8a1801
5b0c2d4
d96c415
8602f39
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,17 @@ | ||
import { CLIENT_SIDE_SDK_KEY } from '@env'; | ||
import React, { useEffect, useState } from 'react'; | ||
import { StyleSheet, Text, View } from 'react-native'; | ||
|
||
import { init, type LDClientImpl } from '@launchdarkly/react-native-client-sdk'; | ||
import { LDProvider } from '@launchdarkly/react-native-client-sdk'; | ||
|
||
const context = { kind: 'user', key: 'test-user-1' }; | ||
|
||
export default function App() { | ||
const [ldc, setLdc] = useState<LDClientImpl>(); | ||
const [flag, setFlag] = useState<boolean>(false); | ||
import Welcome from './src/welcome'; | ||
|
||
useEffect(() => { | ||
init(CLIENT_SIDE_SDK_KEY, context) | ||
.then((c) => { | ||
setLdc(c); | ||
}) | ||
.catch((e) => console.log(e)); | ||
}, []); | ||
|
||
useEffect(() => { | ||
const f = ldc?.boolVariation('dev-test-flag', false); | ||
setFlag(f ?? false); | ||
}, [ldc]); | ||
const context = { kind: 'user', key: 'test-user-1' }; | ||
|
||
const App = () => { | ||
return ( | ||
<View style={styles.container}> | ||
<Text>{flag ? <>devTestFlag: {`${flag}`}</> : <>loading...</>}</Text> | ||
</View> | ||
<LDProvider clientSideSdkKey={CLIENT_SIDE_SDK_KEY} context={context}> | ||
<Welcome /> | ||
</LDProvider> | ||
); | ||
} | ||
}; | ||
|
||
const styles = StyleSheet.create({ | ||
container: { | ||
flex: 1, | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
}, | ||
box: { | ||
width: 60, | ||
height: 60, | ||
marginVertical: 20, | ||
}, | ||
}); | ||
export default App; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { StyleSheet, Text, View } from 'react-native'; | ||
|
||
import { useBoolVariation } from '@launchdarkly/react-native-client-sdk'; | ||
|
||
export default function Welcome() { | ||
const flag = useBoolVariation('dev-test-flag', false); | ||
|
||
return ( | ||
<View style={styles.container}> | ||
<Text>Welcome to LaunchDarkly</Text> | ||
<Text>{flag ? <>devTestFlag: {`${flag}`}</> : <>loading...</>}</Text> | ||
</View> | ||
); | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
container: { | ||
flex: 1, | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
}, | ||
box: { | ||
width: 60, | ||
height: 60, | ||
marginVertical: 20, | ||
}, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { useBoolVariation, useBoolVariationDetail } from './useBoolVariation'; | ||
import { useJsonVariation, useJsonVariationDetail } from './useJsonVariation'; | ||
import useLDClient from './useLDClient'; | ||
import { useNumberVariation, useNumberVariationDetail } from './useNumberVariation'; | ||
import { useStringVariation, useStringVariationDetail } from './useStringVariation'; | ||
import { useVariation, useVariationDetail } from './useVariation'; | ||
|
||
export { | ||
useLDClient, | ||
useVariation, | ||
useVariationDetail, | ||
useNumberVariation, | ||
useNumberVariationDetail, | ||
useBoolVariation, | ||
useBoolVariationDetail, | ||
useStringVariation, | ||
useStringVariationDetail, | ||
useJsonVariationDetail, | ||
useJsonVariation, | ||
}; |
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,13 @@ | ||||||||||||||||
import useLDClient from './useLDClient'; | ||||||||||||||||
|
||||||||||||||||
export const useBoolVariation = (flagKey: string, defaultValue: boolean) => { | ||||||||||||||||
const ldClient = useLDClient(); | ||||||||||||||||
return ldClient?.boolVariation(flagKey, defaultValue) ?? defaultValue; | ||||||||||||||||
}; | ||||||||||||||||
|
||||||||||||||||
export const useBoolVariationDetail = (flagKey: string, defaultValue: boolean) => { | ||||||||||||||||
const ldClient = useLDClient(); | ||||||||||||||||
return ldClient?.boolVariationDetail(flagKey, defaultValue) ?? defaultValue; | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because detailed variations return objects we need to ensure we keep stable object references. Which may happen for flags that exist, because we might be returning a value we have stored. I am concerned that the variation for a default value will not be stable. Which could lead to the hook triggering more re-renders than desired. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok this is definitely a bug. What do you think of a fix like this:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, this fixes a case where the ldclient isn't initialized to have a consistent return. It doesn't affect the stability of the reference returned by the hook. I think you would need to make a memo. |
||||||||||||||||
}; | ||||||||||||||||
|
||||||||||||||||
export default useBoolVariation; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import useLDClient from './useLDClient'; | ||
|
||
export const useJsonVariation = (flagKey: string, defaultValue: unknown) => { | ||
const ldClient = useLDClient(); | ||
return ldClient?.jsonVariation(flagKey, defaultValue) ?? defaultValue; | ||
}; | ||
|
||
export const useJsonVariationDetail = (flagKey: string, defaultValue: unknown) => { | ||
const ldClient = useLDClient(); | ||
return ldClient?.jsonVariationDetail(flagKey, defaultValue) ?? defaultValue; | ||
}; | ||
|
||
export default useJsonVariation; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { useContext } from 'react'; | ||
|
||
import { context, ReactSdkContext } from '../provider/reactSdkContext'; | ||
|
||
const useLDClient = () => { | ||
const { ldClient } = useContext<ReactSdkContext>(context); | ||
return ldClient; | ||
}; | ||
|
||
export default useLDClient; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import useLDClient from './useLDClient'; | ||
|
||
export const useNumberVariation = (flagKey: string, defaultValue: number) => { | ||
const ldClient = useLDClient(); | ||
return ldClient?.numberVariation(flagKey, defaultValue) ?? defaultValue; | ||
}; | ||
|
||
export const useNumberVariationDetail = (flagKey: string, defaultValue: number) => { | ||
const ldClient = useLDClient(); | ||
return ldClient?.numberVariationDetail(flagKey, defaultValue) ?? defaultValue; | ||
}; | ||
|
||
export default useNumberVariation; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import useLDClient from './useLDClient'; | ||
|
||
export const useStringVariation = (flagKey: string, defaultValue: string) => { | ||
const ldClient = useLDClient(); | ||
return ldClient?.stringVariation(flagKey, defaultValue) ?? defaultValue; | ||
}; | ||
|
||
export const useStringVariationDetail = (flagKey: string, defaultValue: string) => { | ||
const ldClient = useLDClient(); | ||
return ldClient?.stringVariationDetail(flagKey, defaultValue) ?? defaultValue; | ||
}; | ||
|
||
export default useStringVariation; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { LDFlagValue } from '@launchdarkly/js-client-sdk-common'; | ||
|
||
import useLDClient from './useLDClient'; | ||
|
||
export const useVariation = (flagKey: string, defaultValue?: any): LDFlagValue => { | ||
const ldClient = useLDClient(); | ||
return ldClient?.variation(flagKey, defaultValue) ?? defaultValue; | ||
}; | ||
|
||
export const useVariationDetail = (flagKey: string, defaultValue?: any): LDFlagValue => { | ||
const ldClient = useLDClient(); | ||
return ldClient?.variationDetail(flagKey, defaultValue) ?? defaultValue; | ||
}; | ||
|
||
export default useVariation; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,43 @@ | ||
/** | ||
* This is the API reference for the React Native LaunchDarkly SDK. | ||
* | ||
* TODO: | ||
* TODO: add rn sdk api docs | ||
* | ||
* For more information, see the SDK reference guide. | ||
* | ||
* @packageDocumentation | ||
*/ | ||
import init from './init'; | ||
import { | ||
useBoolVariation, | ||
useBoolVariationDetail, | ||
useJsonVariation, | ||
useJsonVariationDetail, | ||
useLDClient, | ||
useNumberVariation, | ||
useNumberVariationDetail, | ||
useStringVariation, | ||
useStringVariationDetail, | ||
useVariation, | ||
useVariationDetail, | ||
} from './hooks'; | ||
import { setupPolyfill } from './polyfills'; | ||
import { LDProvider } from './provider'; | ||
|
||
setupPolyfill(); | ||
|
||
export * from '@launchdarkly/js-client-sdk-common'; | ||
|
||
export { init }; | ||
export { | ||
LDProvider, | ||
useLDClient, | ||
useVariation, | ||
useVariationDetail, | ||
useNumberVariation, | ||
useNumberVariationDetail, | ||
useBoolVariation, | ||
useBoolVariationDetail, | ||
useStringVariation, | ||
useStringVariationDetail, | ||
useJsonVariationDetail, | ||
useJsonVariation, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { PropsWithChildren, useEffect, useState } from 'react'; | ||
|
||
import { LDClient, LDContext, LDOptions } from '@launchdarkly/js-client-sdk-common'; | ||
|
||
import init from '../init'; | ||
import { Provider, ReactSdkContext } from './reactSdkContext'; | ||
|
||
type LDProps = { | ||
clientSideSdkKey: string; | ||
context: LDContext; | ||
options?: LDOptions; | ||
}; | ||
const LDProvider = ({ | ||
clientSideSdkKey, | ||
context, | ||
options, | ||
children, | ||
}: PropsWithChildren<LDProps>) => { | ||
const [value, setValue] = useState<ReactSdkContext>({}); | ||
|
||
useEffect(() => { | ||
init(clientSideSdkKey, context, options).then((ldClient: LDClient) => { | ||
setValue({ allFlags: ldClient.allFlags(), ldClient }); | ||
}); | ||
}, []); | ||
|
||
return <Provider value={value}>{children}</Provider>; | ||
}; | ||
|
||
export default LDProvider; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import LDProvider from './LDProvider'; | ||
|
||
// eslint-disable-next-line import/prefer-default-export | ||
export { LDProvider }; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { createContext } from 'react'; | ||
|
||
import { LDClient, LDFlagSet } from '@launchdarkly/js-client-sdk-common'; | ||
|
||
type ReactSdkContext = { | ||
allFlags?: LDFlagSet; | ||
ldClient?: LDClient; | ||
}; | ||
Comment on lines
+5
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is still in flux and will change to have more/less things exposed. |
||
|
||
const context = createContext<ReactSdkContext>({}); | ||
|
||
const { Provider, Consumer } = context; | ||
|
||
export { context, Provider, Consumer, ReactSdkContext }; |
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 think these will be very nice for consistent experiment results. (Aside from also allow type safety, which is nice.)
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 agree. Thank you for pushing for these!