Im making a component and calling it in my app.js with props inside like { placeholder }, but it always returns a refrenceError: Can't find variable placeholder. I don't understand why.
Calling it:
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import * as firebase from 'firebase';
import { Input } from './components/input'
import { Button } from './components/button'
export default class App extends React.Component {
state = {
email: '',
password: '',
}
render() {
return (
<View style={styles.container}>
<Input
placeholder='Enter your email'
label='Email'
onChangeText={password => this.setState({ password })}
value={this.state.password}
/>
<Input
placeholder='Enter your password'
label='Password'
secureTextEntry
onChangeText={email => this.setState({ email })}
value={this.state.email}
/>
<Button>Login</Button>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
padding: 20,
},
});
And the component
import React from 'react';
import { View, StyleSheet, Text, TextInput } from 'react-native';
const Input = () => {
return (
<View>
<Text style={styles.label}>Label</Text>
<TextInput
style={styles.input}
placeholder={ placeholder }
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
marginTop: 10,
width: '100%',
borderColor: '#eee',
borderBottomWidth: 2,
},
label: {
padding: 5,
paddingBottom: 0,
color: '#eee',
fontSize: 17,
fontWeight: '700',
width: '100%'
},
input: {
paddingRight: 5,
paddingLeft: 5,
paddingBottom: 2,
backgroundColor: '#eee',
fontSize: 18,
fontWeight: '700',
width: '100%',
}
})
export { Input };
const Input = ({ placeholder }) => { //<==== here
return (
<View>
<Text style={styles.label}>Label</Text>
<TextInput
style={styles.input}
placeholder={ placeholder }
/>
</View>
);
}
props will not be passing in automatically. It will be passed in as an argument, and your input component doesnt accept any argument and you're trying to access a variable placeholder and hence getting the error stated
Your Input is taking no props at all. You need to pass the props as parameters of the component function:
const Input = (props) => {
return (
<View>
<Text style={styles.label}>Label</Text>
<TextInput
style={styles.input}
placeholder={ props.placeholder }
/>
</View>
);
}
Accept props as a parameter in the Input component, then use props to access placeholder. You need to change the code of Input component to
const Input = (props) => {
return (
<View>
<Text style={styles.label}>Label</Text>
<TextInput
style={styles.input}
placeholder={ props.placeholder }
/>
</View>
);
}
Hope this will help!
Related
I was trying to use a created TextField component inside Formik, I created my own TextField component called Input and imported inside the form, but I cant change any of it's props or call it's methods, like style or onChangeText, any method I try to use with onChangeText won't work, for example. Here's my code from the component and the formulary.
Here's the code of my Input Component and the form where I import it to:
// Input Component
import React, { useState } from 'react';
import { TextInput, TextInputProps } from 'react-native';
import styles from '../../styles/styles';
interface InputProps extends TextInputProps{
value: string,
}
const Input: React.FC<InputProps> = ({value, ...rest}) => {
const [color, setColor] = useState("#f2f2f2");
return (
<TextInput
onFocus={() => setColor('#f9ffc4')}
onBlur={() => setColor('#f2f2f2')}
style={{
width: "70%",
minHeight: 40,
borderColor: styles.primaryColor,
borderBottomWidth: 1,
padding: 0,
borderRadius: 5,
marginBottom: 5,
backgroundColor: color,
}}
>
</TextInput>
)
}
export default Input;
// Form Page
import React from 'react';
import { Button, TextInput, View } from 'react-native';
import { Formik } from 'formik'
import Input from '../../../components/inputs/Input';
export default function FormikTest({ }) {
return (
<Formik
initialValues={{ input: '', teste: '' }}
onSubmit={values => console.log(values)}
>
{({ handleChange, handleSubmit, values }) => (
<View style={{ padding: 8, alignItems: 'center' }}>
<TextInput
style={{
margin: 10,
width: '50%',
height: 50,
borderWidth: 1,
borderColor: '#000',
}}
onChangeText={handleChange('input')}
value={values.input}
/>
<Input
onChangeText={() => { console.log('aqui') }}
value={values.teste}
/>
<Button onPress={handleSubmit} title="Submit" />
</View>
)}
</Formik>
)
}
In order for your custom Input to recognise the props you are passing, you have to instruct it to recognise them. Like so
const Input: React.FC<InputProps> = (props) => {
const [color, setColor] = useState("#f2f2f2");
return (
<TextInput
onFocus={() => setColor('#f9ffc4')}
onBlur={() => setColor('#f2f2f2')}
style={{
width: "70%",
minHeight: 40,
borderColor: styles.primaryColor,
borderBottomWidth: 1,
padding: 0,
borderRadius: 5,
marginBottom: 5,
backgroundColor: color,
}}
onChangeText={props.onChangeText}
autoFocus={props.autoFocus}
...
>
</TextInput>
)
}
export default Input;
For every prop you want to pass, you have to add it to the input, otherwise how does you custom component know what you want from it?
With my current code, I have two input fields and a drop down menu. When ever a value is placed into the field or modified, it clears the rest of the fields. The only one that will stay consistent is the drop down menu. I have suspicions that my useEffect hooks may be doing something, but I'm quite unsure of why. Any suggestions would be great.
(FYI: storeArtic is the push to firebase)
CustomScreen.js
import React, { useState } from "react";
import { StyleSheet, Text, Keyboard, View, TouchableWithoutFeedback } from "react-native";
import { Button, Input } from "react-native-elements";
import { Dropdown } from "react-native-material-dropdown";
import { storeArtic } from '../helpers/fb-settings';
const CustomScreen = ({ route, navigation }) =>{
//create a screen with the ability to add a picture with text to the deck of artic cards
//add check box solution for selection of word type (maybe bubbles, ask about this)
const articDrop = [
{value: 'CV'},
{value: 'VC'},
{value: 'VV'},
{value: 'VCV'},
{value: 'CVCV'},
{value: 'C1V1C1V2'},
{value: 'C1V1C2V2'},
];
const [articCard, setCard] = useState({
word: '',
imageUrl: '',
aType:'',
cType: '',
mastery: false
})
return(
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View>
<Text>Please enter the information of your custom card!</Text>
<Input
placeholder="Enter valid image url"
value={articCard.imageUrl}
autoCorrect={false}
onChangeText={(val) => setCard({ imageUrl: val })}
/>
<Input
placeholder="Enter word or phrase"
value={articCard.word}
autoCorrect={false}
onChangeText={(val) =>
setCard({ word: val, aType: val.charAt(0).toUpperCase(), mastery: false})
}
/>
<Dropdown
value={articCard.cType}
onChangeText={(text) => setCard({cType: text})}
label="Artic Type"
data={articDrop}
/>
<Button
//this will save the cards to the database
title="Save"
onPress={() => {
storeArtic({articCard})
}}
/>
<Button
title="Clear"
onPress={() => {
setCard({word: '', aType: '', cType: '', imageUrl: '', mastery: false});
navigation.navigate('Home');
}}
/>
</View>
</TouchableWithoutFeedback>
)
}
export default CustomScreen;
HomeScreen.js
import React, { useState, useEffect } from "react";
import { StyleSheet, Text, Keyboard, TouchableOpacity, View, TouchableWithoutFeedback, Image } from "react-native";
import { Button } from "react-native-elements";
import { Feather } from "#expo/vector-icons";
import { initArticDB, setupArticListener } from '../helpers/fb-settings';
const HomeScreen = ({route, navigation}) => {
const [ initialDeck, setInitialDeck] = useState([]);
useEffect(() => {
try {
initArticDB();
} catch (err) {
console.log(err);
}
setupArticListener((items) => {
setInitialDeck(items);
});
}, []);
useEffect(() => {
if(route.params?.articCard){
setCard({imageUrl: state.imageUrl, word: state.word, aType: state.aType, cType: state.cType, mastery: state.mastery})
}
}, [route.params?.articType] );
navigation.setOptions({
headerRight: () => (
<TouchableOpacity
onPress={() =>
navigation.navigate('Settings')
}
>
<Feather
style={styles.headerButton}
name="settings"
size={24}
color="#fff"
/>
</TouchableOpacity>
),
headerLeft: () => (
<TouchableOpacity
onPress={() =>
navigation.navigate('About')
}
>
<Text style={styles.headerButton}> About </Text>
</TouchableOpacity>
),
});
return(
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.container}>
<Text style={styles.textmenu}>Welcome to Artic Cards</Text>
<Text style={styles.textsubmenu}>Press Start to Begin!</Text>
<Image source={require('../assets/5-snowflake-png-image.png')}
style={{width: 300, height: 300, alignSelf: 'center'}}/>
<Button
title="Start"
style={styles.buttons}
onPress={() => navigation.navigate('Cards',
{passDeck: initialDeck})}
/>
<Button
title="Progress"
style={styles.buttons}
onPress={() => navigation.navigate('Progress')}
/>
<Button
title="Customize"
style={styles.buttons}
onPress={() => navigation.navigate('Customize')}
/>
</View>
</TouchableWithoutFeedback>
);
};
const styles = StyleSheet.create({
container: {
padding: 10,
backgroundColor: '#E8EAF6',
flex: 1,
justifyContent: 'center'
},
textmenu: {
textAlign: 'center',
fontSize: 30
},
textsubmenu:{
textAlign: 'center',
fontSize: 15
},
headerButton: {
color: '#fff',
fontWeight: 'bold',
margin: 10,
},
buttons: {
padding: 10,
},
inputError: {
color: 'red',
},
input: {
padding: 10,
},
resultsGrid: {
borderColor: '#000',
borderWidth: 1,
},
resultsRow: {
flexDirection: 'row',
borderColor: '#000',
borderBottomWidth: 1,
},
resultsLabelContainer: {
borderRightWidth: 1,
borderRightColor: '#000',
flex: 1,
},
resultsLabelText: {
fontWeight: 'bold',
fontSize: 20,
padding: 10,
},
resultsValueText: {
fontWeight: 'bold',
fontSize: 20,
flex: 1,
padding: 10,
},
});
export default HomeScreen;
Unlike class based setState, with functional components when you do setState, it will override the state with what you provide inside setState function. It is our responsibility to amend state (not overrite)
So, if your state is an object, use callback approach and spread previous state and then update new state.
Like this
<Input
placeholder="Enter valid image url"
value={articCard.imageUrl}
autoCorrect={false}
onChangeText={(val) => setCard(prev => ({ ...prev, imageUrl: val }))} //<----- like this
/>
Do the same for all your inputs.
I am creating a simple login page in react native and am facing some confusions in styling. How can I make the text on the button in the center?
How can I take the INSTARIDE text upwards and not so close to the username field. Code could be run on Expo Snack.
import React, { Component } from 'react';
import { Alert, Button, Text, Container, Header, Content, TextInput, Form, Item, View, Label, StyleSheet } from 'react-native';
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: '',
};
}
onLogin() {
const { username, password } = this.state;
Alert.alert('Credentials', `${username} + ${password}`);
}
render() {
return (
<View style={styles.container}>
<Text style={{fontWeight: 'bold'}}>
My App
</Text>
<TextInput
value={this.state.username}
onChangeText={(username) => this.setState({ username })}
placeholder={'Username'}
style={styles.input}
/>
<TextInput
value={this.state.password}
onChangeText={(password) => this.setState({ password })}
placeholder={'Password'}
secureTextEntry={true}
style={styles.input}
/>
<Button rounded success
title={'Login!'}
style={styles.input}
color = '#65c756'
onPress={this.onLogin.bind(this)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#1b1c1c',
},
input: {
width: 200,
height: 33,
padding: 10,
borderWidth: 1,
borderColor: 'black',
marginBottom: 10,
},
});
Check this below code
import React, { Component } from 'react';
import { Alert, Button, Text, TextInput, View, StyleSheet } from 'react-native';
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: '',
};
}
onLogin() {
const { username, password } = this.state;
Alert.alert('Credentials', `${username} + ${password}`);
}
render() {
return (
<View style={styles.container}>
<Text style={styles.textStyle}>My App</Text>
<TextInput
value={this.state.username}
onChangeText={username => this.setState({ username })}
placeholder={'Username'}
style={styles.input}
/>
<TextInput
value={this.state.password}
onChangeText={password => this.setState({ password })}
placeholder={'Password'}
secureTextEntry={true}
style={styles.input}
/>
<View style={styles.buttonStyle}>
<Button
rounded
success
title={'Login!'}
color="#65c756"
onPress={this.onLogin.bind(this)}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
input: {
width: '90%',
padding: 10,
borderWidth: 1,
borderColor: 'black',
marginBottom: 10,
alignSelf: 'center',
},
buttonStyle: {
width: '90%',
alignSelf: 'center',
},
textStyle: {
fontWeight: 'bold',
justifyContent: 'center',
alignSelf: 'center',
marginBottom: 30,
fontSize: 18,
},
});
Change this according to your requirement. Feel free for doubts
Create custom button by text and touchable opacity
<TouchableOpacity style={{backgroundColor:'#65c756',paddingVertical:5,paddingHorizontal: 30,borderRadius:6}} onPress={this.onLogin.bind(this)}>
<Text style={{color:'white'}}>Login!</Text>
</TouchableOpacity>
for text close to input just add some padding or margin b/w them
To center the button, try adding text-align: center; to the style prop in <Text style={{color:'white'}}>Login!</Text>
For both iOS and Android simulators
The text just disappears/flickers when I start typing. I tried having an initial state of texts with some value instead of keeping it empty. With this the TextInput sticks to this initial state and does not update itself with new text entered.
I think the state is not updating with 'onChangeText' property, but I am not completely sure.
People have seem to solve this, as they had few typos or missing pieces in code. However I have checked mine thoroughly.
Please help if I have missed anything in the below code.
LoginForm.js
import React, { Component } from 'react';
import { Card, Button, CardSection, Input } from './common';
class LoginForm extends Component {
state = { email: '', password: '' }
render() {
return (
<Card>
<CardSection>
<Input
label="Email"
placeHolder="user#gmail.com"
onChangeText={text => this.setState({ email: text })}
value={this.state.email}
/>
</CardSection>
<CardSection>
<Input
secureTextEntry
label="Password"
placeHolder="password"
onChangeText={text => this.setState({ password: text })}
value={this.state.password}
/>
</CardSection>
<CardSection>
<Button>
Log In
</Button>
</CardSection>
</Card>
);
}
}
export default LoginForm;
Input.js
import React from 'react';
import { TextInput, View, Text } from 'react-native';
const Input = ({ label, value, onChangeText, placeholder, secureTextEntry }) => {
const { inputStyle, labelStyle, containerStyle } = styles;
return (
<View style={containerStyle}>
<Text style={labelStyle}>{label}</Text>
<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: 23,
flex: 2
},
labelStyle: {
fontSize: 18,
paddingLeft: 20,
flex: 1
},
containerStyle: {
height: 40,
flex: 1,
flexDirection: 'row',
alignItems: 'center'
}
};
export { Input };
The only way to solve this was to change the way the values of TextInput fields are updated, with this code below.
value={this.state.email.value}
value={this.state.password.value}
You problem is how the Input component is written.
There is a render function written inside the stateless component which is not a React class component:
const Input = ({ label, value, onChangeText, placeHolder, secureTextEntry }) => ( // ← remove the wrapping parentheses
{
render() { // <--- this should not be here
↑
const { inputStyle, labelStyle, containerStyle } = styles;
return (
<View style={containerStyle} >
<Text style={labelStyle}>{label}</Text>
<TextInput
secureTextEntry={secureTextEntry}
autoCorrect={false}
placeholder={placeHolder}
style={inputStyle}
onChangeText={onChangeText}
value={value}
underlineColorAndroid="transparent"
/>
</View>
);
}
}
);
Change it to this:
const Input = ({ label, value, onChangeText, placeHolder, secureTextEntry }) => {
const { inputStyle, labelStyle, containerStyle } = styles;
return (
<View style={containerStyle} >
<Text style={labelStyle}>{label}</Text>
<TextInput
secureTextEntry={secureTextEntry}
autoCorrect={false}
placeholder={placeHolder}
style={inputStyle}
onChangeText={onChangeText}
value={value}
underlineColorAndroid="transparent"
/>
</View>
);
};
See running example
I am trying to get user input, create an array of objects from userInput and save that array of objects into an array. Below is code I have written, but no output.
import React, { Component } from 'react';
import {Text, View, StyleSheet, TextInput, Image, TouchableOpacity, ListView} from 'react-native';
//import {Actions} from 'react-native-router-flux';
const count = 0;
export default class SecondPage extends Component {
constructor(props) {
super(props);
this.state = {
quan:'',
desc:'',
amt:'',
dataStorage :[],
data: { quantity: this.quan, description: this.desc, amount: this.amt },
}
}
_add(){
console.log('Add button pressed');
this.state.dataStorage[count].push(this.state.data);
console.log(this.state.data);
count++;
}
render(){
return(
<View style={styles.container}>
<View style={styles.itemDescription}>
<Text style={styles.itemDescriptionText}>QUANTITY</Text>
<Text style={styles.itemDescriptionText}>DESCRIPTION</Text>
<Text style={styles.itemDescriptionText}>AMOUNT</Text>
<TouchableOpacity style={styles.addButton} onPress={this._add}>
<Text style={styles.addButtonText}>ADD</Text>
</TouchableOpacity>
</View>
<View style={styles.rowsOfInput}>
<TextInput style = {styles.nameInput}
onChangeText={(text) => this.setState({quan: text})}
value = {this.state.quan}
autoCapitalize='none'
autoCorrect={false}
returnKeyType="next"
keyboardAppearance="dark"
/>
<TextInput style = {styles.nameInput}
onChangeText={(text) => this.setState({desc: text})}
value = {this.state.desc}
autoCapitalize='none'
autoCorrect={false}
returnKeyType="next"
keyboardAppearance="dark"
/>
<TextInput style = {styles.nameInput}
onChangeText= {(text) => this.setState({amt: text})}
value = {this.state.amt}
autoCapitalize='none'
autoCorrect={false}
returnKeyType="next"
keyboardAppearance="dark"
/>
</View>
</View>
)}
}
const styles = StyleSheet.create({
container: {
flexDirection: 'column',
},
itemDescription: {
marginTop:20,
backgroundColor:'#00CED1',
flexDirection:'row',
justifyContent:'space-between',
},
itemDescriptionText:{
fontSize:12,
color:'white',
},
addButton:{
borderWidth:1,
height:20,
borderRadius:5,
overflow:'hidden',
backgroundColor:'red',
},
addButtonText:{
paddingLeft:10,
paddingRight:10,
},
nameInput:{
flex:1,
height: 20,
textAlignVertical:'bottom',
paddingLeft: 5,
paddingRight: 5,
fontSize: 12,
backgroundColor:'#E0FFFF',
},
hairLine:{
height:1,
backgroundColor:'black',
marginTop:0,
marginLeft:20,
marginRight:20
},
rowsOfInput:{
// flex:1,
flexDirection:'row',
justifyContent:'space-around'
},
});
Whats wrong in the code? I want to store userInput for each entry into QUANTITY, DESCRIPTION, AMOUNT as array of object.
One of your issues is a scoping issue. Change your _add method to this.
_add = () => {
let dataStorage = [{amt: this.state.amt, quan: this.state.quan, desc: this.state.desc}, ...this.state.dataStorage]
console.log(dataStorage)
this.setState({dataStorage})
}
Also, your data property on state will never work and is unnecessary.
Here is an example.
It still won't display anything because you do nothing with dataStorage.