React Native how to pass this.setState change to parent

I am new to React Native I am making a sample app where the user can login and register for a new account.

I have two React classes,

One is the main class index.ios.js and another class called register.js. In the index class I am saying if the variable register is true render the register screen.

In the class register.js I am trying to set the variable register to false using this.setState({register:false}) but it is not causing the re render of the parent (index.ios.js). Is the a super(state) method or something similar that I am missing ? I believe the parent state is not getting the values of the updated register variable.

Here are my classes:

Render inside index.ios.js:

render: function() {
    if(this.state.register) {
      return this.renderRegisterScreen();
    }
    else if (this.state.loggedIn) {
      return this.userLoggedIn();
    }
    else {
      return this.renderLoginScreen();
    }
  }

Register.js:

var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Image,
  TouchableHighlight,
  TextInput,
} = React;

var Register = React.createClass({
        render: function() {
        return (
          <View style={styles.container}>
            <View style={styles.rafitoImage}>
              <Image source={require('./logo.png')}></Image>
              <Text style={styles.slogan}>Eliminate the need to wait!</Text>
            </View>
            <View style={styles.bottomSection}>
              <View style={styles.username}>
                <View style={styles.inputBorder}>
                  <TextInput placeholder="Username..." style={styles.usernameInput} onChangeText={(text) => this.setState({username: text})}/>
                </View>
                <View style={styles.inputBorder}>
                  <TextInput password={true} placeholder="Password..." style={styles.usernameInput} onChangeText={(text) => this.setState({password: text})}/>
                </View>
                <View style={styles.inputBorder}>
                  <TextInput password={true} placeholder="Verify Password..." style={styles.usernameInput} onChangeText={(text) => this.setState({verifyPassword: text})}/>
                </View>
                <View style={styles.inputBorder}>
                  <TextInput placeholder="Phone.." style={styles.usernameInput} onChangeText={(text) => this.setState({phone: text})}/>
                </View>
                <View style={styles.inputBorder}>
                  <TextInput placeholder="Email.." style={styles.usernameInput} onChangeText={(text) => this.setState({email: text})}/>
                </View>
                <TouchableHighlight style={styles.button}
                  underlayColor='#f1c40f' onPress={this.register}>
                        <Text style={styles.buttonText}>Register</Text>
                </TouchableHighlight>
                <TouchableHighlight style={styles.signUp} onPress={this.resetToLogin}
                underlayColor='#ffffff'>
                <Text style={styles.signUpText}>Already A Member </Text>
                </TouchableHighlight>
              </View>
            </View>
            <View style={styles.copyright}>
            </View>
          </View>
        );
    },

    resetToLogin: function() {
        this.setState({
            register: false //I want this to re render the home screen with the variable register as false
        });
    }
});

    var styles = StyleSheet.create({
      container: {
        flex : 1
      },
      bottomSection: {
        flex: 5,
        flexDirection: 'row' 
      },
      button: {
            height: 36,
            backgroundColor: '#32c5d2',
            justifyContent: 'center',
            marginTop: 20
        },
      buttonText: {
            fontSize: 18,
            color: 'white',
            alignSelf: 'center'
      },
      signUpText: {
        color: '#3598dc'
      },
      signUp: {
        alignItems: 'flex-end',
        marginTop: 10,
      },
      username: {
        flex: 1,
        padding: 5
      },
      rafitoImage: {
        flex: 3,
        justifyContent: 'center',
        alignItems: 'center',
      },
      copyright: {
        alignItems: 'center'
      },
      usernameInput: {
            height: 36,
            marginTop: 10,
            marginBottom: 10,
            fontSize: 18,
            padding: 5
      },
      copyrightText: {
        color: '#cccccc',
        fontSize: 12
      },
      inputBorder: {
        borderBottomWidth: 1,
        borderBottomColor: '#ececec'
      },
      slogan: {
        color: '#3598dc'
      }
    });

module.exports = Register;

Attempt 1

As per the answer I added this to my index.ios.js

renderRegisterScreen: function() {
    return (
      <Register login={this.login}/>
    )
  }

And I added this to my register.js

<TouchableHighlight style={styles.signUp} onPress={this.props.login}
                underlayColor='#ffffff'>
                <Text style={styles.signUpText}>Already A Member </Text>
                </TouchableHighlight>

But for some reason it does not even go to the register screen anymore, it executes the login function as soon as the register screen renders. What am I missing now ? Please advise.

Thanks

Update

It works when I pass down registered as a property but not when I do not. I would like to understand why if someone could post that.

Thanks

Answers:

Answer

You can pass the function down to the child as props, then set the state of the parent from within the child that way.

Parent Component:

   var Parent = React.createClass({

    getInitialState() {
        return {
            registered: false
        }
    },

  register(){
    console.log("logging in... ");
    this.setState({
        registered: true
    });

  },

  render: function() {
    return (
      <View style={styles.container}>
        <Child register={this.register.bind(this)} registered={this.state.registered} />
      {this.state.registered && <View style={{padding:10, backgroundColor:'white', marginTop:10}}>
                                    <Text style={{fontSize:20}}>Congratulations, you are now registered!</Text>
                              </View>}
       </View>
    );
  }
});

Child Component:

var Child = React.createClass({

render: function() {
return(
    <View style={{backgroundColor: 'red', paddingBottom:20, paddingTop:20 }}>
        <TouchableHighlight style={{padding:20, color: 'white', backgroundColor: 'black'}} onPress={() => this.props.register() }>
 {this.props.registered ? <Text style={{color: 'white'}}>registered</Text> : <Text style={{color: 'white'}}>register</Text>}
        </TouchableHighlight>                     
    </View>
    ) 
  }
})
Answer

Here is a more powerful solution. This will let the child component change any state variable in the parent.

Parent component:

render: function() {
    return (
       ...
        <Child setParentState={newState=>this.setState(newState)} />
       ...
    );
}

// Take note of the setState()

Child component:

this.props.setParentState({registered: true})
Answer

Why my attempt was failing was because I was using

onPress={this.props.login}

It should be

onPress={()=>this.props.login}

because of that mistake my onPress function would execute as soon as the button would render. I am not sure why that happens but I know what my mistake was.

Answer

Using StackNavigator I found a soultion leveraging screenProps. Here you can pass down functions and values to your routes. App global state is managed in App. App then passes in functions and/or state to NavComponent screenProps. Each child route in StackNavigator will then have access via this.props.screenProps

This solution is working well for now. Would love some feedback, or suggestions for improving this method

class HomeScreen extends React.Component {

  render() {
    return (
      <View>
        <Text>{JSON.stringify(this.props.screenProps.awesome)}</Text>
        <Button
          onPress={() => this.props.screenProps.updateGlobalState("data")}
          title="Update parent State"
        />
      </View>
    );
  }
}

const NavComponent = StackNavigator({
  Home: { screen: HomeScreen },
  // AllOthers: { screen: AllComponentsHereMayAccessScreenProps },
});

export default class App extends React.Component {
  constructor() {
    super();
    this.state = {
      everythingIsAwesome: false,
    }
  }

  _updateGlobalState(payload) {
    console.log('updating global state: ', payload);
    this.setState({everythingIsAwesome: payload});
  }

  render() {
    return <NavComponent screenProps={{
      updateGlobalState: this._updateGlobalState.bind(this),
      awesome: this.state.everythingIsAwesome
    }} />;
  }
}

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.