UseEffect not triggering when used with MaterialTopTabNavigator

Viewed 1197

This is my first time using this platform to ask questions so please pardon me if my question does not seem well developed.

brief introduction

what I am trying to achieve is a dynamic Tab navigator, whereby the number of tabs changes depending on the number of elements in an array where this array changes in the number of elements over time, i.e :

{
  userIds : [1,2,3,4,5,6]
}

will render a tab navigator with 6 tabs

I am using react-redux for managing state and I have been following this tutorial on youtube just for your information: https://www.youtube.com/watch?v=9boMnm5X9ak&list=PLC3y8-rFHvwheJHvseC3I0HuYI2f46oAK

context

in the main code snippet the action FetchMonthlyTransIdAct() is being dispatched, this consist of 2 actions being dispatched in order :

RequestMonthlyTransaction → FetchSuccess or FetchFail

(as per mentioned in FetchMonthlyTransIdAct.js, ) the initial state is as follows and the changes each action does :

{
  loading : false
  Id : []
  error : ''
}
{
  loading : true //when RequestMonthlyTransaction is dispatched
  Id : []
  error : ''
}
{
  loading : false // When FetchSuccess is dispatched after RequestMonthlyTransaction
  Id : [1,2,3,4,5,6]// When FetchSuccess is dispacthed after RequestMonthlyTransaction
  error : ''
}
{
  loading : false //when FetchFail is dispacthed after RequestMonthlyTransaction
  Id : []
  error : 'some error message here' //when FetchFail is dispatched after RequestMonthlyTransaction
}

problem

so the problem that I am currently facing is that useEffect does not seem to trigger when I am rendering components with navigationContainer/ tab.navigator

here is the snippet of my code, I have narrowed down the source of the problem between asterisks

const Tab = createMaterialTopTabNavigator();

const mapStateToProps = state => {
  return {
    userData: state.MonthlyEntry
  }
}

const mapDispatchToProps = dispatch => {
  return {
    FetchMonthlyTransId: () => dispatch(FetchMonthlyTransIdAct())
  }
}

const EntryTabNavigator = ({userData, FetchMonthlyTransId}) => {

  useEffect (() => {
        FetchMonthlyTransId()
  }, [])
    console.log(userData.Id)

    if (userData.loading || userData.error != '') {
        return <View/>

    } else {
        return( 
             **************************************************************************************
             <NavigationContainer independent = {true}>
                <Tab.Navigator swipeEnabled = {true} tabBarOptions = {{scrollEnabled:true, tabStyle:{width:120}}}>
                    {userData.Id.map((data) => {return (<Tab.Screen key = {data.toString()} name = {data.toString()} component = {MonthlyTransactions} initialParams={{id:data.toString()}}/>)})}
                </Tab.Navigator>
             </NavigationContainer>
             **************************************************************************************
        )
    }
};

export default connect(mapStateToProps, mapDispatchToProps)(EntryTabNaviga

the error message simply that there was no screen for tab navigator to render (due to userData.Id being an empty array when it should not)

based on the console.log(userData.Id)

the expected output should be Array [1,2,3,4,5,6] but the actual output was Array [] which indicates that the useEffect was not triggered

I have tried replacing the snippet of code between the astericks with <View><Text>{userData.Id}</Text><View> and it was able to render as expected (returning a screen with the string representation of the array as the text), hence leading me to identify that the code snippet between the astericks is the problematic portion. I have also tried adding a console.log statement within useEffect and it does not output anything into the console when I have the code snippet in asterisks, however it does output into the console when I replaced the snippet of code between the astericks with <View><Text>{userData.Id}</Text><View>

should there be a similar problem to this that has already been asnwered, it would be much apppreciated if you could direct me to it, it would also be great if you could point me to resources to improve my knowledge with redux (prefreably beginner friendly) ! additional reference code (reducer and action) is below

Thank you in advance

FetchMonthlyTransIdAct.js


const requestMonthlyTransaction = () => {
  return {
    type: "REQUEST_MONTHLY_TRANSACTION",
  }
}

const fetchSucess = (ids) => {
  return {
    type: "FETCH_SUCCESS",
    payload: ids,
  }
}

const fetchFail = (error) => {
  return {
    type: "FETCH_FAILURE",
    payload: error,
  }
}

export const FetchMonthlyTransIdAct = () => {
  return (dispatch) => {
    dispatch(requestMonthlyTransaction())
    async function getId() {
      return require('../../data/DummyId.js').id //returns [1,2,3,4,5,6]
    }
    getId().then(
      id => dispatch(fetchSucess(id))
    ).catch(
      error => dispatch(fetchFail(error))
    )

  }
}

FetchMonthlyTransIdRed.js

const initialState = {
  loading:false,
  Id : [],
  error:''
}

const FetchMonthlyTransactionIdRed = (state = initialState, action) => {
  switch (action.type){
    case "REQUEST_MONTHLY_TRANSACTION":
      return {
        ...state,
        loading: true,
      }

    case "FETCH_SUCCESS":
      return {
        ...state,
        loading: false,
        Id: action.payload
      }

    case "FETCH_FAILURE":
      return {
        ...state,
        loading: false,
        error: action.payload
      }
    default: return state;
  }
}


export default FetchMonthlyTransactionIdRed;
2 Answers

after much tinkering, I manage to find a solution (or a workaround rather) to the problem. which is to add an initial element in the array of the Id attribute in the initial state in FetchMonthlyTransIdRed.js, that will allow the first render of the navigation component to occur without issues, and subsequently in the next re-render when FetchMonthlyTransId is dispatched Id is then updated with the array that I have imported

Use React Navigation's useFocusEffect, e.g.:

import { useFocusEffect } from '@react-navigation/native';

function Profile({ userId }) {
  const [user, setUser] = React.useState(null);

  useFocusEffect(
    React.useCallback(() => {
      const unsubscribe = API.subscribe(userId, user => setUser(user));
      return () => unsubscribe();
    }, [userId])
  );

  return <ProfileContent user={user} />;
}
Related