Context
I'm currently integrating reCAPTCHA Enterprise into a React Native app and noticed that the recommended API (Recaptcha.fetchClient) requires manual management of the RecaptchaClient instance across different screens. I wanted to open a discussion about potential improvements to make this easier for developers.
Current API (Recommended)
import { Recaptcha, RecaptchaAction, RecaptchaClient } from '@google-cloud/recaptcha-enterprise-react-native';
// Must initialize once
const client = await Recaptcha.fetchClient(siteKey);
// Now need to pass this client to other screens...
// How do we share this across the app?
The Challenge
The documentation states:
You must initialize the reCAPTCHA client only once during the lifetime of your app.
However, the API returns a client instance that needs to be shared across multiple screens (e.g., Login, SignUp, Password Reset). This requires developers to implement additional patterns to manage the instance:
Solution 1: Context API (Boilerplate Heavy)
// RecaptchaContext.tsx
const RecaptchaContext = createContext<RecaptchaClient | null>(null);
export const RecaptchaProvider = ({ children }) => {
const [client, setClient] = useState<RecaptchaClient | null>(null);
useEffect(() => {
Recaptcha.fetchClient(siteKey).then(setClient);
}, []);
return (
<RecaptchaContext.Provider value={client}>
{children}
</RecaptchaContext.Provider>
);
};
export const useRecaptcha = () => useContext(RecaptchaContext);
// App.tsx
<RecaptchaProvider>
<Navigation />
</RecaptchaProvider>
// LoginScreen.tsx
const client = useRecaptcha();
const token = await client?.execute(RecaptchaAction.LOGIN());
Solution 2: Singleton Pattern
// recaptchaService.ts
class RecaptchaService {
private client: RecaptchaClient | null = null;
async initialize(siteKey: string) {
if (!this.client) {
this.client = await Recaptcha.fetchClient(siteKey);
}
return this.client;
}
getClient() {
if (!this.client) throw new Error('Not initialized');
return this.client;
}
}
export const recaptchaService = new RecaptchaService();
// Usage
await recaptchaService.initialize(siteKey);
const token = await recaptchaService.getClient().execute(RecaptchaAction.LOGIN());
Both solutions work but require additional setup code.
Observation: The Deprecated API Pattern
I noticed the deprecated API (initClient + execute) had a simpler usage pattern:
import { initClient, execute, RecaptchaAction } from '@google-cloud/recaptcha-enterprise-react-native';
// Initialize once
await initClient(siteKey, 10000);
// Use anywhere in the app - no instance management needed!
const token = await execute(RecaptchaAction.LOGIN(), 10000);
This pattern works well because the native layer already manages the client internally. Looking at the implementation:
Android (RecaptchaEnterpriseReactNativeModule.kt:37):
private lateinit var recaptchaClient: RecaptchaClient // Stored in module
iOS (RecaptchaEnterpriseReactNative.swift:19):
var recaptchaClient: RecaptchaClient? // Stored in module
The native modules already handle instance management internally.
Question
What was the reasoning behind deprecating initClient/execute in favor of the instance-based approach?
Reference: Patterns from Similar Libraries
React Native Firebase
import { getAuth, signInWithEmailAndPassword } from '@react-native-firebase/auth';
// Functional API - no manual instance management
await signInWithEmailAndPassword(getAuth(), email, password);
Firebase JS SDK
import { initializeApp } from 'firebase/app';
import { getAuth, signInWithEmailAndPassword } from 'firebase/auth';
// Initialize once
const app = initializeApp(config);
// Functional API - pass auth instance to functions
await signInWithEmailAndPassword(getAuth(app), email, password);
AWS Amplify
import { Amplify, Auth } from 'aws-amplify';
// Configure once
Amplify.configure(awsconfig);
// Use anywhere
await Auth.signIn(username, password);
These libraries provide different approaches to instance management that might be worth considering.
Potential Approaches
Approach 1: Static Methods (Similar to Amplify)
// Configure once
await Recaptcha.configure({ siteKey: 'YOUR_SITE_KEY' });
// Use anywhere - no instance needed
const token = await Recaptcha.execute(RecaptchaAction.LOGIN());
// Check if configured
if (Recaptcha.isConfigured()) {
// Ready to use
}
Approach 2: Improve and Re-introduce Functional API
export { initClient as configure };
export { execute };
// Usage
await configure(siteKey);
const token = await execute(RecaptchaAction.LOGIN());
Potential Benefits
Any of these approaches could provide:
- Reduced boilerplate for common use cases
- Easier usage across multiple screens
- Simpler onboarding experience
I'm happy to contribute an implementation if there's interest in this direction.
Environment:
- Package version: 18.9.0-beta01
- React Native version: 0.83.0
- Platform: iOS & Android
Context
I'm currently integrating reCAPTCHA Enterprise into a React Native app and noticed that the recommended API (
Recaptcha.fetchClient) requires manual management of theRecaptchaClientinstance across different screens. I wanted to open a discussion about potential improvements to make this easier for developers.Current API (Recommended)
The Challenge
The documentation states:
However, the API returns a client instance that needs to be shared across multiple screens (e.g., Login, SignUp, Password Reset). This requires developers to implement additional patterns to manage the instance:
Solution 1: Context API (Boilerplate Heavy)
Solution 2: Singleton Pattern
Both solutions work but require additional setup code.
Observation: The Deprecated API Pattern
I noticed the deprecated API (
initClient+execute) had a simpler usage pattern:This pattern works well because the native layer already manages the client internally. Looking at the implementation:
Android (RecaptchaEnterpriseReactNativeModule.kt:37):
iOS (RecaptchaEnterpriseReactNative.swift:19):
The native modules already handle instance management internally.
Question
What was the reasoning behind deprecating
initClient/executein favor of the instance-based approach?Reference: Patterns from Similar Libraries
React Native Firebase
Firebase JS SDK
AWS Amplify
These libraries provide different approaches to instance management that might be worth considering.
Potential Approaches
Approach 1: Static Methods (Similar to Amplify)
Approach 2: Improve and Re-introduce Functional API
Potential Benefits
Any of these approaches could provide:
I'm happy to contribute an implementation if there's interest in this direction.
Environment: