React-Native TextInput one step behind in Child Component - react-native

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);
});

Related

How to Use method of functional component into class component in react native?

I am working in one react native project in which, I want to make common component for show loading indicator (for inform user to wait until process done.)
For that , I have make one js file that is common for my project
Look like below :
Loader.JS : Common functional component in react native
import React, {useState} from 'react';
import {View, StyleSheet, ActivityIndicator} from 'react-native';
import {loaderColor} from './app.constants';
const Loader = () => {
return (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={loaderColor} />
</View>
);
};
const UseLoader = () => {
const [visible, setVisible] = useState(true);
const showLoader = () => setVisible(true);
const hideLoader = () => setVisible(false);
const loader = visible ? <Loader /> : null;
return [loader, showLoader, hideLoader];
};
const styles = StyleSheet.create({
loadingContainer: {
backgroundColor: 'red',
flex: 1,
position: 'absolute',
...StyleSheet.absoluteFillObject,
alignItems: 'center',
justifyContent: 'center',
zIndex: 100,
padding: 10,
},
});
export default UseLoader;
And my class component is look like this :
import React, {Component} from 'react';
import {View} from 'react-native';
// import {UseLoader} from '../UseLoader';
import '../UseLoader';
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
};
}
componentDidMount() {
[loader, showLoader , hideLoader] = UseLoader;
this.callApi()
}
callApi() {
...
}
render() {
return (
<View style={styles.body}>
{loader}
</View>
);
}
}
I have tried to import functional component in both way But failed to use it.
Is any solution that can Import functional component in class component in react native ?
you can use this.
You can add a ref to the child component:
<loader ref='loader' {...this.props} />
Then call the method on the child like this:
<Button onPress={this.refs.loader.myfunc} />
Same functionality, but instead of using a String to reference the component, we store it in a global variable instead.
<loader ref={loader => {this.loader = loader}} {...this.props} />
<Button onPress={this.loader.myfunc} />
If you want to do it common, change the state on the class component, where you send if it is visible or not, like this:
const Loader = (props) => {
if(props.show){
return (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={loaderColor} />
</View>
);
}else{
return null;
}
};
and in your class component
import React, {Component} from 'react';
import {View} from 'react-native';
// import {UseLoader} from '../UseLoader';
import '../UseLoader';
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
};
}
componentDidMount() {
this.setState({showLoading:true});
this.callApi()
}
callApi() {
...
}
render() {
return (
<View style={styles.body}>
<loader show={this.state.showLoading} />
</View>
);
}
}

Undefined is not an object for react navigation

I have two components for my project, and I have gone through all the steps for react navigation as follow:
// App.js
import React from 'react';
import Main from './app/componenets/Main'
import details from './app/componenets/details'
import { createStackNavigator, createAppContainer } from 'react-navigation'
const mainNavigator = createStackNavigator(
{
MainScreen: Main,
detailsScreen: details,
},
{
initialRouteName :'MainScreen'
}
);
const AppContainer = createAppContainer(mainNavigator);
export default class App extends React.Component{
render() {
return(
<AppContainer />
);
}
}
Then I have my Main.js which I have a method as follow:
import React from 'react';
import {
StyleSheet ,
Text,
View,
} from 'react-native'
import Note from './Note'
import detail from './details'
import { createStackNavigator, createAppContainer } from "react-navigation";
export default class Main extends React.Component {
static navigationOptions = {
title: 'To do list',
headerStyle: {
backgroundColor: '#f4511e',
},
};
constructor(props){
super(props);
this.state = {
noteArray: [],
noteText: ''
}
}
render() {
let notes = this.state.noteArray.map((val,key) => {
return <Note key={key} keyval={key} val={val}
goToDetailPage= {() => this.goToNoteDetail(key)} />
});
const { navigation } = this.props;
return(
<View style={styles.container}>
<View style={styles.footer}>
<TextInput
onChangeText={(noteText) => this.setState({noteText})}
style={styles.textInput}
placeholder='What is your next Task?'
placeholderTextColor='white'
underlineColorAndroid = 'transparent'
>
</TextInput>
</View>
<TouchableOpacity onPress={this.addNote.bind(this)} style={styles.addButton}>
<Text style={styles.addButtonText}> + </Text>
</TouchableOpacity>
</View>
);
}
//This method is declared in my Note.js
goToNoteDetail=(key)=>{
this.props.navigation.navigate('detailsScreen')
}
}
//Styles which I didn't post to be short in code here
But when I try to do the navigation I get this error:
'undefined is not and object(evaluating 'this.props.val.date')
Do I need to pass the props in a way? or should I do anything else? I am new to React native and confused!
in order to access this.props make arrow function or bind the function in constructor.
goToDetail=()=>{
this.props.navigation.navigate('detailsScreen')
}
Just remove createAppContainer
const AppContainer = mainNavigator;
and tell me what react-navigation that you work with

