Lifting a state so I can modify it with an onPress
Asked Answered
I

1

7

I am trying to figure out how lifting states work. Currently, I am trying to use onPress in a different component to change the state. In the snack demo I provided below, ListHome and MapHome do not respond for some reason, but you will still see what I am trying to accomplish.

I want the map and list "buttons" to accomplish what the "click for this" does.

Demo - No errors with the current implementation, just no response other than the flash from touchable opacity. (Also remember the snack does not respond for some reason. My local device works fine)

EDIT: So to be clear, I want to be able to access and change the state whichComponentToShow from another component. i.e the ListHome Component.

export default class Home extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      visible: true,
      whichComponentToShow: 'Screen1',
    };
  }

  goToMap = () => {
    this.setState({ whichComponentToShow: 'Screen2' });
  };

  render(){
    if(this.state.whichComponentToShow === 'Screen1'){
      return(
       <View style={{backgroundColor: '#d1cfcf' ,flex: 1}}>
        
        <ListHome
          renderMap = {this.goToMap.bind(this)}
        />
  }
}
export default class ListHome extends React.Component {
  
goToMap = () => {
  this.props.renderMap();
}

<TouchableOpacity onPress={() => this.goToMap.bind(this)}>
     <View style={styles.conditionalMap}>   
         <View style={{justifyContent: 'center', alignItems: 'center'}}>       
             <Text style={{color: 'black', fontSize: scale(15)}}>
                 Map
             </Text>                  
         </View>
     </View>
</TouchableOpacity>
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { MaterialCommunityIcons } from '@expo/vector-icons';

import VenueDetailsScreen from './screens/VenueDetails';
import CarouselGallary from './screens/Carousel';
import Home from './screens/Home';
import Friends from './screens/Friends';
import Profile from './screens/Profile';

const Tab = createBottomTabNavigator();

function MyTabs() {
  return (
    <Stack.Navigator initialRouteName="Home">
      <Stack.Screen
        name="Home"
        component={Home}
        options={{ headerShown: false }}
      />
      <Stack.Screen
        name="VenueDetails"
        component={VenueDetailsScreen}
        options={{ headerShown: false }}
      />
      <Stack.Screen
        name="CarouselGallary"
        component={CarouselGallary}
        options={{ headerShown: false }}
      />
      <Stack.Screen
        name="Friends"
        component={Friends}
        options={{ headerShown: false }}
      />
      <Stack.Screen
        name="Profile"
        component={Profile}
        options={{ headerShown: false }}
      />
    </Stack.Navigator>
  );
}

export default function App() {
  return (
    <NavigationContainer>
      <Tab.Navigator
        initialRouteName="Home"
        screenOptions={{
          tabBarActiveTintColor: '#F60081',
          tabBarInactiveTintColor: '#4d4d4d',
          tabBarStyle: {
            backgroundColor: '#d1cfcf',
            borderTopColor: 'transparent',
          },
        }}>
        <Tab.Screen
          name="Home"
          component={MyTabs}
          options={{
            tabBarLabel: 'Home',
            headerShown: false,
            tabBarIcon: ({ color, size }) => (
              <MaterialCommunityIcons name="home" color={color} size={size} />
            ),
          }}
        />
        <Tab.Screen
          name="Friends"
          component={Friends}
          options={{
            tabBarLabel: 'Friends',
            tabBarIcon: ({ color, size }) => (
              <MaterialCommunityIcons
                name="account-group"
                color={color}
                size={size}
              />
            ),
          }}
        />
        <Tab.Screen
          name="Profile"
          component={Profile}
          options={{
            tabBarLabel: 'Profile',
            tabBarIcon: ({ color, size }) => (
              <MaterialCommunityIcons
                name="account"
                color={color}
                size={size}
              />
            ),
          }}
        />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

const Stack = createStackNavigator();
Interfaith answered 13/9, 2021 at 5:17 Comment(3)
It looks like we're missing some context, I don't see where the Home component is used.Alcorn
You can click the demo and see it being used in App.js. Its a big file I didnt want to extend the post.Interfaith
@RobertCorponoi I added it to the post as wellInterfaith
S
4

What you are trying to achieve is a common use case, which is to update the state in an ancestor component. You essentially have a component tree that looks like this:

enter image description here

You are trying to update the state of the Home component from the ListHome component with the renderMap prop that sets the state of the Home screen. This makes sense, so you have basic principle down, the reason why it is not working is due to a minor mistake in the ListHome component

<View style={styles.conditionalMap}>
  <TouchableOpacity onPress={() => this.goToMap.bind(this)}>
    // In the above line, change to this.goToMap()

    <View style={{ justifyContent: 'center', alignItems: 'center' }}>
      <Text style={{ color: 'black', fontSize: scale(15) }}>Map</Text>
    </View>
  </TouchableOpacity>
</View>

This is what the bind method does:

The bind() method creates a new function that, when called, has this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

So the way onPress is currently defined, it will create a new function, but that function is never actually called. Changing this.goToMap.bind(this) to this.goToMap() should solve your problem.

Swat answered 15/9, 2021 at 19:15 Comment(4)
Lets say I wanted to be able to access and change the state in Home from a child component of List Home. Would it be done in the same way or would I have to do something differently?Interfaith
Would the renderMap function I assign to ListHome when rendering it in Home be passed down to the child component of ListHome? @SwatInterfaith
Yes, passing down the renderMap as a prop to a child component would make it possible to update the state of the Home component from the child component. An alternative is to use a Context, you can read more about that here: reactjs.org/docs/context.htmlSwat
Thank you! I appreciate it very much.Interfaith

© 2022 - 2024 — McMap. All rights reserved.