How to change the label and the icon of React Navigation tab bar dynamically based on Redux state

I want to change the label and the icon of React Navigation Tab bar based on Redux state. I don’t know how to access the state of Redux inside the AppNavigator.js. I usually use useSelector hook to do that, but this is not a component and I can’t do that here.

I want to change the label and the icon for the Profile dynamically.

Here is my code for the AppNavigator.js:

import React from "react";
import { Platform } from "react-native";
import { createAppContainer } from "react-navigation";
import { createStackNavigator } from "react-navigation-stack";
import { createBottomTabNavigator } from "react-navigation-tabs";
import { createMaterialBottomTabNavigator } from "react-navigation-material-bottom-tabs";
import { Ionicons } from "@expo/vector-icons";

import HomeScreen from "../screens/HomeScreen";
import ProductDetailsScreen from "../screens/ProductDetailsScreen";
import ProductsScreen from "../screens/ProductsScreen";
import CartScreen from "../screens/CartScreen";
import CategoriesListScreen from "../screens/CategoriesListScreen";
import OrdersListScreen from "../screens/OrdersListScreen";
import OrderDetailsScreen from "../screens/OrderDetailsScreen";
import OrderSubmissionScreen from "../screens/OrderSubmissionScreen";
import ProfileScreen from "../screens/ProfileScreen";
import SearchScreen from "../screens/SearchScreen";
import LoginRegisterScreen from "../screens/LoginRegisterScreen";
import CodeEntryScreen from "../screens/CodeEntryScreen";
import Colors from "../constants/Colors";
import IconWithBadge from "../components/UI/IconWithBadge";

const HomeNavigator = createStackNavigator(
  {
    Home: HomeScreen,
    ProductDetails: ProductDetailsScreen,
    Categories: CategoriesListScreen,
    Products: ProductsScreen,
    Search: SearchScreen,
  },
  {
    defaultNavigationOptions: {
      headerShown: false,
    },
  }
);

const CartNavigator = createStackNavigator(
  {
    Cart: CartScreen,
    OrderSubmission: OrderSubmissionScreen,
    ProductDetails: ProductDetailsScreen,
    OrdersList: OrdersListScreen,
  },
  {
    defaultNavigationOptions: {
      headerShown: false,
    },
  }
);

const LoginRegisterNavigator = createStackNavigator(
  {
    LoginRegister: LoginRegisterScreen,
    CodeEntry: CodeEntryScreen,
  },
  {
    defaultNavigationOptions: {
      headerShown: false,
    },
  }
);

const OrdersNavigator = createStackNavigator(
  {
    OrdersList: OrdersListScreen,
    OrderDetails: OrderDetailsScreen,
  },
  {
    defaultNavigationOptions: {
      headerShown: false,
    },
  }
);

const ProfileNavigator = createStackNavigator({
  Profile: ProfileScreen,
});

const tabScreenConfig = {
  Profile: {
    screen: ProfileNavigator,
    navigationOptions: {
      tabBarLabel: "حسابي",
      tabBarIcon: (tabInfo) => {
        return (
          <Ionicons name="ios-person" size={25} color={tabInfo.tintColor} />
        );
      },
    },
  },
  Orders: {
    screen: OrdersNavigator,
    navigationOptions: {
      tabBarLabel: "طلباتي",

      tabBarOptions: {
        style: {
          // borderWidth: 0,
          // borderTopColor: "transparent"
        },
        labelStyle: {
          fontFamily: "cairo-bold",
        },
        tabStyle: {
          height: 60,
          paddingVertical: 5,
        },
        activeTintColor: Colors.primary,
      },
      tabBarIcon: (tabInfo) => {
        return (
          <Ionicons name="ios-paper" size={25} color={tabInfo.tintColor} />
        );
      },
    },
  },
  Cart: {
    screen: CartNavigator,
    navigationOptions: {
      tabBarVisible: false,
      tabBarLabel: "سلة التسوق",
      tabBarIcon: (tabInfo) => {
        return (
          <IconWithBadge name="ios-cart" size={25} color={tabInfo.tintColor} />
        );
      },
    },
  },
  Home: {
    screen: HomeNavigator,
    navigationOptions: {
      tabBarLabel: "تسوق",
      tabBarIcon: (tabInfo) => {
        return <Ionicons name="ios-home" size={25} color={tabInfo.tintColor} />;
      },
    },
  },
};


const MainAppTabsNavigator =
  Platform.OS === "android"
    ? createMaterialBottomTabNavigator(tabScreenConfig, {
        initialRouteName: "Home",
        tabBarOptions: {
          labelStyle: {
            fontFamily: "cairo-bold",
          },
          activeColor: Colors.primary,
          shifting: true,
        },
      })
    : createBottomTabNavigator(tabScreenConfig, {
        initialRouteName: "Home",
        tabBarOptions: {
          labelStyle: {
            fontFamily: "cairo-bold",
          },
          tabStyle: {
            height: 60,
            paddingVertical: 5,
          },
          activeTintColor: Colors.primary,
        },
      });

