Change state in other component in React Native - javascript

I'm quite new to React-Native.
I have a screen which will render depends on its current state like below. At default (initial state) it will render the login screen.
App.js
import React, { Component } from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
import { Header } from './components/Export';
import LoginBox from './components/LoginBox';
import AdditionalActivity from './components/AdditionalActivity';
import SignUp from './components/SignUp';
export default class App extends Component {
state = {
status: 'initial'
}
renderBasedOnLoggedInState() {
if(this.state.status == 'initial') {
return (
<View>
<Header headerText="APP NAME"/>
<LoginBox/>
<AdditionalActivity />
</View>
);
} else if (this.state.status == 'logged') {
return (
<View>
<Text>Welcome to my application</Text>
</View>
)
} else {
return (
<View>
<Header headerText="APP NAME"/>
<SignUp/>
<AdditionalActivity />
</View>
)
}
}
render() {
return (
<View>
{this.renderBasedOnLoggedInState()}
</View>
);
}
}
When the login below is successed, I need to change the state of component App from "initial" to "logged". How could I do it? The login function here is only for test purpose so don't care much about it XD.
LoginBox.js
import React, { Component } from 'react'
import { Alert, Text, View, TouchableOpacity, Button } from 'react-native'
import GreyTextbox from './GreyTextbox'
export default class LoginBox extends Component {
state = {
username: '',
password: '',
}
Login()
{
var username = this.state.username;
var password = this.state.password;
var userdata = require('./a.json');
var count = 0;
for (let j = 0; j < 2; j++) {
if ((userdata[j].username == username) && ( userdata[j].password == password))
{
Alert.alert("true");
count++;
}
}
if(count == 0)
{
Alert.alert("false");
}
}
render() {
return (
<View style={styles.containerStyle}>
<GreyTextbox
secureOption={false}
placeholder="username"
value={this.state.username}
onChangeText={username => this.setState({username})}
/>
<GreyTextbox
secureOption={true}
placeholder="password"
value={this.state.password}
onChangeText={password => this.setState({password})}
/>
<TouchableOpacity
style={styles.buttonStyle}
onPress={this.Login.bind(this)} > >
<Text style={styles.textStyle}>Log In</Text>
</TouchableOpacity>
</View>
)
}
}
const styles = {
containerStyle: {
//flex: 0.35,
height: 180,
justifyContent: 'space-between',
marginTop: 40,
marginLeft: 30,
marginRight: 30,
position: 'relative'
},
buttonStyle: {
borderWidth: 1,
borderColor: '#3da8ff',
alignContent: 'center',
justifyContent: 'center',
marginLeft: 25,
marginRight: 25,
paddingTop: 5,
paddingBottom: 5,
},
textStyle: {
color: '#3da8ff',
alignSelf: 'center',
fontSize: 20
}
}