Undefined is not an object (evaluating 'this.props.navigation.navigate') - Drawer Navigator

I'm receiving - undefined is not an object (evaluating 'this.props.navigation.navigate'). I know there are already some answers about this issue but I didn't find any for Drawer Navigator, all I found were for Stack Navigator. (I'm using react-navigation V3)
So here is my DrawerNavigator.js :
import React from 'react';
import { Platform, Dimensions, Button, View, Text } from 'react-native';
import { createDrawerNavigator, createAppContainer, StackNavigator, withNavigation } from 'react-navigation';
import {Header} from 'react-native-elements';
import Hamburger from 'react-native-animated-hamburger';
class MenuButton1 extends React.Component {
constructor(props)
{
super(props);
this.state = {
active: false,
}
}
render () {
const { navigate } = this.props.navigation;
return (
<React.Fragment>
<Hamburger active={this.state.active}
type="cross"
onPress={() => this.setState({active: !this.state.active}) || navigate('DrawerOpen')}
/>
</React.Fragment>
)
}
}
class HomeScreen extends React.Component {
render() {
return (
<React.Fragment>
<Header
leftComponent={<MenuButton1 />}
/>
<View style={{top: 30 }}>
<Text> Hello </Text>
</View>
</React.Fragment>
);
}
}
const WIDTF = Dimensions.get('window').width;
const DrawerConfig = {
drawerWidth: WIDTF*0.80,
draertType: 'slide'
}
const DrawerNavigator = createDrawerNavigator ({
HomeScreen: {
screen: HomeScreen,
}
},
DrawerConfig
);
export default createAppContainer (DrawerNavigator);
And my App.js :
import React, {Component} from 'react';
import DrawerNavigator from './components/DrawerNavigator';
export default class App extends React.Component {
render() {
return (
<React.Fragment>
<DrawerNavigator />
</React.Fragment>
);
}
}
And the issue :
enter image description here
navigation object is only available on screens that are direct children of your drawer navigator
You could wrap your MenuButton1 with withNavigation to get access to the navigation object ... or simply wrap your navigation logic in a method and pass it as a prop to your MenuButton like:
class MenuButton1 extends React.Component {
constructor(props) {
// ...
}
render() {
const { onDrawerOpen } = this.props;
return (
<React.Fragment>
<Hamburger
active={this.state.active}
type="cross"
onPress={() => this.setState({ active: !this.state.active }) || onDrawerOpen()
}
/>
</React.Fragment>
);
}
}
-
class HomeScreen extends React.Component {
render() {
return (
<React.Fragment>
<Header
leftComponent={(
<MenuButton1
onDrawerOpen={() => this.props.navigation.openDrawer()}
/>
)}
/>
<View style={{ top: 30 }}>
<Text> Hello </Text>
</View>
</React.Fragment>
);
}
}

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.

Create a login form with TextInput in child component

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