React Native Flow

App helper utilities

Reference for a conventional `helper.tsx` (or `utils/helpers`) module in an Expo / React Native app—based on a common scaffold using Expo Router, AsyncStorage, i18next, dayjs, toasts, and expo-notifications. Names like `saveToSecureStorage` map to AsyncStorage in the sample; swap for secure storage if you handle secrets. Imports such as `@/src/language-config/i18n` are examples—point them at your project.

Packages to install

Install these npm packages in your Expo app (native modules align best with expo install). The downloaded helper.tsx starts with the same list and labels each function.

  • @react-native-async-storage/async-storage
  • dayjs
  • expo-device
  • expo-notifications
  • expo-router
  • react-native-toast-message

Recommended

npx expo install @react-native-async-storage/async-storage dayjs expo-device expo-notifications expo-router react-native-toast-message

npm

npm install @react-native-async-storage/async-storage dayjs expo-device expo-notifications expo-router react-native-toast-message

Peers react and react-native are included with Expo.

Wire in your project

Adjust paths for i18n (`@/src/language-config/...`), `../constants/routes`, and `../context/ThemeContext`. Optional: set EXPO_PUBLIC_GOOGLE_API_KEY for static maps.

Includes a dependency header, import block, and per-export /** … */ notes (npm / React Native / your modules / built-ins).

Language & theme

`changeLanguage`

src/utils/helper.tsx

Async: persists the selected language key (e.g. `@language`) then calls `i18n.changeLanguage` so UI strings update immediately.

Uses

  • npm: @react-native-async-storage/async-storage
  • Your modules: @/src/language-config/i18n
export const changeLanguage = async (lang: string) => {
  await saveToSecureStorage("@language", lang);
  i18n.changeLanguage(lang);
};
`getTheme`

src/utils/helper.tsx

Resolves the effective theme: if the user chose `default`, follows system `ColorSchemeName` (`light` / `dark`); otherwise returns the explicit `Theme` from your context.

Uses

  • React Native: ColorSchemeName
  • Your modules: ../context/ThemeContext (`Theme`)
export const getTheme = (theme: Theme, systemTheme: ColorSchemeName) => {
  if (theme === "default" && systemTheme === "dark") {
    return "dark";
  } else if (theme === "default" && systemTheme === "light") {
    return "light";
  } else {
    return theme;
  }
};

Logging & toasts

`devLog`

src/utils/helper.tsx

Wraps `console.log` so logs run only when `__DEV__` is true—keeps release builds quiet.

Uses

  • Built-in: __DEV__ (Metro / React Native)
export const devLog = (...args: any[]) => {
  if (__DEV__) {
    console.log(...args);
  }
};
`ShowToastOptions`

src/utils/helper.tsx

Optional `text1`, `text2`, and `type` (`success` | `error`) passed into `showError` / `showSuccess` when you don’t use a plain string message.

Uses

  • Built-in: Type-only — pair with react-native-toast-message in showError / showSuccess
export interface ShowToastOptions {
  text1?: string;
  text2?: string;
  type?: "success" | "error";
}
`showError`

src/utils/helper.tsx

Shows a `react-native-toast-message` toast. Accepts a string or `ShowToastOptions`; merges with `getTextList().somethingWentWrong` when lines are missing.

Uses

  • npm: react-native-toast-message
  • Your modules: @/src/language-config/TextList (`getTextList`)
export const showError = (messageOrOptions: string | ShowToastOptions) => {
  let type = "error";
  let text1 = "";
  let text2 = getTextList().somethingWentWrong;

  if (typeof messageOrOptions === "string") {
    text2 = messageOrOptions || text2;
  } else {
    type = messageOrOptions.type || type;
    text1 = messageOrOptions.text1 || text1;
    text2 = messageOrOptions.text2 || text2;
  }

  Toast.show({
    type,
    text1: `${text1}`,
    text2,
  });
};
`showSuccess`

src/utils/helper.tsx