Create a method which changes the state and pass it down to the child component as prop.
export default class App extends Component {
constructor(props) {
super(props)
this.state = {status: 'initial'}
this.changeState = this.changeState.bind(this)
}
changeState() {
this.setState({
status: 'logged'
})
}
render() {
return (
<View>
<Header headerText="APP NAME"/>
<LoginBox changeState = {this.changeState}/>
<AdditionalActivity />
</View>
}
}
And in your LoginBox.js you can call it from props:
class LoginBox extends Component {
constructor(props) {
super(props);
this.login = this.login.bind(this);
}
login() {
this.props.changeState;
}
render() {
return (
{...}
<TouchableOpacity
style={styles.buttonStyle}
onPress={this.login} > >
<Text style={styles.textStyle}>Log In</Text>
</TouchableOpacity>
)
}
}
Another useful tip: Never bind functions in your render method!

Related

How to send a function to child component?

I want to know how I can send a function from my parent component to my child component. Because every time I get the error '"validationFunction" is not a function'. Very need your help. Also I will extend my SigIn component and add there another validation functions, so this is very important to me
SignIn component
import React, { Component } from 'react'
import { View } from 'react-native'
import { MyButton, MyTextInput, Header } from '../uikit'
import { styles } from '../constants/constants'
import { LOG_IN } from '../routes'
import { isEnoughSymbols } from '../validation/isEnoughSymbols'
export class SignIn extends Component {
state = {
symbolsValidation: true
}
isEnoughSymbols = (text) => {
if (text.trim().length < 8)
this.setState({ symbolsValidation: false })
}
render() {
const { mainContainer, buttons } = styles
return (
<View>
<View style={mainContainer}>
<MyTextInput
placeholder={'Email/Login'}
isSecure={false}
errorText={"Incorrect email format!"}
/>
<MyTextInput
placeholder={'Password'}
isSecure={true}
errorText={'Password must contain at least 8 symbols!'}
validationFunction={this.isEnoughSymbols.bind(this)}
/>
<MyTextInput
placeholder={'Confirm password'}
isSecure={true}
errorText={"Passwords don't match"}
/>
<View style={buttons}>
<MyButton
name={'confirm'.toUpperCase()}
onPress={() => this.props.navigation.navigate(LOG_IN)} />
</View>
</View>
</View>
)
}
}
And MyTextInput component
import React, {Component} from 'react'
import { TextInput, View, StyleSheet } from 'react-native'
import { w, width, h } from '../constants/constants'
import { ErrorMessage } from '.'
//import { isEnoughSymbols } from '../validation/isEnoughSymbols'
const MyTextInput = ({ placeholder, isSecure, errorText, validationFunction }) => {
let isValid = true
return (
<View style={styles.container}>
<TextInput
style={styles.text}
placeholder={placeholder}
secureTextEntry={isSecure}
onEndEditing={(e) => validationFunction(e.nativeEvent.text)}
>
</TextInput>
{
isValid ? null : <ErrorMessage
errorText={errorText}
/>
}
</View>
)
}
const styles = StyleSheet.create({
container: {
justifyContent: 'flex-start'
},
text: {
borderBottomWidth: 1,
borderColor: 'black',
fontSize: 30,
width: w - w / 10,
alignSelf: 'center',
textAlign: 'left',
marginTop: 20,
color: 'black'
},
}
)
export { MyTextInput }

How to get the value from a Text Input (a component) to my main app?

I have my main app with a Text, a Text Input (a component) and a button (another component):
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View, Alert } from 'react-native';
import { Tinput } from './components/Tinput.js';
import { Button } from './components/Button.js';
export default function App() {
return (
<View style={styles.container}>
<Text style={{fontSize:20, padding:20, textAlign:'center'}}>Ingrese un numero del pokémon a buscar!</Text>
<Tinput/>
<Button onPress={()=> ConsultarPokemon(/*this is where i want to insert the value from Tinput */)}>
Ingresar
</Button>
<StatusBar style="auto" />
</View>
);
}
And this is my component Tinput, which has the text input:
import React from 'react';
import { TextInput } from 'react-native';
const Tinput = () => {
const [numero, setNumero] = React.useState('');
return (
<TextInput
style={{ width:'90%', height: 50, borderColor: 'gray', borderWidth: 1, backgroundColor: '#fffff0', textAlign:'center', borderRadius: 20, borderWidth:5, margin:20}}
onChangeText={(value) => setNumero({numero: value})}
value={this.state.numero}
maxLength={20}
/>
);
}
export { Tinput };
I want to use the value from the text input on my onPress function, I tried doing this but didn't work:
<Button onPress={()=> ConsultarPokemon(Tinput.state.numero)}>
Ingresar
</Button>
Also, there's an error on my Tinput component: undefined is not an object (evaluating '_this.state.numero')
So I'm probably using state the wrong way too
You will use this.state.X only if you created classes like component classes , and here is an exemple :
class Counter extends React.Component {
constructor(props) {
super(props);
this.initialCount = props.initialCount || 0;
this.state = {
count: this.initialCount
}
}
increment() {
this.setState({ count: this.state.count + 1 })
}
reset() {
this.setState({ count: this.initialCount})
}
render() {
const { count } = this.state;
return (
<>
<button onClick={this.increment.bind(this)}>+1</button>
<button onClick={this.reset.bind(this)}>Reset</button>
<p>Count: {count}</p>
</>
);
}
}
I suggest to do not complicate things and just handle onPress inside Tinput
const Tinput = () => {
const [numero, setNumero] = React.useState('');
return (
<View>
<TextInput
style={{ width:'90%', height: 50, borderColor: 'gray', borderWidth: 1, backgroundColor: '#fffff0', textAlign:'center', borderRadius: 20, borderWidth:5, margin:20}}
onChangeText={(value) => setNumero(value)}
value={numero} // no need to use this.state.numero
maxLength={20}
/>
<Button onPress={()=> ConsultarPokemon(numero)}>
Ingresar
</Button>
</View>
);
}

