Sorry for dumping this here, but I had a bit of a rough time getting the initial implementation right and figured others might be struggling too. I wanted to leave a quick example of how to handle the new modular API for anyone migrating to v23.
@mikehardy, feel free to chime in if there's a cleaner way to do this!
Implementing Modular Remote Config (v23.x.x)
With the release of version 23.x.x, React Native Firebase has fully embraced the Modular API (similar to the Firebase Web SDK v9).
What has changed?
In previous versions, we used a class-based instance (e.g., remoteConfig().fetchAndActivate()). In the modular version, we now pass the configuration instance as the first argument to standalone functions like fetchAndActivate(instance).
Service Implementation
The following implementation centralizes Remote Config logic into a reusable service:
import {
fetchAndActivate,
getRemoteConfig,
getValue,
setConfigSettings
} from '@react-native-firebase/remote-config';
// Cache time constant (e.g., 5 minutes)
export const REMOTE_CONFIG_CACHE_TIME = 300000;
let remoteConfig: any = null;
/**
* Initializes the Remote Config instance and sets global settings.
*/
const initializeRemoteConfig = async () => {
if (!remoteConfig) {
remoteConfig = getRemoteConfig();
}
await setConfigSettings(remoteConfig, {
minimumFetchIntervalMillis: REMOTE_CONFIG_CACHE_TIME
});
};
/**
* Fetches and activates the latest values before returning them as a string.
*/
const getValueAsString = async (property: string): Promise<string> => {
await fetchAndActivate(remoteConfig);
return getValue(remoteConfig, property).asString();
};
/**
* Fetches and activates the latest values before returning them as a boolean.
*/
const getValueAsBoolean = async (property: string): Promise<boolean> => {
await fetchAndActivate(remoteConfig);
return getValue(remoteConfig, property).asBoolean();
};
export const RemoteConfigService = {
getValueAsBoolean,
getValueAsString,
initializeRemoteConfig
};
How to use it
1. Global Initialization
It is recommended to initialize the service early in your app's lifecycle (e.g., in App.tsx or a Redux Store).
import { useEffect } from 'react';
import { RemoteConfigService } from './services/RemoteConfigService';
const App = () => {
const setupRemoteConfig = async () => {
try {
await RemoteConfigService.initializeRemoteConfig();
} catch (error) {
console.error("Failed to initialize Remote Config:", error);
}
};
useEffect(() => {
setupRemoteConfig();
}, []);
return <MainNavigator />;
};
2. Consuming Values
Since the methods are async, you can call them whenever you need a specific flag:
const isNewUiEnabled = await RemoteConfigService.getValueAsBoolean('enable_new_interface');
Quick Notes
-
Global Instance: Keep in mind that if you're using Expo or standard React Native and have your google-services.json (or GoogleService-Info.plist) correctly set up, @react-native-firebase is instantiated automatically. You don't need to manually initialize the Firebase app yourself. You just need to ensure that the initializeRemoteConfig function from the snippet above is called before you trigger any other service functions.
-
Safety: Always wrap your initialization in a try/catch block to prevent app crashes due to network issues during the initial fetch.
Other Option
- Self-Initializing Singleton Pattern:
Instead of relying on the caller to remember to initialize the service in App.tsx, you can bake the initialization check directly into the getters. This ensures the configuration is ready even if a getter is called before the app's main setup finishes.
Since the initializeRemoteConfig function checks for an existing instance, calling it multiple times has zero overhead after the first successful execution.
let remoteConfig = null
const initializeRemoteConfig = async () => {
if (remoteConfig) {
return
}
const instance = getRemoteConfig()
await setConfigSettings(instance, {
minimumFetchIntervalMillis: REMOTE_CONFIG_CACHE_TIME
})
remoteConfig = instance
}
const getValueAsString = async (property: string): Promise<string> => {
await initializeRemoteConfig()
await fetchAndActivate(remoteConfig)
return getValue(remoteConfig, property).asString()
}
const getValueAsBoolean = async (property: string): Promise<boolean> => {
await initializeRemoteConfig()
await fetchAndActivate(remoteConfig)
return getValue(remoteConfig, property).asBoolean()
}
Sorry for dumping this here, but I had a bit of a rough time getting the initial implementation right and figured others might be struggling too. I wanted to leave a quick example of how to handle the new modular API for anyone migrating to v23.
@mikehardy, feel free to chime in if there's a cleaner way to do this!
Implementing Modular Remote Config (v23.x.x)
With the release of version 23.x.x, React Native Firebase has fully embraced the Modular API (similar to the Firebase Web SDK v9).
What has changed?
In previous versions, we used a class-based instance (e.g.,
remoteConfig().fetchAndActivate()). In the modular version, we now pass the configuration instance as the first argument to standalone functions likefetchAndActivate(instance).Service Implementation
The following implementation centralizes Remote Config logic into a reusable service:
How to use it
1. Global Initialization
It is recommended to initialize the service early in your app's lifecycle (e.g., in
App.tsxor a Redux Store).2. Consuming Values
Since the methods are
async, you can call them whenever you need a specific flag:Quick Notes
Global Instance: Keep in mind that if you're using Expo or standard React Native and have your google-services.json (or GoogleService-Info.plist) correctly set up, @react-native-firebase is instantiated automatically. You don't need to manually initialize the Firebase app yourself. You just need to ensure that the initializeRemoteConfig function from the snippet above is called before you trigger any other service functions.
Safety: Always wrap your initialization in a
try/catchblock to prevent app crashes due to network issues during the initial fetch.Other Option
Instead of relying on the caller to remember to initialize the service in
App.tsx, you can bake the initialization check directly into the getters. This ensures the configuration is ready even if a getter is called before the app's main setup finishes.Since the initializeRemoteConfig function checks for an existing instance, calling it multiple times has zero overhead after the first successful execution.