Same shape as `showError` but defaults to success styling—use for confirmations and happy paths.

Uses

  • npm: react-native-toast-message
  • Your modules: @/src/language-config/TextList (`getTextList`)
export const showSuccess = (messageOrOptions: string | ShowToastOptions) => {
  let type = "success";
  let text1 = "";
  let text2 = getTextList().somethingWentWrong;

  if (typeof messageOrOptions === "string") {
    text2 = messageOrOptions || text2;
  } else {
    type = messageOrOptions.type || type;
    text1 = messageOrOptions.text1 || text1;
    text2 = messageOrOptions.text2 || text2;
  }

  Toast.show({
    type,
    text1: `${text1}`,
    text2,
  });
};

Persistence (AsyncStorage)

`saveToSecureStorage`

src/utils/helper.tsx

`AsyncStorage.setItem` wrapped in try/catch with `devLog` on failure. Despite the name, this sample uses AsyncStorage (not hardware-backed secure storage).

Uses

  • npm: @react-native-async-storage/async-storage
export async function saveToSecureStorage(
  key: string,
  value: string
): Promise<void> {
  try {
    await AsyncStorage.setItem(key, value);
  } catch (error) {
    devLog("[AsyncStorage] Error saving data:", error);
  }
}
`getFromSecureStorage`

src/utils/helper.tsx

Reads a string by key; returns `null` if missing or on error.

Uses

  • npm: @react-native-async-storage/async-storage
export async function getFromSecureStorage(
  key: string
): Promise<string | null> {
  try {
    const value = await AsyncStorage.getItem(key);
    return value;
  } catch (error) {
    devLog("[AsyncStorage] Error retrieving data:", error);
    return null;
  }
}
`removeKeysFromSecureStorage`

src/utils/helper.tsx

`AsyncStorage.multiRemove` for batch cleanup (logout, toggle remember-me off, etc.).

Uses

  • npm: @react-native-async-storage/async-storage
export async function removeKeysFromSecureStorage(key: string[]) {
  try {
    await AsyncStorage.multiRemove(key);
  } catch (error) {
    devLog("[AsyncStorage] Error removing data:", error);
    return null;
  }
}
`removeAllKeysFromSecureStorage`

src/utils/helper.tsx

Clears the entire AsyncStorage database—use sparingly (full sign-out / reset).

Uses

  • npm: @react-native-async-storage/async-storage
export async function removeAllKeysFromSecureStorage() {
  try {
    await AsyncStorage.clear();
  } catch (error) {
    devLog("[AsyncStorage] Error removing all data:", error);
    return null;
  }
}

Remember me

`RememberMeCredentials`

src/utils/helper.tsx

Shape: `email`, `password`, `rememberMe`. Used with the helpers below. Storing passwords in AsyncStorage is convenient for demos but insecure for production—prefer tokens or a secure vault.

Uses

  • Built-in: Type-only — no runtime imports
export interface RememberMeCredentials {
  email: string;
  password: string;
  rememberMe: boolean;
}
`saveRememberMeCredentials`

src/utils/helper.tsx

If `rememberMe` is true, writes email/password/`@remember_me` keys; otherwise removes those keys.

Uses

  • npm: @react-native-async-storage/async-storage
  • Note: Uses saveToSecureStorage / removeKeysFromSecureStorage
export const saveRememberMeCredentials = async (credentials: RememberMeCredentials): Promise<void> => {
  try {
    if (credentials.rememberMe) {
      await saveToSecureStorage("@remembered_email", credentials.email);
      await saveToSecureStorage("@remembered_password", credentials.password);
      await saveToSecureStorage("@remember_me", "true");
    } else {
      await removeKeysFromSecureStorage([
        "@remembered_email",
        "@remembered_password",
        "@remember_me",
      ]);
    }
  } catch (error) {
    devLog("[Remember Me] Error saving credentials:", error);
  }
};
`loadRememberMeCredentials`

src/utils/helper.tsx

Reads saved credentials when `@remember_me` equals the string true; otherwise returns empty strings and rememberMe: false.