react native: how to store complete state and all it's props using AsyncStorage

I have a Reminder Component with a simple form comprising of one TextInput from react-native and one DatePicker from native-base and one submit to store the value on click.
I am trying to implement AyncStorage to store those values locally and later display them on another screen. But I am unable to do so, as I am getting an error 'Value is not defined.'
Whatever blog posts and tuts, the person was storing only one single property. I want to store complete state i.e input field and the date onclick of the save button.
This is my ReminderComponent
import React, { Component } from 'react';
import { View,StyleSheet, AsyncStorage, TextInput } from 'react-native';
import {
Form,
Button, Icon,
DatePicker, Text
} from 'native-base';
import PropTypes from 'prop-types';
class Reminder extends Component {
constructor(props) {
super(props);
this.state = {
input: '',
chosenDate: new Date(),
};
this.setDate = this.setDate.bind(this);
this.handleChangeInput = this.handleChangeInput.bind(this);
this.saveData = this.saveData.bind(this);
}
setDate(newDate) {
this.setState({
chosenDate: newDate
});
}
handleChangeInput = (text) => {
this.setState({input:text});
}
//On application loads, this will get the already saved data and set
//the state true when it's true.
componentDidMount() {
AsyncStorage.getItem("key").then((value) => {
this.setState({'key':value});
});
}
//save the input
saveData(value) {
console.log('value', value);
AsyncStorage.setItem("key", value);
this.setState({'key':value});
}
render() {
const {input} = this.state;
return (
<View>
<Form style={styles.formContainer}>
<View style={styles.formView}>
< TextInput
placeholder = "Set your reminder"
onChangeText={this.handleChangeInput}
value={this.state.input}
/>
<DatePicker
defaultDate={new Date()}
minimumDate={new Date(2018, 1, 1)}
maximumDate={new Date(2019, 12, 31)}
locale={"en"}
timeZoneOffsetInMinutes={undefined}
modalTransparent={false}
animationType={"fade"}
androidMode={"default"}
placeHolderText="Select date"
textStyle={{ color: "green" }}
placeHolderTextStyle={{ color: "#d3d3d3" }}
onDateChange={this.setDate}
/>
<Text style={styles.datePicker}>
{this.state.chosenDate.toString().substr(4, 12)}
</Text>
</View>
<View style={styles.footer}>
<Button block success style={styles.saveBtn}
onPress={ () =>
{this.saveData()
console.log('save data', value);}
}
>
<Icon type='MaterialIcons' name='done' />
</Button>
</View>
</Form>
</View>
);
}
}
const styles = StyleSheet.create({
formContainer: {
marginTop: 10,
padding: 10,
},
editIcon:{
color: '#28F1A6',
fontSize: 26,
},
editBtn:{
flex: 1,
alignSelf: 'flex-end',
},
datePicker:{
alignSelf: 'auto',
paddingLeft: 10
},
footer:{
position: 'relative',
top: 350
},
saveBtn: {
position:'relative',
marginTop: 35,
}
});
export default Reminder;
This is my ReminderScreen.
import React, { Component } from 'react';
import { View, StatusBar } from 'react-native';
import PropTypes from 'prop-types';
import Reminder from '../components/Reminder';
const ReminderScreen = ({navigation}) => (
<View >
<Reminder navigation={navigation} >
<StatusBar backgroundColor = "#28F1A6" />
</Reminder >
</View>
);
Reminder.propTypes = {
navigation: PropTypes.object.isRequired
}
export default ReminderScreen;
Some tweaks needed in the saveData function and get items from Asyncstorage.
While storing the data in AsyncStorage just convert entire state as a string and save it. For retrieving just convert the string to JSON object and set the value in setState function.
Please see the modified code below for Remainder component.
import React, { Component } from 'react';
import { View,StyleSheet, AsyncStorage, TextInput } from 'react-native';
import {
Form,
Button, Icon,
DatePicker, Text
} from 'native-base';
import PropTypes from 'prop-types';
class Reminder extends Component {
constructor(props) {
super(props);
this.state = {
input: '',
chosenDate: new Date(),
};
this.setDate = this.setDate.bind(this);
this.handleChangeInput = this.handleChangeInput.bind(this);
this.saveData = this.saveData.bind(this);
}
setDate(newDate) {
this.setState({
chosenDate: newDate
});
}
handleChangeInput = (text) => {
this.setState({input:text});
}
//On application loads, this will get the already saved data and set
//the state true when it's true.
componentDidMount() {
AsyncStorage.getItem("key").then((value) => {
this.setState(JSON.parse(value));
});
}
//save the input
saveData() {
AsyncStorage.setItem("key", JSON.stringify(this.state));
}
render() {
const {input} = this.state;
return (
<View>
<Form style={styles.formContainer}>
<View style={styles.formView}>
< TextInput
placeholder = "Set your reminder"
onChangeText={this.handleChangeInput}
value={this.state.input}
/>
<DatePicker
defaultDate={new Date()}
minimumDate={new Date(2018, 1, 1)}
maximumDate={new Date(2019, 12, 31)}
locale={"en"}
timeZoneOffsetInMinutes={undefined}
modalTransparent={false}
animationType={"fade"}
androidMode={"default"}
placeHolderText="Select date"
textStyle={{ color: "green" }}
placeHolderTextStyle={{ color: "#d3d3d3" }}
onDateChange={this.setDate}
/>
<Text style={styles.datePicker}>
{this.state.chosenDate.toString().substr(4, 12)}
</Text>
</View>
<View style={styles.footer}>
<Button block success style={styles.saveBtn}
onPress={ () =>
{this.saveData()
console.log('save data', value);}
}
>
<Icon type='MaterialIcons' name='done' />
</Button>
</View>
</Form>
</View>
);
}
}
const styles = StyleSheet.create({
formContainer: {
marginTop: 10,
padding: 10,
},
editIcon:{
color: '#28F1A6',
fontSize: 26,
},
editBtn:{
flex: 1,
alignSelf: 'flex-end',
},
datePicker:{
alignSelf: 'auto',
paddingLeft: 10
},
footer:{
position: 'relative',
top: 350
},
saveBtn: {
position:'relative',
marginTop: 35,
}
});
export default Reminder;