// handleTabPress = ({ navigation, defaultHandler }) => {
//   navigation.popToTop();
//   defaultHandler();
// };

const test = createStackNavigator(
  {
    tabs: MainAppTabsNavigator,
    LoginRegister: LoginRegisterNavigator,
  },
  {
    defaultNavigationOptions: {
      headerShown: false,
    },
  }
);

export default createAppContainer(test);

Here is my code for App.js:

import React, { useState, useEffect } from "react";
import { View, Text, StyleSheet } from "react-native";
import { AppLoading } from "expo";
import * as Font from "expo-font";
import { createStore, combineReducers, applyMiddleware } from "redux";
import { Provider, useDispatch } from "react-redux";
import { composeWithDevTools } from "redux-devtools-extension";
import ReduxThunk from "redux-thunk";
import * as SplashScreen from "expo-splash-screen";

import categoriesReducer from "./store/reducers/categories";
import productsReducer from "./store/reducers/products";
import cartReducer from "./store/reducers/cart";
import ordersReducer from "./store/reducers/orders";
import authReducer from "./store/reducers/auth";
import addressReduceer from "./store/reducers/address";
import AppNavigator from "./navigation/AppNavigator";
import { NavigationContainer } from '@react-navigation/native';

import * as productsActions from "./store/actions/products";
import * as categoriesActions from "./store/actions/categories";
import * as addressActions from "./store/actions/address";
import * as authActions from "./store/actions/auth";

const rootReducer = combineReducers({
  categories: categoriesReducer,
  products: productsReducer,
  cart: cartReducer,
  orders: ordersReducer,
  auth: authReducer,
  address: addressReduceer,
});

const store = createStore(
  rootReducer,
  composeWithDevTools(),
  applyMiddleware(ReduxThunk)
);

const fetchFonts = () => {
  return Font.loadAsync({
    "cairo-regular": require("./assets/fonts/Cairo-Regular.ttf"),
    "cairo-bold": require("./assets/fonts/Cairo-Bold.ttf"),
    "cairo-light": require("./assets/fonts/Cairo-Light.ttf"),
    "cairo-semiBold": require("./assets/fonts/Cairo-SemiBold.ttf"),
    lateef: require("./assets/fonts/LateefRegOT.ttf"),
    "mada-regular": require("./assets/fonts/Mada-Regular.ttf"),
    "mada-bold": require("./assets/fonts/Mada-Bold.ttf"),
    "openSans-regular": require("./assets/fonts/OpenSans-Regular.ttf"),
  });
};

const AppWrapper = () => {
  return (
    <Provider store={store}>
      <App />
    </Provider>
  );
};

const App = () => {
  const dispatch = useDispatch();
  const [fontLoaded, setFontLoaded] = useState(false);
  const [appIsReady, setAppIsReady] = useState(false);

  const preventAutoHide = async () => {
    try {
      await SplashScreen.preventAutoHideAsync();
    } catch (e) {
      console.warn(e);
    }
    prepareResources();
  };

  useEffect(() => {
    preventAutoHide();
  }, []);

  const prepareResources = async () => {
    try {
      await Promise.all([
        dispatch(productsActions.fetchProducts()),
        dispatch(categoriesActions.fetchCategories()),
        dispatch(productsActions.fetchPromotionProducts()),
        // eslint-disable-next-line prettier/prettier
        dispatch(addressActions.fetchAddress()),
        dispatch(authActions.fetchUserDetails()),
      ]);
      setAppIsReady(true);
      await SplashScreen.hideAsync();
    } catch (err) {
      console.log(err.message);
    }
  };

  if (!appIsReady) {
    return (
      <View style={styles.container}>
        <Text style={styles.text}>SplashScreen Demo! 👋</Text>
      </View>
    );
  }

  if (!fontLoaded) {
    return (
      <AppLoading
        startAsync={fetchFonts}
        onFinish={() => setFontLoaded(true)}
      />
    );
  }

  return (
    <Provider store={store}>
      <AppNavigator />
    </Provider>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
    backgroundColor: "#aabbcc",
  },
  text: {
    color: "white",
    fontWeight: "bold",
  },
});

export default AppWrapper;

Here is my code for the store reducer which I want to access the addresses state. If there is an address or not, I want to change the label and the icon based on that.

import { FETCH_ADDRESS, INTIALIZE_ADDRESS } from "../actions/address";
import Address from "../../models/address";

const initialState = {
  addresses: [],
};

export default (state = initialState, action) => {
  switch (action.type) {
    case INTIALIZE_ADDRESS:
      return {
        ...state,
        addresses: initialState.addresses,
      };
    case FETCH_ADDRESS:
      const newAddress = new Address(
        action.addressData.id,
        action.addressData.addressName,
        action.addressData.sector,
        action.addressData.streetNo,
        action.addressData.houseNo,
        action.addressData.region,
        action.addressData.city,
        action.addressData.landmark
      );
      return {
        ...state,
        addresses: state.addresses.concat(newAddress),
      };
  }
  return state;
};

Can you upgrade react navigation to the current version? That would remove your problem entirely, though you’d have to shift everything across to the component-based API