React Native Flow

Helpers utilitaires de l’app

Référence d’un `helper.tsx` classique (ou `utils/helpers`) dans une app Expo/React Native, calqué sur un socle courant : Expo Router, AsyncStorage, i18next, dayjs, toasts et expo-notifications. Des noms comme `saveToSecureStorage` s’appuient ici sur AsyncStorage ; utilisez un stockage sécurisé pour les secrets. Des imports tels que `@/src/language-config/i18n` sont des exemples à adapter.

Paquets à installer

Installez ces paquets npm dans votre app Expo (expo install est préférable pour les modules natifs). Le helper.tsx téléchargé reprend la même liste et précise les dépendances par fonction.

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

Recommandé

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

react et react-native sont fournis avec Expo.

À brancher dans votre projet

Adaptez les chemins i18n (`@/src/language-config/...`), `../constants/routes` et `../context/ThemeContext`. Optionnel : EXPO_PUBLIC_GOOGLE_API_KEY pour les cartes statiques.

Comprend l’en-tête des dépendances, les imports et des notes /** … */ par export (npm / React Native / vos modules / intégré).

Langue et thème

`changeLanguage`

src/utils/helper.tsx

Async : enregistre la clé de langue (ex. `@language`) puis appelle `i18n.changeLanguage` pour rafraîchir les chaînes.

Utilise

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

src/utils/helper.tsx

Calcule le thème effectif : si l’utilisateur a choisi `default`, suit le `ColorSchemeName` système ; sinon renvoie le `Theme` explicite du contexte.

Utilise

  • React Native: ColorSchemeName
  • Vos 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;
  }
};

Logs et toasts

`devLog`

src/utils/helper.tsx

Encapsule `console.log` pour ne journaliser que lorsque `__DEV__` est vrai.

Utilise

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

src/utils/helper.tsx

Champs optionnels `text1`, `text2` et `type` (success | error) pour `showError` / `showSuccess` lorsque vous ne passez pas une simple chaîne.

Utilise

  • Intégré: 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

Affiche un toast react-native-toast-message. Accepte une chaîne ou des options ; complète avec `getTextList().somethingWentWrong` si besoin.

Utilise

  • npm: react-native-toast-message
  • Vos 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

Même forme que `showError`, orientée succès.

Utilise

  • npm: react-native-toast-message
  • Vos 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,
  });
};

Persistance (AsyncStorage)

`saveToSecureStorage`

src/utils/helper.tsx

`AsyncStorage.setItem` avec try/catch et `devLog`. Le nom évoque le secure alors que l’exemple utilise AsyncStorage.

Utilise

  • 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

Lit une chaîne par clé ; renvoie `null` si absente ou en cas d’erreur.

Utilise

  • 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` pour supprimer plusieurs clés (déconnexion, désactivation du « se souvenir »…).

Utilise

  • 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

Vide tout AsyncStorage—rarement, pour reset complet.

Utilise

  • 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;
  }
}

Se souvenir de moi

`RememberMeCredentials`

src/utils/helper.tsx

Forme : `email`, `password`, `rememberMe`. Stocker un mot de passe en AsyncStorage est risqué en production ; préférez jetons ou coffre sécurisé.

Utilise

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

src/utils/helper.tsx

Si `rememberMe` est vrai, enregistre email/mot de passe/`@remember_me` ; sinon supprime ces clés.

Utilise

  • 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

Lit les identifiants si `@remember_me` vaut la chaîne true ; sinon valeurs vides et rememberMe faux.

Utilise

  • 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

Supprime uniquement les trois clés « se souvenir ».

Utilise

  • 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);
  }
};

Texte, dates et devise

`capitalizeFirstLetter`

src/utils/helper.tsx

Renvoie une chaîne vide si l’entrée est vide ; sinon première lettre en majuscule.

Utilise

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

src/utils/helper.tsx

Créneau horaire (matin/après-midi/soir/nuit) avec libellés localisés via `getTextList()`.

Utilise

  • Vos 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

Formate une `Date` ou une chaîne avec dayjs selon le motif (ex. YYYY-MM-DD).

Utilise

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

src/utils/helper.tsx

`Intl.NumberFormat` en mode devise, 2 décimales, GBP par défaut si manquant.

Utilise

  • Intégré: 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

Associe codes ISO et symboles ; repli £ si absent ou inconnu.

Utilise

  • Intégré: 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 et réglages

`openAppSettings`

src/utils/helper.tsx

iOS : `app-settings:` ; Android : `Linking.openSettings()` vers les réglages de l’app.

Utilise

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

src/utils/helper.tsx

Fine couche sur `Linking.openURL` pour liens web ou deep links.

Utilise

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

Navigation Expo Router

`navigate`

src/utils/helper.tsx

Enveloppe `router.push` : `pathname`, `params` optionnel et `search` pour Expo Router.

Utilise

  • 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

Appelle `router.back()` pour revenir dans la pile.

Utilise

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

src/utils/helper.tsx

Enregistre jetons d’accès/refresh, met à jour l’utilisateur via `setUser`, puis navigue vers une route ou la racine des onglets.

Utilise

  • npm: @react-native-async-storage/async-storage, expo-router
  • Vos 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);
  }
};

Données UI

`getConsistentColor`

src/utils/helper.tsx

Couleur pastel stable à partir d’un id (hash simple sur une palette)—avatars ou badges.

Utilise

  • Intégré: 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

Marqueur : `lat`/`lng` requis ; `name`, `color`, `label` optionnels pour Static Maps.

Utilise

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

src/utils/helper.tsx

Options de carte statique : centre, zoom, taille, type, marqueurs, échelle, format.

Utilise

  • Intégré: 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

Construit l’URL Google Static Maps ; lit `EXPO_PUBLIC_GOOGLE_API_KEY` ; chaîne vide + log si absente.

Utilise

  • Intégré: 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

Raccourci avec un marqueur rouge et le reste des options.

Utilise

  • Intégré: 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,
  });
};

Notifications locales

`initializeNotifications`

src/utils/helper.tsx

Sur appareil réel, demande la permission ; sur Android, canal default en importance max (expo-notifications).

Utilise

  • 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

Planifie une notification locale immédiate (`trigger: null`) avec titre et corps.

Utilise

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

Sponsorisé

Promo rapide