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

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

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