Debug School

rakesh kumar
rakesh kumar

Posted on • Edited on

How to Manage Global Settings with React Context and AsyncStorage in React Native

Step 1: Creating the Context (BaseUrlContext)
The BaseUrlContext provides a central location where global settings like the baseUrl and dial_code can be stored and accessed throughout the app.

import React, { createContext, useState, useContext, useEffect } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';

// Create context
const BaseUrlContext = createContext();

// Custom hook to use the context
export const useBaseUrl = () => {
  return useContext(BaseUrlContext);
};
Enter fullscreen mode Exit fullscreen mode

createContext(): This creates the context object for global state management. In this case, it's named BaseUrlContext.

useBaseUrl: A custom hook to access the BaseUrlContext. This makes it easier to access the context in different components without having to manually use useContext each time.

Step 2: Setting up the Context Provider (BaseUrlProvider)
The BaseUrlProvider component will wrap the app and manage the state for baseUrl and dial_code globally. It also provides functions to update these values and persist them using AsyncStorage.

import React, { createContext, useState, useContext, useEffect } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';

const BaseUrlContext = createContext();

export const useBaseUrl = () => {
  return useContext(BaseUrlContext);
};

export const BaseUrlProvider = ({ children }) => {
  const [baseUrl, setBaseUrl] = useState('');
  const [dialCode, setDialCode] = useState(''); // State to store dial_code
  const [otp, setOtp] = useState(''); // State to store OTP
  const [phone, setPhone] = useState(''); // State to store phone number

  useEffect(() => {
    const fetchBaseUrl = async () => {
      const storedBaseUrl = await AsyncStorage.getItem('baseUrl');
      const storedDialCode = await AsyncStorage.getItem('dialCode'); // Fetch dial_code from AsyncStorage
      const storedOtp = await AsyncStorage.getItem('otp'); // Fetch OTP from AsyncStorage
      const storedPhone = await AsyncStorage.getItem('phone'); // Fetch phone number from AsyncStorage

      if (storedBaseUrl) {
        setBaseUrl(storedBaseUrl);
      }
      if (storedDialCode) {
        setDialCode(storedDialCode);
      }
      if (storedOtp) {
        setOtp(storedOtp); // Set OTP in state from AsyncStorage
      }
      if (storedPhone) {
        setPhone(storedPhone); // Set phone number in state from AsyncStorage
      }
    };
    fetchBaseUrl();
  }, []);

  // Function to set a new base URL and remove previous values
  const setNewBaseUrl = async (url) => {
    await AsyncStorage.removeItem('otp');  // Remove OTP when setting a new base URL
    await AsyncStorage.removeItem('phone');  // Remove phone number when setting a new base URL
    await AsyncStorage.removeItem('dialcode');  // Remove phone number when setting a new base URL
    await AsyncStorage.setItem('baseUrl', url);
    setBaseUrl(url);
  };

  // Function to set a new dial code and remove previous values
  const setNewDialcode = async (dialcode) => {
    await AsyncStorage.removeItem('otp');  // Remove OTP when setting a new dial code
    await AsyncStorage.removeItem('phone');  // Remove phone number when setting a new dial code
    await AsyncStorage.setItem('dialCode', dialcode); // Save dial_code to AsyncStorage
    setDialCode(dialcode); // Update dial_code in context
  };

  // Function to set a new OTP and remove previous phone number
  const setNewOtp = async (otp) => {

    await AsyncStorage.setItem('otp', otp); // Save OTP to AsyncStorage
    setOtp(otp); // Update OTP in context
    console.log('OTP set in context:', otp);
  };

  // Function to set a new phone number and remove OTP
  const setNewPhone = async (phone) => {

    await AsyncStorage.setItem('phone', phone); // Save phone to AsyncStorage
    setPhone(phone); // Update phone in context
  };

  return (
    <BaseUrlContext.Provider value={{ baseUrl, dialCode, otp, phone, setNewBaseUrl, setNewDialcode, setNewOtp, setNewPhone }}>
      {children}
    </BaseUrlContext.Provider>
  );
};

Enter fullscreen mode Exit fullscreen mode

useState: React's state management hook to store baseUrl and dialCode globally.

useEffect: On app load, this hook fetches the stored values from AsyncStorage and sets them into the context state. This ensures the app retains settings even after a restart.

setNewBaseUrl and setNewDialcode: These functions are responsible for updating the baseUrl and dial_code both in the context and in AsyncStorage. AsyncStorage.setItem is used to persist these settings.

Step 3: Wrapping the App with the BaseUrlProvider
To make the context accessible throughout the app, we need to wrap the app with the BaseUrlProvider component. This ensures that any component inside the BaseUrlProvider can access and update the global settings.

