skip to Main Content

I am trying to migrate react to react native since I want to make an application next after I made the website, but since some syntaxes of react-native seems to be different than typical react. I am just fairly new to react-native, so I think the problem is with the way react-native handle token. Graphql queries and mutations seems to be working fine but my jwt token and authentication seems to take a hit.

Code


apolloClient.js

import { ApolloClient } from "@apollo/client/core";
import { InMemoryCache } from "@apollo/client/cache";
import { setContext } from "@apollo/client/link/context";
import { createUploadLink } from "apollo-upload-client";
import AsyncStorage from "@react-native-async-storage/async-storage";

const httpLink = createUploadLink({
  uri: "http://localhost:5001/graphql",
});

const authLink = setContext(async () => {
  const token = await AsyncStorage.getItem("jwtToken");
  return {
    headers: {
      Authorization: token ? `Bearer ${token}` : "",
    },
  };
});

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
});

export default client;

auth.js

import { useReducer, createContext, useEffect } from "react";
import jwtDecode from "jwt-decode";
import AsyncStorage from "@react-native-async-storage/async-storage";

const initialState = {
  user: null,
};

const getToken = async () => {
  try {
    const storedToken = await AsyncStorage.getItem("jwtToken");

    if (storedToken !== null) {
      const decodedToken = jwtDecode(AsyncStorage.getItem("jwtToken"));
      const expirationData = decodedToken.exp;
      const currentDate = Date.now / 1000;
      if (expirationData >= currentDate) {
        initialState.user = decodedToken;
      }
    }
  } catch (err) {
    console.log(err);
  }
};

getToken();

const AuthContext = createContext({
  user: null,
  login: (userData) => {},
  logout: () => {},
});

function authReducer(state, action) {
  switch (action.type) {
    case "LOGIN":
      return {
        ...state,
        user: action.payload,
      };
    case "LOGOUT":
      return {
        ...state,
        user: null,
      };
    default:
      return state;
  }
}

function AuthProvider(props) {
  const [state, dispatch] = useReducer(authReducer, initialState);
  //   const router = useRouter();

  function login(userData) {
    AsyncStorage.setItem("jwtToken", userData.token);

    dispatch({
      type: "LOGIN",
      payload: userData,
    });
  }

  function logout() {
    AsyncStorage.removeItem("jwtToken");
    // toast.error("Logged out successfully", shortToastConfig);
    dispatch({ type: "LOGOUT" });
  }

  useEffect(() => {
    if (state.user) {
      const decodedToken = jwtDecode(AsyncStorage.getItem("jwtToken"));
      const timeLeft = decodedToken.exp * 1000 - Date.now();
      setTimeout(() => {
        dispatch({
          type: "LOGOUT",
        });
        AsyncStorage.removeItem("jwtToken");
        // toast.error("Session Expired", shortToastConfig);
        // router.push("/login");
      }, timeLeft);
    }
  }, [state]);

  return (
    <AuthContext.Provider
      value={{ user: state.user, login, logout }}
      {...props}
    />
  );
}

export { AuthContext, AuthProvider };

App.js

import React from 'react';
import { ApolloProvider } from '@apollo/client';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { useFonts } from 'expo-font';
import {
  MyShop,
  SignIn,
  SignUp,
  Navigator,
  Chats,
  ShoppingCart,
  Settings,
  // Country,
  Me,
  Home,
  Categories,
  SubCategories,
  Products,
} from './screens';
import { AuthProvider } from './context/auth';
import client from './context/apolloClient';

export default function App() {
  LogBox.ignoreAllLogs(true);
  const Stack = createStackNavigator();

  return (
    <ApolloProvider client={client}>
      <AuthProvider>
        <NavigationContainer>
          <Stack.Navigator
            screenOptions={{ headerShown: false }}
            initialRouteName="Navigator"
          >
            <Stack.Screen
              name="Navigator"
              component={Navigator}
              options={{ gestureEnabled: false }}
            />
            <Stack.Screen
              name="Home"
              component={Home}
              options={{ gestureEnabled: false }}
            />
            {/* <Stack.Screen
            name="Country"
            component={Country}
            options={{ gestureEnabled: false }}
          /> */}
            <Stack.Screen
              name="SignIn"
              component={SignIn}
              options={{ gestureEnabled: false }}
            />
            <Stack.Screen
              name="SignUp"
              component={SignUp}
              options={{ gestureEnabled: false }}
            />
            <Stack.Screen
              name="Me"
              component={Me}
              options={{ gestureEnabled: false }}
            />
            <Stack.Screen name="MyShop" component={MyShop} />
            <Stack.Screen name="ShoppingCart" component={ShoppingCart} />
            <Stack.Screen name="Chats" component={Chats} />
            <Stack.Screen name="Settings" component={Settings} />

            {/* Other Pages */}
            <Stack.Screen name="Categories" component={Categories} />
            <Stack.Screen name="SubCategories" component={SubCategories} />
            <Stack.Screen name="Products" component={Products} />
          </Stack.Navigator>
        </NavigationContainer>
      </AuthProvider>
    </ApolloProvider>
  );
}

Error Shown:

enter image description here

If you need more code I can be transparent and edit this post, and if u don’t understand what I mean or trying to point out please comment down below if you can, and if you would be kind enough also to give me some few tips and advice on react-native so I could learn more about it. Thank you!!

3

Answers


  1. AsyncStorage functions that you are using are asynchrounous functions. You will have to wait for them to be resolved.

    Usage:

    const getData = async () => {
    try {
      const value = await AsyncStorage.getItem('@storage_Key')
      if(value !== null) {
        // value previously stored
      }
    } catch(e) {
      // error reading value
    }
    

    }

    // In your case the key is `jwtToken`
    const token = await AsyncStorage.getItem("jwtToken");
    

    More info and source here:

    https://react-native-async-storage.github.io/async-storage/docs/usage

    Try to make your code work with a hardcoded JWT token and then add the async storage functionality.

    Login or Signup to reply.
  2. If the code in question is the one that you’re currently using, then there is still a problem with how you’re using asynchronous functions.

    E.g. in auth.js:

    const storedToken = await AsyncStorage.getItem("jwtToken");
    
    if (storedToken !== null) {
       const decodedToken = jwtDecode(AsyncStorage.getItem("jwtToken"));
    ...
    }
    

    You get the stored token in the correct way. But then you call jwtDecode and instead of passing the stored token you pass it an asynchronous function. jwtDecode expects a string. There are at least two places where you do it this way. You should be passing the stored token instead:

    const decodedToken = jwtDecode(storedToken);
    
    Login or Signup to reply.
  3. You need to wait to resolve the Promise when getting something from AsyncStorage.

    In AuthProvider change useEffect to

    useEffect(() => {
      if (state.user) {
        logout();
      }
    }, [state]);
    

    where

    const logout = async () => {
      const decodedToken = jwtDecode(await AsyncStorage.getItem("jwtToken"));
      const timeLeft = decodedToken.exp * 1000 - Date.now();
      setTimeout(() => {
        dispatch({
          type: "LOGOUT",
        });
        AsyncStorage.removeItem("jwtToken");
        // toast.error("Session Expired", shortToastConfig);
        // router.push("/login");
      }, timeLeft);
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search