title: React Native display validation errors inline
body: <p>I am a complete noob to React Native and I am just trying to get hang of it. </p>
  
  <p>I have a simple Login screen which I have developed using Container/Presentation component concept. My presentation component only has render function that renders three two TextInput and a Button. </p>
  
  <p>Rather than displaying errors using Toasts I want to display errors below the TextInput itself. So, what I have done is added Text element below the TextInput. Something like below.</p>
  
  <pre><code>&lt;TextInput placeholder="Email"&gt;&lt;/TextInput&gt;
  &lt;Text ref="emailErrors"&gt;&lt;/Text&gt;
  </code></pre>
  
  <p>By default, Text with ref emailErrors is hidden. But when the focus shifts from Email TextInput and if email is invalid for some reason I want to generate a simple error and set it as text for Text element with ref emailErrors. </p>
  
  <p>Now, I understand that I will have to write my logic in container components and pass it as prop to presentation component. But what I am unable to understand is how to trigger the setting of error text and displaying the Text element.</p>
  
  <p><strong>UPDATE:</strong></p>
  
  <p>My Presentation Component:</p>
  
  <pre><code>class LoginForm extends Component {
  
    constructor(props) {
      super(props);
    }
  
    render() {
      return (
  
        &lt;KeyboardAvoidingView
          style={styles.loginFormContainer}&gt;
  
          &lt;ScrollView
            scrollEnabled={true}
            enableOnAndroid={true}
            enableAutomaticScroll={true}
            keyboardDismissMode="on-drag"
            keyboardShouldPersistTaps="always"
            resetScrollToCoords={{ x: 0, y: 0 }}
            showsVerticalScrollIndicator={false}
            showsHorizontalScrollIndicator={false}&gt;
  
            &lt;View style={styles.loginForm}&gt;
              &lt;Image
                source={require("../../../assets/images/logos/logo.png")}
                style={styles.logoImage}&gt;&lt;/Image&gt;
  
              &lt;View style={styles.textInputWithIcon}&gt;
                &lt;Image
                  style={styles.textInputIcon}
                  source={require("../../../assets/images/icons/email.png")}
                &gt;&lt;/Image&gt;
                &lt;View style={styles.textField}&gt;
                  &lt;TextInput name="email"
                    ref="email"
                    placeholder="Email"
                    blurOnSubmit={false}
                    returnKeyType={"next"}
                    underlineColorAndroid={COLORS.red}
                    onSubmitEditing={() =&gt; this.refs.password.focus()}
                    style={[GLOBALSTYLES.textInput, styles.textInput]}
                    onChangeText={(text) =&gt; this.props.onEmailTextChanged(text)}
                  &gt;
                  &lt;/TextInput&gt;
                  &lt;Text
                    ref="email"&gt;
                  &lt;/Text&gt;
                &lt;/View&gt;
              &lt;/View&gt;
  
              &lt;View style={styles.textInputWithIcon}&gt;
                &lt;Image
                  style={styles.textInputIcon}
                  source={require("../../../assets/images/icons/locked.png")}
                &gt;&lt;/Image&gt;
                &lt;View style={styles.textField}&gt;
                  &lt;TextInput name="password"
                    ref="password"
                    blurOnSubmit={false}
                    placeholder="Password"
                    secureTextEntry={true}
                    returnKeyType={"next"}
                    style={styles.textInput}
                    underlineColorAndroid={COLORS.red}
                    onSubmitEditing={() =&gt; Keyboard.dismiss()}
                    style={[GLOBALSTYLES.textInput, styles.textInput]}
                    onChangeText={(text) =&gt; this.props.onPasswordTextChanged(text)}
                    onBlur={() =&gt; this.props.onPasswordTextBlurred()}
                  &gt;&lt;/TextInput&gt;
                  &lt;Text
                    ref="password"&gt;
                  &lt;/Text&gt;
                &lt;/View&gt;
              &lt;/View&gt;
  
              &lt;TouchableOpacity
                activeOpacity={0.5}
                onPress={() =&gt; { this.props.onLoginPressed() }}
                style={styles.loginButton}
                underlayColor={COLORS.white}&gt;
                &lt;Text style={styles.loginButtonText}&gt;Login&lt;/Text&gt;
              &lt;/TouchableOpacity&gt;
            &lt;/View&gt;
  
          &lt;/ScrollView&gt;
  
        &lt;/KeyboardAvoidingView&gt;
  
      )
  
    };
  
  };
  
  
  export default LoginForm;
  </code></pre>
  
  <p>My Container Component:</p>
  
  <pre><code>class Login extends Component {
  
    static navigationOptions = ({ navigation }) =&gt; ({
      "title": "Login"
    });
  
    constructor(props) {
  
      super(props);
      this.state = {}
  
    }
  
  
    // onLoginPressed will trigger the authentication workflow with the remote server.
  
    onLoginPressed() {
  
      const { isUserLoggedIn, email, password } = this.state;
  
      if (this.state.isEmailValid &amp;&amp; this.state.isPasswordValid) {
  
        axios.post(CONFIGURATION.LOGIN_URL, {
          username: email,
          password: password
        }).then(response =&gt; {
  
          const navigationParams = {
            baseUrl: response.data.url,
            token: response.data.token,
            username: email
          }
  
          this.props.dispatch(loginSuccess(navigationParams));
  
          // Adding retrieved values to AsyncStorage
          AsyncStorage.multiSet(
            [
              [IS_USER_LOGGED_IN, "YES"],
              [USER, email],
              [TOKEN, response.data.token],
              [BASE_URL, response.data.url]
            ],
            () =&gt; {
              this.props.navigation.navigate("WebApp", navigationParams);
            });
  
        }).catch(error =&gt; {
  
          console.error(error);
          ToastAndroid.show("Authentication Failed", ToastAndroid.SHORT);
  
        });
  
      }
  
    }
  
    // Updating the state key email
    onEmailTextChanged(text) {
      this.setState({ "email": text });
    }
  
    // Updating the state key password
    onPasswordTextChanged(text) {
      this.setState({ "password": text });
    }
  
    onEmailTextBlurred() {
  
      var text = this.state.email;
      console.warn(text);
  
      if (text == undefined || text.trim().length == 0) {
        this.setState({ "isEmailValid": false });
        this.setState({ "emailErrorMessage": "Email cannot be empty" });
      }
      else {
        var regex = /^(([^&lt;&gt;()\[\]\\.,;:\s@"]+(\.[^&lt;&gt;()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  
        var isEmailValid = regex.test(text);
        if (!isEmailValid) {
          this.setState({ "isEmailValid": false });
          this.setState({ "emailErrorMessage": "Email is incorrect." });
        }
        else {
          this.setState({ "isEmailValid": true });
        }
      }
  
    }
  
    onPasswordTextBlurred() {
  
      var text = this.state.password;
  
      if (text == undefined || text.trim().length == 0) {
        this.setState({ "isPasswordValid": false });
        this.setState({ "passwordErrorMessage": "Password cannot be empty" });
      }
      else {
        this.setState({ "isPasswordValid": true });
      }
  
    }
  
    // rendering the LoginForm (presentational component) corresponding to this container component
    render() {
      return (
        &lt;LoginForm
          onLoginPressed={() =&gt; this.onLoginPressed()}
          onEmailTextChanged={(text) =&gt; this.onEmailTextChanged(text)}
          onPasswordTextChanged={(text) =&gt; this.onPasswordTextChanged(text)}
          onEmailTextBlurred={() =&gt; this.onEmailTextBlurred()}
          onPasswordTextBlurred={() =&gt; this.onPasswordTextBlurred()}
        /&gt;
      )
    }
  
  }
  
  const mapStateToProps = (state) =&gt; {
  
    return state;
  
  }
  
  
  const mapDispatchToProps = (dispatch) =&gt; {
  
    const boundActionCreators = bindActionCreators(loginSuccess, dispatch);
    return { ...boundActionCreators, dispatch };
  
  }
  
  
  export default connect(mapStateToProps, mapDispatchToProps)(Login);
  </code></pre>