export default function App() {
  return (
    <BaseUrlProvider> {/* Wrap the app with BaseUrlProvider */}
      <NavigationContainer>
        <Stack.Navigator>
          <Stack.Screen
            name="CountrySelection"
            component={CountrySelectionPage}
            options={{ title: 'Select Country' }}
          />
          <Stack.Screen
            name="Main"
            component={MainDrawerNavigator}
            options={{ headerShown: false }}
          />
        </Stack.Navigator>
      </NavigationContainer>
    </BaseUrlProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

BaseUrlProvider: By wrapping the app with BaseUrlProvider, the global settings (baseUrl, dial_code) are now available in any component within the app using the useBaseUrl hook.

Step 4: Accessing and Updating the Global Settings in the Parent Component
In the parent component, you can access and update the dial_code by using the useBaseUrl hook. The example shows how to update the dial_code when a user selects a country from a modal (CountrySelectModal).

import { useBaseUrl } from '../contexts/BaseUrlContext'; // Import the useBaseUrl hook

const { setNewDialcode } = useBaseUrl(); // Get the function to update the dial_code

const handleCountrySelect = async (country) => {
  try {
    setSelectedCountry(country.name); // Store only the country name
    setIsPhoneEnabled(true); // Enable the phone input after selecting a country
    setIsModalVisible(false); // Close the modal after selection

    await setNewDialcode(country.dial_code); // Save the dial_code globally using context

    console.log('Country and dial_code saved successfully');
  } catch (error) {
    console.error('Error saving country data:', error);
  }
};
Enter fullscreen mode Exit fullscreen mode

setNewDialcode: This function is called to update the dial_code in the global state via the BaseUrlContext.

AsyncStorage: The dial code is also saved in AsyncStorage so that it persists across app restarts.
==========================OR============================
we can set it in controller useMotoshareViewModel

 await setNewOtp(otp); // Save OTP to context and AsyncStorage
Enter fullscreen mode Exit fullscreen mode
  const sendOtp = async (phoneNumber) => {
    //setIsLoading(true); // Start loading state
    const api = new MotoshareAPI(baseUrl); // Pass baseUrl from context to MotoshareAPI
    console.log("OTP sendOtp ing");
    try {
      // Call the API to send OTP
      const response = await api.sendOtp({ phone: phoneNumber });  // Assuming POST request with phone parameter

      if (response && response.success) {
        console.log("OTP sent successfully");
        console.log(phoneNumber);
        const otp = response.otp.toString(); // Ensure OTP is a string

        await setNewOtp(otp); // Save OTP to context and AsyncStorage
        await setNewPhone(phoneNumber); // Save phone number in context and AsyncStorage
      } else {
        throw new Error("Failed to send OTP");
      }
    } catch (error) {
      console.error("Error sending OTP:", error);
      setMessage("Failed to send OTP");
    } finally {
      setIsLoading(false); // Stop loading state
    }
  };
Enter fullscreen mode Exit fullscreen mode

Step 5: Rendering the Country Selection Modal and Passing Data
The CountrySelectModal component will render a list of countries, and when a user selects a country, it will trigger the handleCountrySelect function from the parent component to update the global state.

<CountrySelectModal 
  isModalVisible={isModalVisible} 
  setIsModalVisible={setIsModalVisible}
  countries={countries}
  handleCountrySelect={handleCountrySelect}
  isLoading={isLoading}
  searchTerm={searchTerm}
  handleSearchChange={handleSearchChange}
  filteredCountries={filteredCountries}
/>
Enter fullscreen mode Exit fullscreen mode

Step 6: Using the Global Settings in Other Parts of the App
Anywhere in your app, you can now access the baseUrl and dial_code using the useBaseUrl hook.

Example:

const { baseUrl, dialCode } = useBaseUrl();
console.log('Selected Dial Code:', dialCode); // Access the dial_code from the context
Enter fullscreen mode Exit fullscreen mode
  const { setNewDialcode, otp,phone} = useBaseUrl(); // Get the function to update the dial_code and OTP from context
const handleVerifyOtp = async () => {
    // Compare entered OTP with stored OTP in context
    console.log("OTP inside!");
    console.log(otp);
    console.log(inputOtp);
    if (inputOtp == otp) {
      console.log("OTP Verified Successfully!");
      console.log(otp);
      console.log("Sending OTP to phone:", phoneNumber); // Add this log

     // await verifyOtp(otp,phone); // Pass the phone number to send OTP
    } else {
      console.log("Invalid OTP!");
      Alert.alert("Error", "Invalid OTP entered.");
    }
  };
Enter fullscreen mode Exit fullscreen mode

This allows you to use the selected country’s dial_code globally, without passing it down through props.

Image description

Image description

Image description

Top comments (0)