# @flagbearer/react-native

The React Native SDK for Flagbearer. Requires React Native 0.72 or later.

## Install

```sh
npm install @flagbearer/react-native
```

To keep flags between launches, also install a storage, such as AsyncStorage:

```sh
npm install @react-native-async-storage/async-storage
```

## Keys

Use a client key, which starts with `fbc_`. Client keys are public and safe to
include in app code. Server keys, which start with `fbs_`, are secret and belong
only on your server, with [`@flagbearer/node`](/sdks/node).

## Set up one client

Create one client for the whole app, in its own module:

```ts
// src/flags.ts
import AsyncStorage from "@react-native-async-storage/async-storage";

import { init } from "@flagbearer/react-native";

export const flags = init({
  clientKey: "fbc_...",
  storage: AsyncStorage,
});
```

With `storage`, the app starts with the flags each user last had, even
offline. It's optional. Any store with `getItem`, `setItem` and `removeItem`
works. For example, MMKV:

```ts
const storage = new MMKV();

export const flags = init({
  clientKey: "fbc_...",
  storage: {
    getItem: (key) => storage.getString(key) ?? null,
    setItem: (key, value) => storage.set(key, value),
    removeItem: (key) => storage.delete(key),
  },
});
```

## Users

When the signed-in user is known or changes, call `identify`:

```ts
await flags.identify({ key: user.id, email: user.email });
```

The email is optional. Include it to turn flags on for testers by email.

## Checking a flag

Read flags with two small hooks:

```tsx
// src/flags.ts
import { useEffect, useSyncExternalStore } from "react";

import type { FlagKey } from "@flagbearer/react-native";

export function useFlag(key: FlagKey, defaultValue: boolean) {
  const enabled = useSyncExternalStore(
    flags.subscribe,
    () => flags.getSnapshot()[key] ?? defaultValue,
  );

  useEffect(() => {
    flags.isEnabled(key, defaultValue);
  }, [key, defaultValue, enabled]);

  return enabled;
}

// False until the current user's flags have loaded.
export function useFlagsLoaded() {
  return useSyncExternalStore(
    flags.subscribe,
    () => flags.getStatus() !== "loading",
  );
}
```

```tsx
const newCheckout = useFlag("new-checkout", false);
return newCheckout ? <NewCheckout /> : <Checkout />;
```

The default is used until the current user's flags have loaded, and when the
flag doesn't exist. Use `useFlagsLoaded` to hold back UI that mustn't switch
between the default and the real value:

```tsx
const loaded = useFlagsLoaded();
const newCheckout = useFlag("new-checkout", false);
if (!loaded) return <ActivityIndicator />;
return newCheckout ? <NewCheckout /> : <Checkout />;
```

## Typed flag keys

In TypeScript, the [CLI](/sdks/cli) can write a file listing the application's
flags. Then a misspelled key is a type error, and editors autocomplete keys:

```sh
npx @flagbearer/cli generate types --app <app> --out src/flagbearer.d.ts
```

```ts
useFlag("new-checkout", false); // ok
useFlag("new-chekout", false); // type error: not a flag in this app
```

Commit the file, and run the command in its first line again after adding or
removing a flag. Without the file, any string is a flag key.

## Error reports

`allFlags()` returns every flag's current value, so error reports show exactly
which flags were on. It doesn't count as checking them.

```ts
Sentry.setContext("flags", flags.allFlags());
// { "new-checkout": true, "dark-mode": false }
```