Component not rendering in react-native

Though I am experienced in React I am very new to react-native. I have tried several answers posted in SO for the same issue but none of them helping me fix the issue.
I have App component and the App component calls Account component. The Account component renders input fields but the input fields are not displayed in UI but If I add only Text in App component like <Text>Hello</Text> it is showing in UI but my custom Account component is not showing in the UI. I am not getting an idea what I am doing wrong.
PFB component details
App.js
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Account from './components/Form/components/Account';
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<Account />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Account.js
import React, { Component } from "react";
import { StyleSheet, Text, View, Button, TextInput, ScrollView } from 'react-native';
export default class Account extends Component {
constructor(props){
super(props);
this.state = {
cardNo: "",
amount: 0
}
}
handleCardNo = no => {
this.setState({
cardNo: no
});
}
handleAmount = amount => {
this.setState({
amount
});
}
handleSend = event => {
const { cardNo, amount } = this.state;
this.setState({
loading: true
});
const regex = /^[0-9]{1,10}$/;
if(cardNo == null){
}else if (!regex.test(cardNo)){
//card number must be a number
}else if(!regex.test(amount)){
//amount must be a number
}else{
// const obj = {};
// obj.cardNo = cardNo;
// obj.amount = amount;
// const url = "http://localhost:8080/apple"
// axios.post(url, obj)
// .then(response => return response.json())
// .then(data => this.setState({
// success: data,
// error: "",
// loading: false
// }))
// .catch(err => {
// this.setState({
// error: err,
// success: "",
// loading: false
// })
// })
//values are valid
}
}
render(){
const { cardNo, amount } = this.state;
return(
<View>
<TextInput label='Card Number' style={{height: 40, flex:1, borderColor: '#333', borderWidth: 1}} value={cardNo} onChangeText={this.handleCardNo} />
<TextInput label='Amount' style={{height: 40, flex:1, borderColor: '#333', borderWidth: 1}} value={amount} onChangeText={this.handleAmount} />
<Button title='Send' onPress={this.handleSend}/>
</View>
)
}
}
Your code has some styling issues. Try changing you render function with this
render() {
const { cardNo, amount } = this.state
return (
<View style={{ alignSelf: 'stretch' }}>
<TextInput
placeholder="Card Number"
style={{ height: 40, borderColor: '#333', borderWidth: 1 }}
value={cardNo}
onChangeText={this.handleCardNo}
/>
<TextInput
placeholder="Amount"
style={{ height: 40, borderColor: '#333', borderWidth: 1 }}
value={amount}
onChangeText={this.handleAmount}
/>
<Button title="Send" onPress={this.handleSend} />
</View>
)
}