Uses

  • npm: @react-native-async-storage/async-storage
export const loadRememberMeCredentials = async (): Promise<RememberMeCredentials> => {
  try {
    const savedEmail = await getFromSecureStorage("@remembered_email");
    const savedPassword = await getFromSecureStorage("@remembered_password");
    const rememberMe = await getFromSecureStorage("@remember_me");

    if (savedEmail && savedPassword && rememberMe === "true") {
      return {
        email: savedEmail,
        password: savedPassword,
        rememberMe: true,
      };
    }

    return {
      email: "",
      password: "",
      rememberMe: false,
    };
  } catch (error) {
    devLog("[Remember Me] Error loading credentials:", error);
    return {
      email: "",
      password: "",
      rememberMe: false,
    };
  }
};
`clearRememberMeCredentials`

src/utils/helper.tsx

Removes the three remember-me keys without wiping all storage.

Uses

  • npm: @react-native-async-storage/async-storage
export const clearRememberMeCredentials = async (): Promise<void> => {
  try {
    await removeKeysFromSecureStorage([
      "@remembered_email",
      "@remembered_password",
      "@remember_me",
    ]);
  } catch (error) {
    devLog("[Remember Me] Error clearing credentials:", error);
  }
};

Strings, dates & currency

`capitalizeFirstLetter`

src/utils/helper.tsx

Returns empty string for falsy input; otherwise uppercases the first character and leaves the rest untouched.

Uses

  • Built-in: No packages
export const capitalizeFirstLetter = (text: string): string => {
  if (!text) return "";
  return text.charAt(0).toUpperCase() + text.slice(1);
};
`getTimeOfDay`

src/utils/helper.tsx

Bucketed greeting by hour (5–12 morning, 12–17 afternoon, 17–21 evening, else night) using localized strings from `getTextList()`.

Uses

  • Your modules: @/src/language-config/TextList (`getTextList`)
export const getTimeOfDay = () => {
  const currentHour = new Date().getHours();

  if (currentHour >= 5 && currentHour < 12) {
    return getTextList().morning;
  } else if (currentHour >= 12 && currentHour < 17) {
    return getTextList().afternoon;
  } else if (currentHour >= 17 && currentHour < 21) {
    return getTextList().evening;
  } else {
    return getTextList().night;
  }
};
`formatDate`

src/utils/helper.tsx

Formats a `Date` or date string with **dayjs** (`format` argument, e.g. `YYYY-MM-DD`).

Uses

  • npm: dayjs
export const formatDate = (date: string | Date, format: string): string => {
  return dayjs(date).format(format);
};
`formatCurrency`

src/utils/helper.tsx

`Intl.NumberFormat` with `style: "currency"`, fixed 2 fraction digits, defaulting currency to `GBP` when omitted.

Uses

  • Built-in: Intl.NumberFormat
export const formatCurrency = (amount: number, currency: string) => {
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: currency || "GBP",
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  }).format(amount);
};
`getCurrencySymbol`

src/utils/helper.tsx

Maps ISO codes (USD, EUR, INR, …) to symbols; falls back to `£` when code is empty or unknown.

Uses

  • Built-in: Intl (standard JS)
export const getCurrencySymbol = (currencyCode: string | null | undefined): string => {
  const normalizedCurrencyCode = currencyCode?.trim().toUpperCase();

  if (!normalizedCurrencyCode) {
    return "£";
  }

  const currencyMap: Record<string, string> = {
    USD: "$",
    GBP: "£",
    EUR: "€",
    INR: "₹",
    JPY: "¥",
    CNY: "¥",
    AUD: "A$",
    CAD: "C$",
    CHF: "CHF",
    SEK: "kr",
    NOK: "kr",
    DKK: "kr",
    PLN: "zł",
    RUB: "₽",
    BRL: "R$",
    MXN: "$",
    ZAR: "R",
    SGD: "S$",
    HKD: "HK$",
    NZD: "NZ$",
    KRW: "₩",
    TRY: "₺",
    AED: "د.إ",
    SAR: "﷼",
    PKR: "₨",
    BDT: "৳",
    THB: "฿",
    MYR: "RM",
    IDR: "Rp",
    PHP: "₱",
    VND: "₫",
  };

  return currencyMap[normalizedCurrencyCode] || "£";
};

