skip to Main Content

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


  1. Chosen as BEST ANSWER

    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!


  2. 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.

    Login or Signup to reply.
  3. It is happening because you are using replace function instead of goBack or navigate in WorkoutSelection

    So first change from replace to goBack or navigate

    after selecting values WorkoutSelection , if you want to refresh WorkoutsScreen so your selected filter can be apply then you have two ways to do that

    1. call api on focus change in WorkoutsScreen (follow below mentioned link )
      https://reactnavigation.org/docs/function-after-focusing-screen/

    2. fire event from WorkoutSelection before calling goBack or navigate and that event should be listened on WorkoutsScreen so when you get event , you can call api and refresh screen as per filter selection

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search