I am building a fitness app, and currently I am having a problem with React Native navigation stack, that I wasn’t having before and not sure why this is happening.
As you can see in the video, after you select the type of training and number of days, it should go to the screen with the filtered workouts (WorkoutSelection).
Edit: I tried moving the navigation.navigate() outside the async function and it does navigate to the correct page, so I’m assuming it has something to do with it being inside a async function.
Here’s the code for the confirm button:
async function confirmHandler() {
setIsLoading(true);
try {
const data = await getTrainingData(
selectedTrainingData.value,
selectedDaysData.value
);
setFilteredWorkouts(data);
setIsLoading(false);
navigation.navigate('WorkoutSelection');
} catch (error) {
console.log(error);
setIsLoading(false);
return (
<ErrorScreen message={'Something went wrong. Please try again.'} />
);
}
}
What is happening right now is whenever I click the confirm button, it goes to the home screen.
It does fetch the data from the API, I logged it to the console and it’s working so, that shouldn’t be the problem.
Here’s the navigation code:
function TrainingOptionsStack() {
return (
<Stack.Navigator
screenOptions={{
headerStyle: { backgroundColor: GlobalStyles.colors.background },
headerTintColor: 'white',
headerTitleStyle: {
fontFamily: 'open-sans-semi-bold',
},
}}
>
<Stack.Screen
name='WorkoutsScreen'
component={WorkoutsScreen}
options={{ title: 'Workouts' }}
/>
<Stack.Screen
name='SelectPhase'
component={BlockOptions}
options={{ title: 'Training Phase' }}
/>
<Stack.Screen
name='WorkoutSelection'
component={WorkoutSelection}
options={{
title: 'Select a workout',
}}
/>
<Stack.Screen
name='SelectDay'
component={SelectDay}
options={{ title: 'Select Day' }}
/>
<Stack.Screen name='WorkoutOfTheDay' component={WorkoutOfTheDay} />
<Stack.Screen
name='PreviewModal'
component={PreviewModal}
options={{ presentation: 'modal', title: false }}
/>
</Stack.Navigator>
);
}
function AppNavigation() {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={{
headerStyle: { backgroundColor: GlobalStyles.colors.background },
headerTintColor: 'white',
tabBarStyle: {
backgroundColor: GlobalStyles.colors.primary700,
paddingTop: 5,
height: 90,
},
headerTitleStyle: {
fontFamily: 'open-sans-semi-bold',
},
tabBarActiveTintColor: 'white',
}}
>
<Tab.Screen
name='Home'
component={HomeScreen}
options={{
title: 'Home',
tabBarIcon: ({ focused }) => {
return (
<Ionicons
name='home-outline'
size={34}
color={
focused
? GlobalStyles.colors.primary400
: GlobalStyles.colors.primary500
}
/>
);
},
}}
/>
<Tab.Screen
name='Workouts'
component={TrainingOptionsStack}
options={{
title: 'Workouts',
tabBarIcon: ({ focused }) => {
return (
<Ionicons
name='barbell-outline'
size={34}
color={
focused
? GlobalStyles.colors.primary400
: GlobalStyles.colors.primary500
}
/>
);
},
headerShown: false,
}}
/>
<Tab.Screen
name='RepMaxCalculator'
component={RepMaxCalculator}
options={{
title: 'Max Rep Calculator',
tabBarIcon: ({ focused }) => {
return (
<Ionicons
name='calculator-outline'
size={30}
color={
focused
? GlobalStyles.colors.primary400
: GlobalStyles.colors.primary500
}
/>
);
},
}}
/>
<Tab.Screen
name='ProgressChart'
component={ProgressChart}
options={{
title: 'Progress Chart',
tabBarIcon: ({ focused }) => {
return (
<Ionicons
name='bar-chart-outline'
size={30}
color={
focused
? GlobalStyles.colors.primary400
: GlobalStyles.colors.primary500
}
/>
);
},
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
And here’s the ‘WorkoutSelection’ component –
import React, { useEffect } from 'react';
import { FlatList, StyleSheet, View } from 'react-native';
import SelectWorkout from '../components/SelectWorkout';
import FlatButton from '../components/UI/buttons/FlatButton';
import { GlobalStyles } from '../constants/styles';
import useAppContext from '../store/AppContext';
import { doc, setDoc, serverTimestamp } from 'firebase/firestore';
import { db } from '../firebase/firebaseConfig';
function WorkoutSelection({ navigation }) {
const {
filteredWorkouts,
previewWorkoutHandler,
setWorkoutPreviewTitle,
setCurrentWorkout,
currentWorkout,
userIsAuthenticated,
} = useAppContext();
// useEffect(() => {
// const docRef = doc(db, 'users', userIsAuthenticated.uid);
// async function addCurrentWorkoutToDataBase() {
// await setDoc(docRef, 'CurrentWorkout', currentWorkout, { merge: true });
// }
// addCurrentWorkoutToDataBase();
// }, [currentWorkout]);
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<FlatButton
style={styles.headerButton}
onPress={() => navigation.replace('WorkoutsScreen')}
>
Cancel
</FlatButton>
),
});
}, []);
const id = filteredWorkouts
.flatMap((item) => item.workouts)
.flatMap((item) => item.id);
function previewHandler(item) {
previewWorkoutHandler(id[0]);
setWorkoutPreviewTitle(item.title);
navigation.navigate('PreviewModal');
}
function selectWorkoutHandler(item) {
setCurrentWorkout([item]);
navigation.navigate('WorkoutsScreen');
}
return (
<View style={styles.rootContainer}>
<FlatList
data={filteredWorkouts}
renderItem={({ item }) => (
<SelectWorkout
name={item.title}
onSelect={() => selectWorkoutHandler(item)}
onShowPreview={() => previewHandler(item)}
/>
)}
keyExtractor={(item) => item.id}
/>
</View>
);
}
export default WorkoutSelection;
const styles = StyleSheet.create({
rootContainer: {
backgroundColor: GlobalStyles.colors.background,
flex: 1,
},
headerButton: {
textDecorationLine: 'none',
marginRight: 24,
fontSize: 16,
fontFamily: 'open-sans-semi-bold',
},
});
Here’s a little video to demonstrate the problem –
https://drive.google.com/file/d/1OoKlj2tZsL6sYqpcUnKUcQhAjmhTZGJZ/view?usp=share_link
What I expect is to go to the ‘WorkoutSelection" screen so the user can complete the workout selection flow and I can update it in Firebase.
Appreciate any help!
3
Answers
I figured out the problem. I had to go back a few commits and start adding one thing at a time to try to get to the point where I was having the same problem.
The problem was in the App.js, the rootFunction that I created wasn't returning anything conditionally as I wanted. I had to restructure it a bit and got it working.
Totally missed it. Thanks all for the help!
Have you import all the component on above page?
e.g. if you want to use
WorkoutSelection
component then import that.like this.
import {WorkoutSelection} from ‘path here’.
and then use that component for the navigation.
Hope you got my point.
It is happening because you are using
replace
function instead ofgoBack
ornavigate
inWorkoutSelection
So first change from
replace
togoBack
ornavigate
after selecting values
WorkoutSelection
, if you want to refreshWorkoutsScreen
so your selected filter can be apply then you have two ways to do thatcall api on focus change in
WorkoutsScreen
(follow below mentioned link )https://reactnavigation.org/docs/function-after-focusing-screen/
fire event from
WorkoutSelection
before callinggoBack
ornavigate
and that event should be listened onWorkoutsScreen
so when you get event , you can call api and refresh screen as per filter selection