Linking & settings

`openAppSettings`

src/utils/helper.tsx

iOS: opens `app-settings:`; Android: `Linking.openSettings()` for app details—use after permission denials.

Uses

  • React Native: Linking, Platform
export const openAppSettings = () => {
  if (Platform.OS === "ios") {
    Linking.openURL("app-settings:");
  } else {
    Linking.openSettings();
  }
};
`openWebUrl`

src/utils/helper.tsx

Thin wrapper around `Linking.openURL` for https links and deep links.

Uses

  • React Native: Linking
export const openWebUrl = (url: string) => {
  Linking.openURL(url);
};

Expo Router navigation

`navigate`

src/utils/helper.tsx

Typesafe-ish `router.push` wrapper: `pathname`, optional `params`, and `search` object forwarded to Expo Router.

Uses

  • npm: expo-router
type RelativePathString = string;

type NavigateOptions = {
  params?: Record<string, string>;
  search?: Record<string, string | number | boolean | undefined>;
};

export const navigate = (
  route: RelativePathString | any,
  options?: NavigateOptions
) => {
  router.push({
    pathname: route,
    ...(options?.params && { params: options.params }),
    ...(options?.search && { search: options.search }),
  });
};
`navigateBack`

src/utils/helper.tsx

Calls `router.back()` for stack pops.

Uses

  • npm: expo-router
export const navigateBack = () => {
  router.back();
};
`updateUser`

src/utils/helper.tsx

Persists access/refresh tokens from an API payload, updates user state via `setUser`, then navigates to a route (or default tab root).

Uses

  • npm: @react-native-async-storage/async-storage, expo-router
  • Your modules: ../constants/routes · `navigate` in same file
  • Note: Expects API shape with data.access_token, data.refresh_token, data.user
export const updateUser = async (
  data: any,
  setUser: (user: any) => void,
  route?: any
) => {
  await saveToSecureStorage("@access_token", data?.data?.access_token);
  await saveToSecureStorage("@refresh_token", data?.data?.refresh_token);
  setUser(data?.data?.user);
  if (route) {
    navigate(route);
  } else {
    navigate(Routes.TABS.ROOT);
  }
};

UI data helpers

`getConsistentColor`

src/utils/helper.tsx

Deterministic pastel color from a string id (e.g. review id) using a simple hash into a fixed palette—good for avatars or tags.

Uses

  • Built-in: No packages
export const getConsistentColor = (reviewId: string) => {
  const colors = [
    "#FF6B6B",
    "#4ECDC4",
    "#45B7D1",
    "#96CEB4",
    "#FFEAA7",
    "#DDA0DD",
    "#98D8C8",
    "#F7DC6F",
    "#BB8FCE",
    "#85C1E9",
    "#F8C471",
    "#82E0AA",
    "#F1948A",
    "#85C1E9",
    "#D7BDE2",
  ];

  let hash = 0;
  for (let i = 0; i < reviewId.length; i++) {
    hash = reviewId.charCodeAt(i) + ((hash << 5) - hash);
  }
  return colors[Math.abs(hash) % colors.length];
};

Google Static Maps

`MapMarker`

src/utils/helper.tsx

Marker shape: required `lat`/`lng`, optional `name`, `color`, `label` for Static Maps marker parameters.

Uses

  • Built-in: Type-only
export interface MapMarker {
  name?: string;
  lat: number;
  lng: number;
  color?: string;
  label?: string;
}
`StaticMapOptions`

src/utils/helper.tsx

Options for the Static Maps URL: `center`, `zoom`, `size`, `maptype`, `markers`, `scale`, `format`.