Custom Tabs using react-native

I have created custom tabs in react-native but I am unable to select a tab. I have initialized the state for the selected tab but do not know where to set the state.
here is my code:
'use strict';
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
Image,
View
} from 'react-native';
var Dimensions = require('Dimensions');
var windowSize = Dimensions.get('window');
var bg = require('image!bg');
class TabView extends Component {
constructor(props) {
super(props);
this.state = {
selectedTab: 'list',
selectedTab: 'map'
};
}
render() {
return (
<View style={styles.container}>
<Image style={styles.bg} source={bg} />
<View style={styles.tabView}>
<View style={[styles.listView,styles.selectedView]}>
<Text>List View</Text>
</View>
<View style={[styles.listView,{}]}>
<Text>Map View</Text>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
bg: {
position: 'absolute',
left: 0,
top: 0,
width: windowSize.width,
height: windowSize.height
},
tabView: {
flexDirection: 'row',
//bottom: 200,
borderWidth:2,
borderColor:'rgba(4, 193, 3,1)',
borderRadius: 5,
marginHorizontal: 20,
marginTop: 50
},
listView: {
flex: 2,
padding:7,
alignItems:'center'
},
mapView: {
flex: 2,
padding:7,
alignItems:'center'
},
selectedView: {
backgroundColor:'rgba(4, 193, 3,1)',
color: 'white'
}
});
module.exports = TabView
I just want to know where shall I add a check so that I can make a difference in the selected tab
Any help will be appreciated.
Please, check out the code here, to get an idea how it can be done
const Tab = (props) => {
let style = props.isSelected && styles.selectedTab || styles.normalTab;
return (
<View style={style}>
<TouchableHighlight onPress={() => props.onTabPress(props.id)}>
<Text>{props.title}</Text>
</TouchableHighlight>
</View>
)
}
class TabsView extends Component {
constructor(props) {
super(props)
this.state = {
selectedTab: 'one'
}
}
render() {
return (
<View>
<Tab onTabPress={this.onSelectTab.bind(this)} title="One" id="one" isSelected={this.state.selectedTab == "one"}/>
<Tab onTabPress={this.onSelectTab.bind(this)} title="Two" id="two" isSelected={this.state.selectedTab == "two"}/>
</View>
)
}
onSelectTab(selectedTab) {
this.setState({ selectedTab })
}
}
The above code splits your component in two parts, a logical part (TabsView) and a dumb presentational part (Tab)
The logical handles the clickHandler (onSelectTab) which is passed as a prop (onTabPress) to the dumb (Tab) Component.
I just want to know where shall I add a check so that I can make a difference in the selected tab
In the render method, it should go
example:
render() {
let FirstTabStyles = Object.assign(
defaultTabStyles,
(isFirstSelected && selectedStyles || {})
)
let SecondTabStyle = Object.assign(
defaultTabStyles,
(isSecondSelected && selectedStyles || {})
)
return (
<View>
<FirstTab style={FirstTabStyle} />
<SecondTab style={SecondTabStyle} />
</View>
)
}

Categories