I am new to react native and have been doing this for a week or so. I just finished the tutorials for making an interactive buttons and work on it. But i am stuck on this. The app is really simple right now, just trying to make a form and add some trigger event using onPress on it.
Below is the portion of my code. I am simply lost for words why its not calling SubmitThisForm() on the onPress event.
Can you guys help me on this.
Thanks a lot.
import React, { Component } from 'react';
import {Container, Content, InputGroup,Button, View, Icon, Card,
CardItem, Text, Body} from 'native-base';
import {Input} from './common';
class LoginForm extends Component {
state = {email: '', password: ''};
SubmitThisForm() {
console.log("Can you see this");
}
render () {
return (
<Container>
<Content>
<Card style={styles.FormContainer}>
<CardItem>
<Body>
<InputGroup borderType="regular">
<Icon name="ios-mail-outline" style={{color:'#384850'}}/>
<Input
placeHolder="example#example.com"
value = {this.state.email}
onChangeText={email=>this.setState( { email })}
/>
</InputGroup>
<InputGroup borderType="regular">
<Icon name="lock" style={{color:'#384850'}}/>
<Input
secureTextEntry= {true}
placeHolder="password"
value = {this.state.password}
onChangeText={password=>this.setState( { password })}
/>
</InputGroup>
</Body>
</CardItem>
</Card>
<View style={styles.SignIn}>
<Button block warning onPress={ () => {this.SubmitThisForm()}}><Text>Sign In</Text></Button>
</View>
<View style={styles.SignUp}>
<Button block info><Text>Sign Up</Text></Button>
</View>
<View style={styles.SignIn}>
<Button block primary><Text>Forgot Password</Text></Button>
</View>
</Content>
</Container>
);
};
}
const styles = {
ErrorTextStyle: {
fontSize: 20,
alignSelf: 'center',
color: 'red'
},
FormContainer:{
marginTop:30,
shadowColor:'#000',
shadowOffset:{width:0,height:2},
shadowOpacity:0.1,
shadowRadius:2,
},
SignIn:{
marginTop:10,
flex:1,
alignSelf:'stretch',
},
SignUp:{
marginTop:40,
flex:1,
alignSelf:'stretch',
}
}
export default LoginForm
The include input component is like this:
import React from 'react';
import {Text, View, TextInput} from 'react-native';
const Input = ({ label,value,onChangeText,placeHolder,secureTextEntry }) => {
const {InputStyle,LabelStyle,ContainerStyle } = styles;
return (
<View style = {ContainerStyle}>
<TextInput
secureTextEntry = {secureTextEntry}
placeholder={placeHolder}
autoCorrect={false}
style = {InputStyle}
value={value}
onChangeText={onChangeText}
/>
</View>
);
};
const styles = {
InputStyle:{
color:'#000',
paddingRight:5,
paddingLeft:5,
fontSize:18,
lineHeight:30,
flex:2,
height:40
},
LabelStyle:{
fontSize:18,
paddingLeft:20,
flex:1,
},
ContainerStyle:{
height:40,
flex:1,
flexDirection:'row',
alignItems:'center'
}
}
export { Input };
You need to either use an Arrow function, or bind() SubmitThisForm to your Component.
You can either declare your method like:
SubmitThisForm = () => {
console.log('Can you see this?')
}
Or, you can bind() your function in the constructor by adding:
constructor() {
super()
this.SubmitThisForm = this.SubmitThisForm.bind(this)
}
If you do not bind this in your custom functions, this will equal undefined. When you use Arrow functions however, this is lexically scoped which means the context of this will be the enclosing context (LoginForm)
Related
I stumbled upon this issue that whenever I type anything on the TextInput, there is no text being type, meaning its blank.
I am using typescript btw. Here's my FormInput code:
import React from 'react';
import {View, TextInput, StyleSheet} from 'react-native';
import {COLORS, FONTS} from '../../constants';
interface Props {
placeholderText: string;
labelValue: any;
}
const FormInput: React.FC<Props> = ({
placeholderText,
labelValue,
...restProps
}) => {
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder={placeholderText}
autoCorrect={false}
value={labelValue}
{...restProps}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
marginVertical: 35,
},
input: {
paddingVertical: 25,
paddingHorizontal: 20,
borderRadius: 50,
borderWidth: 2,
borderColor: COLORS.transparent,
backgroundColor: COLORS.lightGray,
...FONTS.body3,
color: COLORS.darkgray,
},
});
export default FormInput;
And I added it on my LoginScreen like this:
interface Props {
placeholder: string;
keyBoardType: string;
secureTextEntry: string;
buttonTitle: string;
}
interface State {
email: string;
password: string;
}
export default class LoginScreen extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
email: '',
password: '',
};
}
render() {
return (
<View style={styles.container}>
<SafeAreaView>
<View style={styles.logoContainer}>
<Image style={styles.logo} source={images.logo} />
</View>
<Text style={styles.loginText}>Login</Text>
<View style={styles.textInputContainer}>
<FormInput
placeholderText="Your email..."
keyBoardType="email-address"
autoCapitalize="none"
autoCorrect="none"
labelValue={this.state.email}
onChangeText={(userEmail: any) =>
this.setState({email: userEmail})
}
/>
<FormInput
placeholderText="Your password..."
secureTextEntry={true}
labelValue={this.state.password}
onChangeText={(userPassword: any) =>
this.setState({password: userPassword})
}
/>
<FormButton
buttonTitle="Login"
onPress={() => Alert.alert('hello')}
/>
</View>
</SafeAreaView>
</View>
);
}
}
When I started typing. It looks like this:
Any idea what's causing this?
From React Native documentation: Note that some props are only available with multiline={true/false}. Additionally, border styles that apply to only one side of the element (e.g., borderBottomColor, borderLeftWidth, etc.) will not be applied if multiline=true. To achieve the same effect, you can wrap your TextInput in a View:
So just add multiline prop to your FormInput
const FormInput = ({ placeholderText, labelValue, ...restProps }) => {
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder={placeholderText}
autoCorrect={false}
multiline // -- this one!
value={labelValue}
{...restProps}
/>
</View>)}
I have the following code (full example):
import React, { useState, useEffect } from 'react';
import { SafeAreaView, View, TextInput, Button, StyleSheet } from 'react-native';
const App = () => {
const [textblocks, setTextblocks] = useState([]);
const CreateTextBlockHandler = () => {
let array = [...textblocks];
array.push({text: ''});
setTextblocks(array);
};
const SaveTextHandler = (index, text) => {
let array = [...textblocks];
array[index].text = text;
setTextblocks(array);
};
const RenderTextInputs = () => {
return textblocks.map((item, index) => {
return (
<View key={index}>
<TextInput style={styles.textinput} placeholder="text" value={textblocks[index].text} onChangeText={value => SaveTextHandler(index, value)} />
</View>
);
});
};
return (
<SafeAreaView style={styles.pancontainer}>
<RenderTextInputs />
<Button title="Create textblock" onPress={CreateTextBlockHandler} />
</SafeAreaView>
);
};
const styles = StyleSheet.create({
pancontainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
textinput: {
width: 200,
margin: 10,
borderWidth: 1,
borderColor: 'black'
}
});
export default App;
How it works: I have a button to dynamically add a new textinput. This works. The problem is, for every character I enter in a textput, the focus is lost after every character.
I created a snack, you can test it here: https://snack.expo.io/BJcHxluyI.
I THINK I need to save the reference to the textinput somewhere, and then give the focus back to the textinput after every keystroke, but I don't know how to do that.
Change
return (
<SafeAreaView style={styles.pancontainer}>
<RenderTextInputs />
<Button title="Create textblock" onPress={CreateTextBlockHandler} />
</SafeAreaView>
);
To
return (
<SafeAreaView style={styles.pancontainer}>
{RenderTextInputs()}
<Button title="Create textblock" onPress={CreateTextBlockHandler} />
</SafeAreaView>
);
Or put RenderTextInputs outside App and pass data via props. With first syntax react treats RenderTextInputs as a component and, as it is nested in App, mounts it again on each App render.
App.js
import React,{ Component } from 'react';
import { StyleSheet,TextInput, View, Button, Text } from 'react-native';
import Toast from 'react-native-simple-toast';
import TextItem from '.src/Components/Textviews/TextViewDisplay';
export default class App extends Component {
state = {
placeName : "",
titleText: "Text view"
}
placeNameChangeHandler = val =>{
this.setState({
placeName : val
})
}
placeSubmitHandler = () =>{
this.setState({
titleText: this.state.placeName.trim()
})
Toast.showWithGravity('This is a long toast at the top.', Toast.LONG, Toast.TOP)
}
render() {
return (
<View style={styles.rootContainer}>
<View style={styles.btnEditContainer}>
<View style ={styles.wrapStyle}>
<TextInput
style = {styles.textInputStyle}
value = {this.state.placeName}
onChangeText = {this.placeNameChangeHandler}
/>
</View>
<View style ={styles.wrapStyle}>
<Button
title="Add"
style ={styles.buttonStyle}
onPress ={this.placeSubmitHandler}/>
</View>
</View>
<View style={styles.textContainer}>
<View style ={styles.wrapStyle}>
<Text
style ={styles.textStyle}>
{this.state.titleText}
</Text>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
rootContainer: {
height:"100%",
width:"100%",
backgroundColor: "#008000",
flexDirection:"column",
alignItems:"center",
justifyContent: "center"
},
btnEditContainer: {
backgroundColor:"#008080",
flexDirection:"row",
alignItems:"center",
justifyContent: "center"
},
textContainer: {
backgroundColor:"#00FFFF",
flexDirection:"column",
alignItems:"center",
justifyContent: "center"
},
textStyle: {
fontSize: 20,
flexDirection:"column",
alignItems:"center",
justifyContent: "center"
},
buttonStyle: {
//flex:1
},
textInputStyle: {
borderColor:"black",
borderWidth:1,
},
wrapStyle: { marginLeft:5,
marginRight:5 },
});
TextViewDisplay.js
import React from 'react';
import {StyleSheet, View, Text} from 'react-native';
const textItem = (props) => (
<View style={styles.rootElement}>
<Text style={styles.textItem}>
{props.textItem}
</Text>
</View>
);
const styles = StyleSheet.create({
rootElement : {
backgroundColor:"red"
},
textItem : {
color: '#f44336'
}
});
export default textItem;
What I am trying to do:
Instead of Text in App.js file I have written a
TextViewDisplay.js file to reuse.
How to properly implement in render function of App.js
If I undrestood your query correctly, then you just want to replace this
<Text style ={styles.textStyle}>
{this.state.titleText}
</Text>
from your App component with your TextItem (re-usable) component.
As you have already imported your TextItem component, you can do this way
<View style={styles.textContainer}>
<View style ={styles.wrapStyle}>
//This is your re-usable component
<TextItem style = {styles.textStyle}>
{this.state.titleText}
</TextItem >
</View>
</View>
And you just need to change {props.textItem} to {props.children} in your TextItem component.
<View style={styles.rootElement}>
<Text style={{...props.style, ...styles.textItem}}> //Here you can get style from your parent component
{props.children} //This is the child element
</Text>
</View>
Note: Always use PascalCase names for your component.
If you don't want to work with {props.children} and only want to work with {props.textItem}, in that case you need to pass props,
<TextItem style = {styles.textStyle} textItem = {this.state.titleText} />
Now you can use {props.textItem},
<View style={styles.rootElement}>
<Text style={{...props.style, ...styles.textItem}}> //Here you can get style from your parent component
{props.textItem}
</Text>
</View>
Just import your component and instantiate it in any render function:
import textItem from './TextViewDisplay';
render() {
return (
<View style={styles.rootContainer}>
<textItem textItem={'label'} />
...
</View>
}
I would suggest defining the component upper camel case though:
const MyTextItem = (props) => (...)
And giving the property a more meaningful name:
<MyTextItem label={'label'} />
Finally, it's also good to define property types, so other users of your component known which properties to pass, and what type they should be. For example as such:
import PropTypes from 'prop-types';
MyTextItem.propTypes = {
label: PropTypes.string.isRequired,
}
It's very simple re using components in react native :
Step - 1 : Import the function to the screen where you want to reuse that.
ex : import {textItem} from '../pathName'
Step -2 :
render() {
return (
//Create the component of the UI and then use it in render and don't forget to import that:
<TextItem
textItem = 'happy Coding'
/>
)}
Also make sure the component name starts with capital letter.
I want to make button enabled when something is typed in text field and it should get disabled when value in the text field is removed.
But currently when i type in the text field the the button gets activated but when i remove the text it remains enabled and doesn't update it's state.
What should i do?
My code:
import React, { Component } from 'react';
import { StyleSheet, View, Text, TouchableHighlight,TextInput,TouchableOpacity, ImageBackground,Button } from 'react-native';
import { createStackNavigator, createAppContainer } from "react-navigation";
class App extends Component {
state={
username:'',
password:'',
validity:true
}
static navigationOptions={
header:null
};
render() {
return (
<View style={styles.container}>
<View style={{marginLeft:50,marginTop:30}}>
<TextInput
style={styles.textInputStyle}
placeholderTextColor="#FFF"
placeholder='Username'
onChangeText={(value)=>this.setState({username:value,validity:false})}
value={this.state.username}
/>
<TextInput
style={styles.textInputStyle}
placeholderTextColor="#FFF"
placeholder='Password'
secureTextEntry={true}
onChangeText={(value)=>this.setState({password:value,validity:false})}
value={this.state.password}
/>
</View>
<View style={{alignSelf:'center'}} >
<TouchableOpacity style={this.state.validity?
styles.inactiveStyle:styles.activeStyle}
disabled={this.state.validity}
onPress={this.handleLogin}>
<Text>Login</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
const AppNavigator = createStackNavigator(
{
Home: App,
},
{
initialRouteName: "Home",
defaultNavigationOptions:{
header:null
}
}
);
const styles = StyleSheet.create({
container:{
flex:1,
},
textInputStyle:{
height:50,
width:300,
borderBottomWidth:1,
borderBottomColor:'#FFF',
marginTop:20,
color:'#FFF'
},
activeStyle:{
marginTop:18,
width:180,
backgroundColor:'#05acfa',
alignItems: 'center',
padding: 10
},
inactiveStyle:{
marginTop:18,
width:180,
backgroundColor:'grey',
alignItems: 'center',
padding: 10
}
});
export default createAppContainer(AppNavigator);
If we talking about disable in case when one of field is empty better change disable prop logic. So you don't need additional variable, and logic will be clear
disabled={!Boolean(this.state.username && this.state.password)}
so if any of field is empty you will get true
in your case
import React, { Component } from 'react';
import { StyleSheet, View, Text, TouchableHighlight,TextInput,TouchableOpacity, ImageBackground,Button } from 'react-native';
import { createStackNavigator, createAppContainer } from "react-navigation";
class App extends Component {
state={
username:'',
password:'',
validity:true
}
static navigationOptions={
header:null
};
render() {
return (
<View style={styles.container}>
<View style={{marginLeft:50,marginTop:30}}>
<TextInput
style={styles.textInputStyle}
placeholderTextColor="#FFF"
placeholder='Username'
onChangeText={(value)=>this.setState({username:value,validity:false})}
value={this.state.username}
/>
<TextInput
style={styles.textInputStyle}
placeholderTextColor="#FFF"
placeholder='Password'
secureTextEntry={true}
onChangeText={(value)=>this.setState({password:value,validity:false})}
value={this.state.password}
/>
</View>
<View style={{alignSelf:'center'}} >
<TouchableOpacity style={this.state.validity?
styles.inactiveStyle:styles.activeStyle}
disabled={!Boolean(this.state.username && this.state.password)}
onPress={this.handleLogin}>
<Text>Login</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
const AppNavigator = createStackNavigator(
{
Home: App,
},
{
initialRouteName: "Home",
defaultNavigationOptions:{
header:null
}
}
);
const styles = StyleSheet.create({
container:{
flex:1,
},
textInputStyle:{
height:50,
width:300,
borderBottomWidth:1,
borderBottomColor:'#FFF',
marginTop:20,
color:'#FFF'
},
activeStyle:{
marginTop:18,
width:180,
backgroundColor:'#05acfa',
alignItems: 'center',
padding: 10
},
inactiveStyle:{
marginTop:18,
width:180,
backgroundColor:'grey',
alignItems: 'center',
padding: 10
}
});
export default createAppContainer(AppNavigator);
I'm assuming you want the button to be disabled when the value is an empty string. In your onChangeText callback check if the value is "" and then update the state to disable the button.
onChangeText={(value) => {
if (value === "")
this.setState({password:value, validity:true})}
else
this.setState({password:value, validity:false})}
}
You should update the state of disabled in your TouchableOpacity component. See here
Native Base docs only shows how to change background color, text color and font size. But it seems not possible to add icons to tabs.
Is it possible or I will need to fork and implement myself?
Thank you.
With NativeBase 2.0 and above you can add icons to Tabs using TabHeading tags inside heading property.
<Content>
<Tabs>
<Tab heading={<TabHeading><Icon name='settings'/></TabHeading>}>
<Tab heading={<TabHeading><Icon name='home'/></TabHeading>}>
</Tabs>
</Content>
You need to implement yourself. I have implemented this functionality. Please have a look if that would be help you.
Create tabs.js
import React from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity,RefreshControl
} from 'react-native';
import IconTabs from 'react-native-vector-icons/Ionicons';
import NavigationBar from 'react-native-navbar';
import { Container, Header, Title, Button,Icon } from 'native-base';
const Tabs = React.createClass({
tabIcons: [],
propTypes: {
goToPage: React.PropTypes.func,
activeTab: React.PropTypes.number,
tabs: React.PropTypes.array,
},
componentDidMount() {
this._listener = this.props.scrollValue.addListener(this.setAnimationValue);
},
setAnimationValue({ value, }) {
this.tabIcons.forEach((icon, i) => {
const progress = (value - i >= 0 && value - i <= 1) ? value - i : 1;
});
},
render() {
return (
<View>
<View style={[styles.tabs, this.props.style, ]}>
{this.props.tabs.map((tab, i,) => {
return <TouchableOpacity key={tab} onPress={() => this.props.goToPage(i)} style={styles.tab}>
<IconTabs
name={tab}
size={20}
color={this.props.activeTab === i ? 'rgb(255,255,255)' : 'rgb(189, 224, 250)'}
ref={(icon) => { this.tabIcons[i] = icon; }}
/>
<Text style={{fontWeight:'bold', fontSize:10, color:this.props.activeTab === i ? 'rgb(255,255,255)' : 'rgb(189, 224, 250)'}}>{`${this.props.name[i]}`}</Text>
</TouchableOpacity>;
})}
</View>
</View>
);
},
});
const styles = StyleSheet.create({
tab: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingBottom: 10,
},
tabs: {
height: 50,
flexDirection: 'row',
paddingTop: 5,
borderWidth: 0,
borderTopWidth: 0,
borderLeftWidth: 0,
borderRightWidth: 0,
backgroundColor: '#2196F3',
},
});
export default Tabs;
And use this component in your view like following.
import React, {Component} from 'react';
import {
StyleSheet,
Text,
View,
ScrollView,Navigator
} from 'react-native';
import ScrollableTabView from 'react-native-scrollable-tab-view';
import Tabs from './tabs';
export default class LeavesTab extends Component{
constructor(props) {
super(props);
}
_navigate(name) {
this.props.navigator.push({
name: name,
passProps: {
name: name
}
})
}
render() {
let Tabname = ["Tab1","Tab2","Tab3","Tab4"];
return (
<ScrollableTabView
initialPage={this.props.index}
renderTabBar={() => <Tabs name={Tabname} navigator={this.props.navigator} showHeader={true} />}
>
<ScrollView tabLabel="md-calendar">
<Requests tabLabel='Requests' navigator={this.props.navigator} />
</ScrollView>
<ScrollView tabLabel="md-checkbox">
<LeaveList tabLabel='Approved' navigator={this.props.navigator} />
</ScrollView>
<ScrollView tabLabel="md-time">
<LeaveList tabLabel='Pending' navigator={this.props.navigator} />
</ScrollView>
<ScrollView tabLabel="md-close-circle">
<LeaveList tabLabel='Rejected' navigator={this.props.navigator} />
</ScrollView>
</ScrollableTabView>
);
}
}
const styles = StyleSheet.create({
});
Santosh's answer is right, but I found a cleaner way to implement this based on Native Base tabs.
A rendering tab component is necessary, like in Santosh's example.
But in the component, instead of using the ScrollableTabView, I can use React Native's Tabs component. An example:
export default class App extends Component {
render() {
return (
<Container>
<Header>
<Title>Header</Title>
</Header>
<Content>
<Tabs renderTabBar={() => <TabBar />}>
<One tabLabel="video-camera" />
<Two tabLabel="users" />
</Tabs>
</Content>
<Footer>
<FooterTab>
<Button transparent>
<Icon name="ios-call" />
</Button>
</FooterTab>
</Footer>
</Container>
);
}
}
EDIT
#KumarSanketSahu said that version 2.0 is comming with the ability of changing icons in the tabs. My answer above is for version 0.5.x.