Uses

  • Built-in: Type-only
export interface StaticMapOptions {
  center?: { lat: number; lng: number };
  zoom?: number;
  size?: string;
  maptype?: "roadmap" | "satellite" | "terrain" | "hybrid";
  markers?: MapMarker[];
  scale?: 1 | 2;
  format?: "png" | "jpg" | "gif";
}
`generateStaticMapUrl`

src/utils/helper.tsx

Builds a Google Maps Static API URL from options; reads `process.env.EXPO_PUBLIC_GOOGLE_API_KEY`, logs and returns `""` if missing; encodes marker pipes per Google’s format.

Uses

  • Built-in: EXPO_PUBLIC_GOOGLE_API_KEY (env), global URL / encodeURIComponent
  • Note: Uses Google Maps Static API
export const generateStaticMapUrl = (options: StaticMapOptions): string => {
  const {
    center,
    zoom = 13,
    size = "600x200",
    maptype = "roadmap",
    markers = [],
    scale = 1,
    format = "png",
  } = options;

  const apiKey = process.env.EXPO_PUBLIC_GOOGLE_API_KEY;

  if (!apiKey) {
    devLog("Warning: Google Maps API key not found");
    return "";
  }

  const baseUrl = "https://maps.googleapis.com/maps/api/staticmap";
  const params: string[] = [];

  if (center) {
    params.push(`center=${center.lat},${center.lng}`);
  } else if (markers.length > 0) {
    params.push(`center=${markers[0].lat},${markers[0].lng}`);
  }

  params.push(`zoom=${zoom}`);
  params.push(`size=${size}`);
  params.push(`maptype=${maptype}`);
  params.push(`scale=${scale}`);
  params.push(`format=${format}`);

  if (markers.length > 0) {
    markers.forEach((marker) => {
      const markerParams: string[] = [];
      if (marker.color) {
        markerParams.push(`color:${marker.color}`);
      }
      if (marker.label) {
        markerParams.push(`label:${marker.label}`);
      }
      markerParams.push(`${marker.lat},${marker.lng}`);
      const markerString = markerParams.join("|");
      params.push(`markers=${encodeURIComponent(markerString)}`);
    });
  }

  params.push(`key=${apiKey}`);

  const finalUrl = `${baseUrl}?${params.join("&")}`;
  devLog("Generated Static Map URL:", finalUrl);
  return finalUrl;
};
`generateStaticMapUrlWithMarker`

src/utils/helper.tsx

Convenience wrapper: centers on one point, adds a red marker, spreads extra `StaticMapOptions` except conflicting fields.

Uses

  • Built-in: Builds on generateStaticMapUrl
export const generateStaticMapUrlWithMarker = (
  name: string,
  lat: number,
  lng: number,
  options?: Omit<StaticMapOptions, "markers" | "center">
): string => {
  return generateStaticMapUrl({
    center: { lat, lng },
    markers: [{ name, lat, lng, color: "red" }],
    ...options,
  });
};

Local notifications

`initializeNotifications`

src/utils/helper.tsx

On real devices, requests notification permission; on Android creates a `default` channel with max importance using **expo-notifications**.

Uses

  • npm: expo-notifications, expo-device
  • React Native: Platform
export const initializeNotifications = async () => {
  if (Device.isDevice) {
    const { status } = await Notifications.requestPermissionsAsync();
    if (status !== "granted") {
      devLog("Permission for notifications not granted!");
      return;
    }
  }

  if (Platform.OS === "android") {
    await Notifications.setNotificationChannelAsync("default", {
      name: "default",
      importance: Notifications.AndroidImportance.MAX,
    });
  }
};
`showNotification`

src/utils/helper.tsx

Schedules an immediate local notification (`trigger: null`) with title and body.

Uses

  • npm: expo-notifications
export const showNotification = async (title: string, body: string) => {
  await Notifications.scheduleNotificationAsync({
    content: { title, body },
    trigger: null,
  });
};

Sponsored

Quick promo