Create a login form with TextInput in child component - react-native

I am new to React Native and I am trying to implement a simple Login form.
I tried the following first, which works:
import React, { Component } from 'react';
import {
View,
Text,
TextInput,
StyleSheet,
} from 'react-native';
import TitledInput from './login-form';
export default class LoginForm extends Component {
constructor(props) {
super(props);
this.state = { email: '', password: ''};
}
render() {
return (
<View style={styles.container}>
<TextInput
label='Email Adress'
placeholder='you#domain.com'
value={this.state.email}
onChangeText={(email) => this.setState({email})}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
Then I wanted to split the input in another independent component called TitledInput and tried this (which is not working):
LoginForm
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
} from 'react-native';
import TitledInput from './login-form';
export default class LoginForm extends Component {
constructor(props) {
super(props);
this.state = { email: '', password: ''};
}
render() {
return (
<View style={styles.container}>
<TitledInput
label='Email Adress'
placeholder='you#domain.com'
value={this.state.email}
onChangeText={(email) => this.setState({email})}
/>
</View>
);
}
}
TitledInput
import React, { Component } from 'react';
import { View, Text, TextInput, StyleSheet } from 'react-native';
export default class TitledInput extends Component {
const { inputStyle, labelStyle, containerStyle } = styles;
render() {
return (
<View style={container}>
<Text style={label}>{props.label.toUpperCase()}</Text>
<TextInput
autoCorrect={false}
placeholder={props.placeholder}
secureTextEntry={props.secureTextEntry}
value={props.value}
onChangeText={props.onChangeText}
style={input}
/>
</View>
);
}
}
I get a 'maximum call stack exceeded' error.
I could read on the internet that this error can occur when calling setState in a re-render function... but I don't know how to handle this case where I want my login form to know the value a its input child component.
I want to know it because I will use it when clicking the submit button or is it the whole purpose of the state?

while you are changing email input value in TiledInput email value will propagate to LoginForm. In LoginForm because of state "email" change the login form will rerender. In this case you have to use ComponentWillReceiveProps inorder to get the new email value.
Instead of this approach you can have another state value inside TitledInput to keep email value.
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
} from 'react-native';
import TitledInput from './login-form';
export default class LoginForm extends Component {
constructor(props) {
super(props);
this.state = { email: '', password: ''};
}
render() {
return (
<View style={styles.container}>
<TitledInput
label='Email Adress'
placeholder='you#domain.com'
onChangeText={(email) => this.setState({email})}
/>
</View>
);
}
}
// TitledInput
import React, { Component } from 'react';
import { View, Text, TextInput, StyleSheet } from 'react-native';
export default class TitledInput extends Component {
constructor(props) {
super(props);
this.state = { text: ''};
}
const { inputStyle, labelStyle, containerStyle } = styles;
handleTextChange(value){
this.setState({text:value});
this.props.onChangeText(value);
}
render() {
return (
<View style={container}>
<Text style={label}>{props.label.toUpperCase()}</Text>
<TextInput
autoCorrect={false}
placeholder={props.placeholder}
secureTextEntry={props.secureTextEntry}
value={this.state.text}
onChangeText={this.handleTextChange()}
style={input}
/>
</View>
);
}
}
[update] By using onBlur instead of onChangeText you can reduce the number of function call

Related

React-Native TextInput one step behind in Child Component

TextInput in Child Component on logging is one step behind in my Input
Parent Component:
import React from 'react';
import {
View,
Text,
} from 'react-native';
import NumberInput from '../../Components/NumberInput'; //child component
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
number: '',
};
}
onChange = value => {
this.setState({number: value});
console.log(this.state.number);
};
render() {
return (
<View>
<Text>Login</Text>
<NumberInput onChange={this.onChange} />
</View>
);
}
}
Child Component
import React from 'react';
import {View, Text, TextInput} from 'react-native';
import {withNavigation} from 'react-navigation';
const NumberInput = ({onChange}) => {
return (
<View>
<Text>Enter mobile number to continue</Text>
<TextInput
style={styles.input}
keyboardType={'phone-pad'}
placeholder="999-999-9999"
underlineColorAndroid="transparent"
autoCompleteType="tel"
maxLength={10}
onChangeText={value => onChange(value)}
/>
</View>
);
};
export default withNavigation(NumberInput);
Pass your console.warn() as a callback to this.setState()
this.setState({
number: value
},() => {
console.warn(this.state.number);
});

what's the problem with the "super" method?

This is my code:
import React, { component } from 'react';
import { View, Text, StyleSheet} from 'react-native';
class GuideLine extends component {
constructor(props){
super(props);
this.state = {};
};
render() {
return(
<View>
<Text>Hello</Text>
</View>
);
};
};
const styles = StyleSheet.create({});
export default GuideLine;
and I'm getting this error : Super expression must either be null or function. What should I do to fix it ?

React Native: Updating the state from child to parent and then pass updated props at second component

