Home > database >  undefined is not an object (evaluating 'tabs.pop().name')
undefined is not an object (evaluating 'tabs.pop().name')

Time:02-21

I am new to react native and I am trying to access a context from a bottomtab navigator where the context was created in the stack navigator stackCreate and the tab navigator is nested in stackCreate, but when i try to add a new object to the context in addTab i get: undefined is not an object (evaluating 'tabs.pop().name'), but when i call console.log(tabs.pop().name.replace("Step ", "")); it works, so I am confused why this is happening.

navigators;

const Stack = createStackNavigator();

const StackCreate = createStackNavigator();

const StepTabs = createBottomTabNavigator();

const StepStack = () => {

    return (
        <tabContext.Consumer>
        {({tabs}) => (
        <StepTabs.Navigator
        screenOptions={{
        tabBarHideOnKeyboard: true
        }}  
        >
            {
                tabs.map((tab) => <StepTabs.Screen key={tab.name} name={tab.name} stepTitle={tab.stepTitle} component={tab.component} 
                options={{
                    tabBarLabel: `${tab.stepTitle}`,
                }}
                /> )
            }
        </StepTabs.Navigator>
        )}
        </tabContext.Consumer>
    )
}

const CreateStack = () => {

    const [tabs, setTabs] = useState([
    {
        name: 'Step 1',
        component: CreateStep,
        stepTitle: 'Step 1',
    }
    ]);
    const [ingredientsList, setIngredientsList] = useState([]);

    
    return (
        <ingredientContext.Provider value={{ingredientsList, setIngredientsList}}>
        <tabContext.Provider value={{tabs, setTabs}}>
        <StackCreate.Navigator>   
        <StackCreate.Screen name="Create" component={Create}/>
        <StackCreate.Screen name="IngredientStep" component={IngredientStep}/>
        <StackCreate.Screen name="MyModal" component={ModalScreen} 
        screenOptions={{ presentation: 'modal' }}/>
        <StackCreate.Screen name="StepStack" component={StepStack}/>
        </StackCreate.Navigator>
        </tabContext.Provider>
        </ingredientContext.Provider>

    )
}

const RootStack =() => {

    return (
        <userContext.Consumer>
            {({UserId}) => (            
            <NavigationContainer>
                <Stack.Navigator
                    screenOptions={{
                        headerStyle: {
                            backgroundColor: "transparent",
                        },
                        headerShown: false,
                        headerTintColor: Colors.tertiary,
                        headerTransparent: true,
                        headerTitle: '',
                        headerLeftContainerStyle: {
                            paddingLeft: 20,
                        }
                    }}
                >
                    {UserId ? (
                    <Stack.Group>
                    <Stack.Screen name="Home" component={Home}/>
                    <Stack.Screen name="Recipes" component={Recipes} />
                    <Stack.Screen name="CreateStack" component={CreateStack} />
                    </Stack.Group>
                    ) : (
                    <Stack.Group>
                    <Stack.Screen name="Login" component={Login}/>
                    <Stack.Screen name="Register" component={Register}/>
                    </Stack.Group>
                    )}
                </Stack.Navigator> 
            </NavigationContainer>
            )
            }   
        </userContext.Consumer>
    );

}

export default RootStack;

createStep

const CreateStep = ({navigation, route}) => {  

    const {tabs, setTabs } = useContext(tabContext);


    const removeTab = (route) => {
        setTabs(tabs => tabs.filter(tab => {if(tab.stepTitle != route.stepTitle){if(parseInt(tab.stepTitle.replace("Step ", "")) > parseInt(route.stepTitle.replace("Step ", ""))){tab.stepTitle = 'Step '   (parseInt(tab.stepTitle.replace("Step ", "")) - 1);}     return tab;}}));
    }

    const addTab = () => {
        //console.log(tabs.pop().name.replace("Step ", ""));
        setTabs(tabs => [...tabs, 
            {
                name: 'Step '   (parseInt(tabs.pop().name.replace("Step ", ""))   1),
                component: CreateStep,
                stepTitle: 'Step '   (parseInt(tabs.pop().stepTitle.replace("Step ", ""))   1),
            }
        ])
    }

    const finishRecipe = () => {
        navigation.navigate("Recipes");
    }

    return (
        <StyledContainer>
            <InnerContainer>
                <StyledFormArea>    
                    <StyledTextInputLarge multiline={true} />
                </StyledFormArea>
                <View style={{
                    flex: 1,
                    flexDirection: 'row',
                    justifyContent: 'space-between',
                    height: 10,
                    width: '100%',
                }}>
                {(tabs.length < 10) ? (<Button title="Add Step" onPress={addTab}/>) : null}
                {(route.name != 'Step 1') ? (<Button title="Remove Step" onPress={() => removeTab(route)}/>) : null }
                <Button title="Finish Recipe" onPress={finishRecipe}/>
                </View>
            </InnerContainer>
        </StyledContainer> 
    )

}

export default CreateStep;

CodePudding user response:

First, when you call tabs.pop(), it remove the last value from tabs array and return this value.
I guess you're calling tabs.pop() twice:

  • The first time you call, it work fine because tabs is not empty.
  • But when you call the second time, tabs is empty at this time and tabs.pop() return undefined. Of course, undefined.name make a error.

Example:

const plants = ['broccoli'];
console.log(plants.pop());//"broccoli"
console.log(plants.pop());//undefined

You can read: array.pop() for more infomation.

I suggest that I can assign returned value in tabs for a variable like tab:

const tab = tabs.pop()

After that, I use tab instead of tabs.pop()

  • Related