When adding text into a <TextInput> component with multiline set to false the value text matches the placeholder text font size, whereas if multiline is equal to true, the value text font size is smaller than the placeholder font size.
Is this a normal behaviour or a bug?
Edit:
/* #flow */
import React from 'react'
import Separator from './components/Separator'
import {Button, StyleSheet, TextInput, View} from 'react-native'
const styles = StyleSheet.create({
subject: {
height: 20,
},
body: {
height: 100,
},
})
export default class NewQuestion extends React.Component {
static navigationOptions = ({navigation}) => ({
title: 'AIBU',
headerLeft: (
<Button
onPress={() => {
navigation.goBack()
}}
title="Back"
/>
),
})
state = {
subject: '',
body: '',
}
render() {
return (
<View>
<TextInput
autoFocus
onChangeText={subject => this.setState({subject})}
placeholder="Enter your AIBU subject..."
style={styles.subject}
value={this.state.subject}
/>
<Separator />
<TextInput
multiline
onChangeText={body => this.setState({body})}
placeholder="Enter your AIBU description..."
style={styles.body}
value={this.state.body}
/>
</View>
)
}
}
As far as I see, the default font size is actually different for multiline and non-multiline TextInput-s. One way to handle this is by introducing another component which acts as an abstraction on top of TextInput:
const styles = StyleSheet.create({
text: {
fontFamily: 'System',
fontStyle: 'normal',
fontSize: 20,
lineHeight: 20,
letterSpacing: 0.6
}
})
export default class AppTextInput extends Component {
static propTypes = {
style: TextInput.propTypes.style
}
static defaultProps = {
style: {}
}
render() {
return (
<TextInput
{...this.props}
style={[
styles.text,
this.props.style
]}
/>
)
}
}
Now you can just use AppTextInput the same way TextInput is supposed to be used, and all issues related to styling inputs are covered in a single place.
Related
I am a bit new to react native and I have an issue I need help with
how do I build a text input in react native that has a placeholder that changes to a text view on top when clicked?
Similar to the screenshot below
empty text input field looks like this in its default state
text field with data entered
see the empty input text has a placeholder appearing in the middle of the input text field
see the second diagram, the place holder text is moved to the top of the input field once the user starts typing text into the input field
The easiest way is to use react-native-paper package with their text input:
import * as React from 'react';
import { TextInput } from 'react-native-paper';
const MyComponent = () => {
const [text, setText] = React.useState('');
return (
<TextInput
label="Email"
value={text}
onChangeText={text => setText(text)}
/>
);
};
export default MyComponent;
Result:
Here is what I use without any library, working example and if you want to add animation to the fields working example with animation
import React, { Component } from 'react';
import {
View,
StatusBar,
TextInput,
Text,
} from 'react-native';
class FloatingLabelInput extends Component {
state = {
isFocused: false,
};
handleFocus = () => this.setState({ isFocused: true });
handleBlur = () => this.setState({ isFocused: false });
render() {
const { label, ...props } = this.props;
const { isFocused } = this.state;
const labelStyle = {
position: 'absolute',
left: 0,
top: !isFocused ? 18 : 0,
fontSize: !isFocused ? 20 : 14,
color: !isFocused ? '#aaa' : '#000',
};
return (
<View style={{ paddingTop: 18 }}>
<Text style={labelStyle}>
{label}
</Text>
<TextInput
{...props}
style={{ height: 26, fontSize: 20, color: '#000', borderBottomWidth: 1, borderBottomColor: '#555' }}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
blurOnSubmit
/>
</View>
);
}
}
export default class App extends Component {
state = {
value: '',
};
handleTextChange = (newText) => this.setState({ value: newText });
render() {
return (
<View style={{ flex: 1, padding: 30, backgroundColor: '#f5fcff' }}>
<StatusBar hidden />
<FloatingLabelInput
label="Email"
value={this.state.value}
onChangeText={this.handleTextChange}
/>
</View>
);
}
}
I am pretty new to react native and am trying to get the home page of my first basic app to load. Currently, it asks users to input their "name" and "networth" and I simply want to return the networth input multiplied by a constant with an alert that appears after pressing submit saying "{name} + " net worth will be " + {newNetworth} + " next year!". However right now my output is: "undefined will be worth undefined next year!" and I cannot figure out for the life of me how to get the actual values to pass through.
Here is my child component:
import React from 'react';
import { View, Text, TouchableOpacity, TextInput, StyleSheet } from 'react-native';
class OpeningPageQs extends React.Component {
state = {
name: '',
networth: 0,
newNetworth: 0
}
nextYearWorth() {
this.setState({
name: this.state.name,
newNetworth: this.state.networth * 1.1
});
return (alert(this.name + ' will be worth ' + this.newNetworth + ' next year!'));
}
render (){
return (
<View>
<TextInput style= { styles.input }
placeholder= "name"
onChangeText= { this.name }
/>
<TextInput style= { styles.input }
placeholder= 'networth'
onChangeText= { this.networth }
/>
<TouchableOpacity style = {styles.submitButton}
onPress = {
() => this.nextYearWorth(this.state.name, this.state.networth)
}
>
<Text style={styles.submitButtonText}>
Submit
</Text>
</TouchableOpacity>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
paddingTop: 23
},
input: {
margin: 15,
height: 40,
borderColor: '#7a42f4',
borderWidth: 1
},
submitButton: {
backgroundColor: '#7a42f4',
padding: 10,
margin: 15,
height: 40,
},
submitButtonText: {
color: 'white'
}
})
export default OpeningPageQs;
And here is my Parent component:
import { StatusBar } from 'expo-status-bar';
import React, { useState, Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import OpeningPageQs from './components/OpeningPageQs';
export default class App extends React.Component {
constructor (props){
super(props);
//building initial state for input props
this.state = {
name: '',
networth: this.networth
};
}
render(){
return (
<View style={styles.container}>
<StatusBar style="auto" />
<OpeningPageQs
name={this.state.name}
networth={this.state.networth}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Any help would be greatly appreciated
Reffering to: https://reactnative.dev/docs/textinput
You are wrongly setting the state. Try like this:
<TextInput style= { styles.input }
placeholder= "name"
onChangeText={text => this.setState({name: text})}
/>
It may be worth not setting the state just to show the results. You can either create local variable in nextYearWorth method, or multiple the value when setting the state inside onChangeText callback.
For the alert - please take a look at Modal component: https://reactnative.dev/docs/modal#docsNav
You can use like that.
<TextInput style= { styles.input }
placeholder= "name"
onChangeText= { text => this.name = text }
/>
I want to create some screen with stack and tabs navigator, but it seems not worked, it get error message like this error on virtual device...
Is this because the navigation or what?
This is my code
ConfirmActivation.js
import React, { Component } from 'react';
import { StyleSheet, Image } from 'react-native';
import { Container, Header, Content, Left, Right, Body, Text, StyleProvider,
Form, Item, Input, Label, Button, View } from 'native-base';
import { StackNavigator } from 'react-navigation';
import Icon from 'react-native-vector-icons/FontAwesome';
import getTheme from '../../../native-base-theme/components';
import material from '../../../native-base-theme/variables/material';
import Index from '../tabs/Index';
export default class sharpcs extends React.Component {
static navigationOptions = {
title: <Image source={require('../../assets/img/sharp_logo.png')} style={{width: 200, height: 50}} />,
header: null
}
render() {
return (
<AppNavigation/>
);
}
}
class ConfirmActivation extends Component{
static navigationOptions = {
title: <Image source={require('../../assets/img/sharp_logo.png')} style={{width: 200, height: 50}} />,
header: null
}
render() {
const { navigate } = this.props.navigation;
return (
<StyleProvider style={getTheme(material)}>
<Container style={styles.container} >
<Form style={styles.form} >
<Item floatingLabel>
<Label>Masukkan kode verifikasi</Label>
<Input keyboardType = 'numeric' maxLength = {13} />
</Item>
<View style={styles.buttonContainer} >
<Button
onPress={() =>
navigate('MainScreen')
} success style={{width: 125}} >
<Label style={{color: '#FFF', marginLeft: 24}} >
Lanjut
</Label>
</Button>
</View>
</Form>
</Container>
</StyleProvider>
);
}
}
const App = StackNavigator({
ThisScreen: { screen: ConfirmActivation },
MainScreen: { screen: Index }
});
const AppNavigation = () => (
<App />
);
const styles = StyleSheet.create({
form: {
flex: 2,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
},
container: {
padding: 20
},
buttonContainer: {
marginTop: 20,
flexDirection: 'row',
alignSelf: 'flex-end'
}
});
Index.js
import React, { Component } from 'react';
export default class Index extends React.Component {
render() {
return null;
}
}
The cause of the error is the title in the navigationOptions in sharpcs class. It expects a string. You had provided it an image component. Although the image appears, but this error appears when navigating. So use instead of title, headerTitle
I want to get the value of the email and passowrd in my Form.js, so that click click button can pass them a client api
1. this is my class src/components/login/Form.js
import React, { Component } from 'react';
import {
KeyboardAvoidingView,
StyleSheet,
TouchableHighlight,
Image,
} from 'react-native';
import UserInput from "./UserInput";
import usernameImg from '../../images/Iconperson.png';
import passwordImg from '../../images/Iconlock.png';
import eyeImg from '../../images/eye.png';
export default class Form extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
};
}
_onPress() {
console.warn("email", this.state.email)
console.warn("password", this.state.password)
}
render() {
return (
<KeyboardAvoidingView behavior='padding'
style={styles.container}>
<UserInput source={usernameImg}
onChangeText = {(email) => this.setState({email})}
keyboardType = 'email-address'
placeholder='Username'
autoCapitalize={'none'}
returnKeyType={'done'}
autoCorrect={false} />
<UserInput source={passwordImg}
onChangeText = {(password) => this.setState({password})}
placeholder='Password'
returnKeyType={'done'}
autoCapitalize={'none'}
autoCorrect={false} />
<TouchableHighlight
onPress={this._onPress}
activeOpacity={1} >
<Text>LOGIN</Text>
</TouchableHighlight>
</KeyboardAvoidingView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
}
});
2. this is my class src/components/login/UserInput.js
import React, { Component , PropTypes} from 'react';
import Dimensions from 'Dimensions';
import {
StyleSheet,
View,
TextInput,
Image,
} from 'react-native';
const DEVICE_WIDTH = Dimensions.get('window').width;
const DEVICE_HEIGHT = Dimensions.get('window').height;
export default class UserInput extends Component {
render() {
return (
<View style={styles.inputWrapper}>
<Image source={this.props.source}
style={styles.inlineImg} />
<TextInput style={styles.input}
onChangeText = {(this.props.ChangeText) =>
this.setState({field})}
placeholder={this.props.placeholder}
secureTextEntry={this.props.secureTextEntry}
autoCorrect={this.props.autoCorrect}
autoCapitalize={this.props.autoCapitalize}
returnKeyType={this.props.returnKeyType}
placeholderTextColor='white'
keyboardType ={this.props.keyboardType}
underlineColorAndroid='transparent' />
</View>
);
}
}
UserInput.propTypes = {
ChangeText : PropTypes.string,
source: PropTypes.number.isRequired,
placeholder: PropTypes.string.isRequired,
keyboardType : PropTypes.string,
secureTextEntry: PropTypes.bool,
autoCorrect: PropTypes.bool,
autoCapitalize: PropTypes.string,
returnKeyType: PropTypes.string,
};
const styles = StyleSheet.create({
input: {
backgroundColor: 'rgba(255, 255, 255, 0.2)',
width: DEVICE_WIDTH - 40,
height: 40,
marginHorizontal: 20,
paddingLeft: 45,
borderRadius: 20,
color: '#ffffff',
},
inputWrapper: {
flex: 1,
},
inlineImg: {
position: 'absolute',
zIndex: 99,
width: 22,
height: 22,
left: 35,
top: 9,
},
});
As you are typing into the TextInput I see you are trying to change the state in the Form component. For this to happen correctly, setState needs to be called from the Form component. There are a couple functions currently being passed down through props:
(email) => this.setState({email})
// and
(password) => this.setState({password})
Looking at how those are being used in your UserInput component, whenever a new character is added to that text box, invoke the function above. so when this.setState() is called, it is saying UserInput.setState(). Since we want to change the state in Form we have to bind those functions to the parent component.
Instead of passing the functions directly to props, let's add some user methods:
export default class Form extends Component {
constructor(props) {}
_onChangeEmail(email) {
this.setState(Object.assign({}, state, { email })); // good practice for immutability
}
_onChangePassword(password) {
this.setState(Object.assign({}, state, { password })); // good practice for immutability
}
render() {}
}
Next, we need to bind these class methods to itself. That way no matter where these are called, this will always point to the Form component. This is most commonly done in the constructor:
export default class Form extends Component {
constructor(props) {
super(props)
this.state = {}
// this is where we do the binding
this._onChangeEmail = this._onChangeEmail.bind(this)
this._onChangePassword = this._onChangePassword.bind(this)
}
_onChangeEmail(email) { /* code */}
_onChangePassword(password) { /* code */}
render() {}
}
Now pass these down to the UserInput component through props:
// Form.js
render() {
return (
<KeyboardAvoidingView>
<UserInput onChangeText={this._onChangeEmail} />
<UserInput onChangeText={this._onChangePassword} />
</KeyboardAvoidingView>
)
}
These methods should now be used when the user inputs text:
export default class UserInput extends Component {
render() {
return (
<View>
<Image />
<TextInput onChangeText = {this.props.onChangeText} />
</View>
);
}
}
Bonus: You also should add a property 'value' to the text input:
export default class UserInput extends Component {
render() {
return (
<View>
<Image />
<TextInput
onChangeText={this.props.onChangeText} />
value={this.props.textValue}
</View>
);
}
}
And make sure it is passed down from the parent component:
// Form.js
render() {
return (
<KeyboardAvoidingView>
<UserInput
onChangeText={this._onChangeEmail}
textValue={this.state.email}/>
<UserInput
onChangeText={this._onChangePassword}
textValue={this.state.password}/>
</KeyboardAvoidingView>
)
}
The value of TextInput may be retrieved through its _lastNativeTextproperty.
this.refs.myTextInput._lastNativeText
See this answer
Note: I am new to React Native and have searched up how to do this but found no helpful results I am using React Native to create an app and want to add multiple components, such as text, buttons, and a text input space, but am having trouble doing so without receiving errors. Is there any way to include multiple components into one javascript document using React Native?
The code I currently have:
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default class App extends React.Component {
render() {
return (
<View style={{alignItems: 'center'}}>
<Text style={styles.bigblack}>Sample Bold Text Here</Text>
<Text>Sample Text Here:</Text>
</View>
);
}
}
const styles = StyleSheet.create({
bigblack: {
color: 'black',
fontWeight: 'bold',
fontSize: 28,
},
red: {
color: 'red',
},
container: {
flex: 1,
backgroundColor: '#fdf5e6',
alignItems: 'center',
justifyContent: 'center',
},
});
Code I want to add for Text Input:
class UselessTextInput extends Component {
render() {
return (
<TextInput
{...this.props}
editable = {true}
maxLength = {40}
/>
);
}
}
export default class UselessTextInputMultiline extends Component {
constructor(props) {
super(props);
this.state = {
text: 'Useless Multiline Placeholder',
};
}
render() {
return (
<View style={{
backgroundColor: this.state.text,
borderBottomColor: '#000000',
borderBottomWidth: 1 }}
>
<UselessTextInput
multiline = {true}
numberOfLines = {4}
onChangeText={(text) => this.setState({text})}
value={this.state.text}
/>
</View>
);
}
}
Code I want to add for Button:
<Button
onPress={onPressLearnMore}
title="Learn More"
color="#841584"
accessibilityLabel="Learn more about this button"
/>
You can create multiple component in same document but can export default only one.
So you can create multiple component like below:
export class UselessTextInput {}
export class UselessTextInputMultiline {}
export class Button {}
while accessing :
import {UselessTextInput, UselessTextInputMultiline, Button} from './components/customInput' // change with your respective path
if you still want to have single export default then:
export default class UselessTextInputMultiline {}
and while importing
import Template,{Button} from './components/customInput'
For, exporting multiple component:
module.exports = {
text: UselessTextInput,
btn: Button
}
imports will be like:
let txtInput= require('./components/customInput').text;
let btnInput = require('./components/customInput').btn;