I'm new to React Native and I can't figure out how to make my Auth Flow work with react-navigation v6. I have a file named AuthContext.js, which is where I declare my Context object and manage the Auth state. I suspect the issue has something to do with the way I'm exporting AuthContext and or the way I'm using AuthContext in my App.js.
Any help is greatly appreciated!
AuthContext.js
import React, { createContext, useReducer, useEffect, useMemo } from 'react';
import * as SecureStore from 'expo-secure-store';
import myAPI from './myAPI'
// declare AuthContext as a Context object
const AuthContext = createContext();
// reducer to manage auth state
const [state, dispatch] = useReducer(
(prevState, action) => {
switch (action.type) {
case 'AUTH_ERROR':
return {
...prevState,
errorMessage: action.error,
};
case 'CLEAR_AUTH_ERROR':
return {
...prevState,
errorMessage: '',
};
case 'LOG_IN':
return {
...prevState,
isLogout: false,
userToken: action.token,
};
case 'LOG_OUT':
return {
...prevState,
isLogout: true,
userToken: null,
};
case 'RESTORE_TOKEN':
return {
...prevState,
userToken: action.token,
isLoading: false,
};
default:
return state;
}
},
{
isLoading: true,
isLogout: false,
userToken: null,
//errorMessage: '',
}
);
//**** fetch token action ****//
//**********************************//
useEffect(() => {
const fetchToken = async () => {
let userToken;
try {
userToken = await SecureStore.getItemAsync('userToken');
} catch (err) {
// Restoring token failed
console.log('Unable to fetch token.');
}
// After restoring token, we may need to validate it
dispatch({ type: 'RESTORE_TOKEN', token: userToken });
};
fetchToken();
}, []);
//**********************************//
//**** signUp, logIn, and logOut actions ****//
//*********************************************//
const authContext = useMemo(
() => ({
signUp: async ({ email, username, password }) => {
try {
const response = await myAPI.post('/public/auth/users', {
email,
username,
password,
});
console.log('\n', response.status);
console.log('\n', response.data);
// to log the user in after signup, store the token and dispatch the LOG_IN action
// await SecureStore.setItemAsync('userToken', response.data.access_token);
// dispatch({ type: 'LOG_IN', token: response.data.access_token });
} catch (err) {
dispatch({ type: 'AUTH_ERROR', error: err.response.data.detail });
console.log('\n', err.response.status);
console.log('\n', err.response.data);
}
},
logIn: async ({ username, password }) => {
var qs = require('query-string');
const config = {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
};
try {
const response = await myAPI.post(
'/public/auth/users/login',
qs.stringify({ username, password }),
config
);
console.log('\n', response.status);
await SecureStore.setItemAsync('userToken', response.data.access_token);
dispatch({ type: 'LOG_IN', token: response.data.access_token });
} catch (err) {
dispatch({ type: 'AUTH_ERROR', error: err.response.data.detail });
console.log('\n', err.response.status);
console.log('\n', err.response.data);
}
},
logOut: async () => {
await SecureStore.deleteItemAsync('token');
dispatch({ type: 'LOG_OUT' });
},
clearErrorMessage: () => dispatch({ type: 'CLEAR_AUTH_ERROR' }),
}),
[]
);
//*********************************************//
export const AuthProvider = ({ children }) => {
return (
<AuthContext.Provider value={authContext}>{children}</AuthContext.Provider>
);
};
export default AuthContext;
App.js
// Import Installed Libraries
import * as React from 'react';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import 'react-native-gesture-handler';
import { Ionicons, FontAwesome, Foundation } from '@expo/vector-icons';
// Import Local Files
import SignupScreen from './components/SignupScreen';
import LoginScreen from './components/LoginScreen';
import FeedScreen from './components/FeedScreen';
import SearchScreen from './components/SearchScreen';
import ProfileScreen from './components/ProfileScreen';
import SettingsScreen from './components/SettingsScreen';
import LoadingScreen from './components/LoadingScreen';
import { AuthProvider } from './components/AuthContext';
// Authentication Flow
const AuthStack = createNativeStackNavigator();
const AuthFlow = () => (
<AuthStack.Navigator>
<AuthStack.Screen name="Login" component={LoginScreen} />
<AuthStack.Screen name="SignUp" component={SignupScreen} />
</AuthStack.Navigator>
);
// Feed Flow
const FeedStack = createNativeStackNavigator();
const FeedFlow = () => (
<FeedStack.Navigator initialRouteName="Feed">
<FeedStack.Screen name="Feed" component={FeedScreen} />
</FeedStack.Navigator>
);
// Search Flow
const SearchStack = createNativeStackNavigator();
const SearchFlow = () => (
<SearchStack.Navigator initialRouteName="Search">
<SearchStack.Screen name="Search" component={SearchScreen} />
</SearchStack.Navigator>
);
// Profile Flow
const ProfileStack = createNativeStackNavigator();
const ProfileFlow = () => (
<ProfileStack.Navigator initialRouteName="Profile">
<ProfileStack.Screen name="Profile" component={ProfileScreen} />
<ProfileStack.Screen name="Settings" component={SettingsScreen} />
</ProfileStack.Navigator>
);
// Bottom Tap App Flow
const AppTabs = createBottomTabNavigator();
const AppFlow = () => (
<AppTabs.Navigator
screenOptions={{
tabBarShowLabel: false,
tabBarInactiveTintColor: '#858585',
tabBarActiveTintColor: '#000000',
}}>
<AppTabs.Screen
name="FeedFlow"
component={FeedFlow}
headerMode="none"
options={{
tabBarIcon: ({ color }) => (
<Foundation name="home" size={25} color={color} />
),
headerShown: false,
}}
/>
<AppTabs.Screen
name="SearchFlow"
component={SearchFlow}
options={{
tabBarIcon: ({ color }) => (
<FontAwesome name="search" size={25} color={color} />
),
headerShown: false,
}}
/>
<AppTabs.Screen
name="ProfileFlow"
component={ProfileFlow}
options={{
tabBarIcon: ({ color }) => (
<Ionicons name="ios-person" size={25} color={color} />
),
headerShown: false,
}}
/>
</AppTabs.Navigator>
);
const RootStack = createNativeStackNavigator();
const RootStackFlow = () => {
const { state } = React.useContext(authContext);
if (state.isLoading) {
// We haven't finished checking for the token yet
return <LoadingScreen />;
}
return (
<RootStack.Navigator>
{state.userToken == null ? (
// No token found, user isn't logged in
<RootStack.Screen
name="AuthStack"
component={AuthFlow}
options={{
headerShown: false,
}}
/>
) : (
// User is logged in
<RootStack.Screen
name="AppTabs"
component={AppFlow}
options={{
headerShown: false,
}}
/>
)}
</RootStack.Navigator>
);
};
export default () => {
return (
<AuthProvider>
<RootStackFlow />
</AuthProvider>
);
};
package.json
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"@react-native-async-storage/async-storage": "^1.15.14",
"@react-navigation/bottom-tabs": "^6.0.9",
"@react-navigation/drawer": "^6.1.8",
"@react-navigation/native": "^6.0.6",
"@react-navigation/native-stack": "^6.2.5",
"axios": "^0.24.0",
"expo": "^43.0.3",
"native-base": "^3.2.2",
"query-string": "^7.0.1",
"react": "^17.0.2",
"react-native": "^0.64.3",
"react-native-gesture-handler": "^1.10.2",
"react-native-masked-text": "~1.13.0",
"react-native-reanimated": "^2.2.4",
"react-native-safe-area-context": "^3.3.2",
"react-native-screens": "~3.8.0",
"react-native-svg": "^12.1.1",
"react-native-switch-selector": "^2.1.4",
"react-native-tab-view": "^3.1.1",
"expo-secure-store": "~11.0.3"
},
"devDependencies": {
"@babel/core": "^7.16.0"
},
"private": true
}
CodePudding user response:
Here is a working example: expo auth flow
if you want to use the dispatch
for example from:
const LoginScreen = () => {
const { state, dispatch } = React.useContext(AuthContext);
// here you can use a function to login or something, and dispatch it.
return (
<View>
<Text>Login Screen</Text>
</View>
);
};