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:
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
AsyncStorage functions that you are using are asynchrounous functions. You will have to wait for them to be resolved.
Usage:
}
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.
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
: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:You need to wait to resolve the Promise when getting something from AsyncStorage.
In
AuthProvider
change useEffect towhere