Vinay Revankar
Vinay Revankar

Reputation: 832

React component not re-rendering on state change using setState

I have a HomeScreen component which has button. I am trying to popup a modelview(Seperate component ie PopUpView) on the button click. In PopUpView i am sending its visibility as prop (isVisible).On the click of the button i am trying to change the state value popUpIsVisible from false to true. Hoping that this would re-render and popup my model view(Note this is working fine if i explicitly pass true). However with the state change it look like the render function is not being called and the popUp is not being displayed. Thanks for your help in advance

import React from 'react';
import { View, StyleSheet, Text, Button, TouchableHighlight, Alert, Dimensions} from 'react-native';
import { createStackNavigator, createAppContainer } from 'react-navigation';
import PopUpView from './src/PopUpView';


class HomeScreen extends React.Component {

  constructor(props) {
      super(props);
      this.state = {
        popUpIsVisible: false,
      };
    }

  setPopUpIsVisible(isVisible){
    this.setState({popUpIsVisible: isVisible });
  }

  render() {
    this.setPopUpIsVisible = this.setPopUpIsVisible.bind(this);
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor:"blue" }}>
        <Text>Home Screen</Text>
          <PopUpView isVisible={this.state.popUpIsVisible}/>
          <Button onPress={() => {this.setPopUpIsVisible(true)}}  title="Open PopUp Screen"/>
      </View>
    );
  }
}

const RootStack = createStackNavigator(
  {
    Home: HomeScreen
  },
  {
    initialRouteName: 'Home',
  }
);

const AppContainer = createAppContainer(RootStack);

export default class App extends React.Component {
  render() {
    return <AppContainer />;
  }
}

PopUpView.js

import React from 'react';
import { View, Modal,StyleSheet, Text, TouchableOpacity, Dimensions} from 'react-native';
import { TabView, TabBar,SceneMap } from 'react-native-tab-view';
import Icon from 'react-native-vector-icons/SimpleLineIcons';

const FirstRoute = () => (
  <View style={{ flex: 1, backgroundColor: '#ff4081' }} />
);
const SecondRoute = () => (
  <View style={{ flex: 1, backgroundColor: '#673ab7' }} />
);
const ThirdRoute = () => (
  <View style={{ flex: 1, backgroundColor: '#673ab7' }} />
);


export default class PopUpView extends React.Component {
  constructor(props) {
      super(props);
      this.state = {
          modalVisible:this.props.isVisible,
          index: 0,
          routes: [
            { key: 'first', title: 'HIGHLIGHTS' },
            { key: 'second', title: 'AMENITIES' },
            { key: 'third', title: 'FACILITIES' },
          ],
        };
    }
    setModalVisible(visible) {
      this.setState({modalVisible: visible});
    }

renderHeader = props => <TabBar
    {...props}
    indicatorStyle={{backgroundColor: 'red'}}
    tabStyle={styles.bubble}
    labelStyle={styles.noLabel}
/>;

  render() {
      return (
      <Modal
          animationType="slide"
          transparent={true}
          visible={this.state.modalVisible}
          onRequestClose={() => {
            Alert.alert('Modal has been closed.');
          }}>
          <View style={styles.container}>
            <View style={styles.navBar}>
              <Text style={styles.navBarTitle}>Test</Text>
              <TouchableOpacity
                onPress={() => {
                  this.setModalVisible(!this.state.modalVisible);
                }}>
              <Icon style={styles.closeButton} name="close" size={35} color="grey" />
              </TouchableOpacity>
            </View>
            <TabView
              navigationState={this.state}
              renderScene={SceneMap({
                first: FirstRoute,
                second: SecondRoute,
                third: ThirdRoute,
                })}
              onIndexChange={index => this.setState({ index })}
              initialLayout={{ width: Dimensions.get('window').width }}
              renderTabBar={props =>
                      <TabBar
                          {...props}
                          style={{ backgroundColor: 'white' }}
                          indicatorStyle={{backgroundColor: 'black'}}
                          tabStyle={styles.bubble}
                          labelStyle={styles.label}
                      />
                  }
              />
          </View>
        </Modal>
      );
    }
}

const styles = StyleSheet.create({
  container: {
    flex:1,
    margin: 50,
    marginLeft: 20,
    marginRight: 20,
    marginBottom: 20,
    backgroundColor: "white",
    borderWidth: 1,
    borderColor: "grey",
    flexDirection: 'column'
  },
  navBar:{
    height:70,
    justifyContent: 'space-between',
    alignItems: 'center',
    flexDirection: 'row',
    borderBottomColor: 'lightgrey',
    borderBottomWidth: 1,
  },
  navBarTitle:{
    fontSize: 25,
    fontFamily: 'Optima',
    paddingLeft:15,
  },
  closeButton:{
    paddingRight:12,
  },
  label: {
        color: 'black'
    }
})

Upvotes: 0

Views: 1873

Answers (2)

Guruparan Giritharan
Guruparan Giritharan

Reputation: 16334

The problem with your code is that you are using the state inside the PopUpView which does not change when you change the external prop. to fix this you should use the componentwillreceiveprops and update your state accordingly.

componentWillReceiveProps(nextProps){
  if(this.props.isVisible!=nextProps.isVisible){
    this.setState({modalVisible:nextProps.isVisible})
  }
}

The better approach will be using the this.props.isVisible as the visible prop for the Model. In this scenario you will have to pass a function as a prop to popupview which will set the popUpIsVisible to false. Something like below

 <PopUpView isVisible={this.state.popUpIsVisible} 
            onDismiss={()=>{this.setState({popUpIsVisible:false})}}/>

You can call the onDismiss inside the child as

<Modal visible={this.props.isVisible}>
<TouchableHighlight
    onPress={() => {
      this.props.onDismiss();
    }}>
 <Text>Hide Modal</Text>
</TouchableHighlight>
</Modal>

The second approach is better as the visibility of the child is controlled by the parent.

Upvotes: 1

Brijesh Shiroya
Brijesh Shiroya

Reputation: 3383

Add below line into constructor after state defining

this.setPopUpIsVisible = this.setPopUpIsVisible.bind(this);

Upvotes: 0

Related Questions