I am trying to change the value of props at second Recipe-Component updating the state.
I tried to use timer, alerts. but I had also problem.
App js:
import React, { Component } from "react";
import Recipe from "./Recipe";
import {Text, View } from "react-native";
export default class App extends Component {
constructor() {
super();
this.state = {
name: "Pancaces",
isYummy: true
};
}
update = (s) => {
this.setState({name : s.name, isYummy: s.isYummy });
}
render() {
return (
<View style={{alignItems: 'center'}}>
<Recipe update={this.update} name={this.state.name} isYummy={this.state.isYummy} delay={false}/>
<Text>{this.state.name}</Text>
<Recipe name={this.state.name} isYummy={this.state.isYummy} delay={true}/>
</View>
);
}
}
Recipe.js
import React, * as react from "react";
import PropTypes from "prop-types";
import { Text, View } from "react-native";
import styles from "./styles.js";
export default class Recipe extends react.Component{
static propTypes = {
name: PropTypes.string.isRequired,
isYummy: PropTypes.bool.isRequired
};
constructor(props){
super(props)
this.state = {
update : this.update,
name : this.props.name,
isYummy: this.props.isYummy
}
if(this.props.update) this.props.update(({name:'Lentils', isYummy:false}))
};
render() {
return (
<View style={styles.container}>
<Text style={styles.text_container} >{this.state.name}</Text>
{this.state.isYummy ? <Text>THIS RECIPE IS YUMMY</Text> : null}
</View>
);
}
}
Current Output is:
Pancakes
THIS RECIPE IS YUMMY
Lentils
Pancakes
THIS RECIPE IS YUMMY
I expect output
Pancakes
THIS RECIPE IS YUMMY
Lentils
Lentils
SOLVED
I believe this is happening because you are trying to display the values from your state instead of your props and since the constructor isn't called a second time after updating, your state doesn't get reassigned. Here's what I would do:
//Recipe.js
constructor (props)
{
super(props);
if (this.props.update)
this.props.update(({name:'Lentils', isYummy:false}));
}
render()
{
return (
<View style={styles.container}>
<Text style={styles.text_container}>
{this.props.name}
</Text>
{this.props.isYummy &&
<Text>
THIS RECIPE IS YUMMY
</Text>
}
</View>
);
}

undefined is not an object (evaluating 'this.props.dispatch')

i have this React native component that contains two classes the first has a button that i want it to redirect me to another page it doesn't redirect anything it's just dispatching error . When i try the same function of redirection with the second class it works . Where is the problem please ?
import React, { Component } from 'react';
import {
TouchableOpacity ,
View,
BackHandler,
Image,
Text,
Button,
Platform,
ActivityIndicator
} from 'react-native';
import {connect } from 'react-redux'
class HeaderBarWebView extends Component{
constructor(props){
super(props);
}
goCamera(){
this.props.dispatch(changeCurrentPage("camera"))
}
render(){
return(
<View>
<TouchableOpacity style={{justifyContent:'center',display: 'flex'}}
onPress={this.goCamera}>
<Image
style={{tintColor:"white" , height:30,
width:30,marginRight:20}}
source={require('../resource/img/camera.png')}
/>
</TouchableOpacity>
</View>
)}}
class WebviewApp extends Component{
constructor(props)
{
this.state={}
this.goCamera= this.goCamera.bind(this)
goCamera(){
this.props.dispatch(changeCurrentPage("camera"))
}
render()
{
return(
<View style={{flex:1,width:"100%"}}>
<Button title="Press me" onPress={this.goCamera}></Button>
<CustomWebView
...........................
/>
</View>
);
}
}
const mapStateToProps = state => ({
page:state.router.page,
camera : state.router.camera
})
const WebviewAp = connect(mapStateToProps)(WebviewApp,HeaderBarWebView)
export default WebviewAp
Here is the error shown
You need to bind goCamera function with "this", in constructor;
constructor(props){
super(props);
this.goCamera = this.goCamera.bind(this);
}

how to connect react with redux?

so I am trying to learn about react-redux using react-native and I want to make a page where I can input a number and press login. when I press login, the page will alert me the number I input and saved into the store I created with redux.
can anyone please tell me what i'm doing wrong and what should I add or do to make it work?
below is my testing page
import React, {Component} from 'react';
import {View, TextInput, TouchableOpacity, Text} from 'react-native';
import {connect} from 'react-redux';
import actions from '../Redux/Action';
class tes extends Component{
constructor(props){
super(props)
}
render(){
return(
<View>
<TextInput placeholder="phone number"
keyboardType="number-pad"/>
<TouchableOpacity onPress={this.props.onLogin}>
<Text>login</Text>
</TouchableOpacity>
</View>
)
}
}
mapStateToProps = state => {
return {
number: state.phoneNumber
}
}
mapDispatchToProps = dispatch => {
return {
onLogin: (number) => {
dispatch(actions.setLoginNumber(number))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(tes);
this is my store class
import {createStore} from 'redux';
import reducer from './Reducer';
export default createStore(reducer)
here is my reducer class
const reducer = (state = {
phoneNumber: '',
},action) => {
switch(action.type) {
case "LOGIN":
state = {
phoneNumber: action.payload
}
break;
}
return state;
}
export default reducer;
{/* and this one my action class */}
export default function setLoginNumber(number) {
return{
type: "LOGIN",
payload: number
};
}
thanks in advance..
I think your not passing parameter number to onLogin function and you will need local state variable to hold the value. The code should be like this
import React, {Component} from 'react';
import {View, TextInput, TouchableOpacity, Text} from 'react-native';
import {connect} from 'react-redux';
import actions from '../Redux/Action';
class tes extends Component{
constructor(props){
super(props)
this.state = {
number: 0,
};
}
render(){
return(
<View>
<TextInput placeholder="phone number"
onChangeText={inputNumber => {
this.setState({ number: inputNumber })
}}
keyboardType="number-pad"/>
<TouchableOpacity onPress={() => {this.props.onLogin(this.state.number) }}>
<Text>login</Text>
</TouchableOpacity>
</View>
)
}
mapStateToProps = state => {
return {
number: state.phoneNumber
}
}
mapDispatchToProps = dispatch => {
return {
onLogin: (number) => {
dispatch(actions.setLoginNumber(number))
}
}
}
Answer for your second question -
You haven't passed created store to provider component of react-redux like below example
import { Provider } from 'react-redux';
import App from './App';
import store from './store';
export default class Root extends Component {
constructor() {
super();
}
render() {
return (
<Provider store={store}>
<App />
</Provider>
);
}
}
Hope it helps.