TextInput length counter in react-native - react-native

I have a TextInput with maxLength 100 in my scene, and I want to add a counter below which shows something like "38/100", auto-updating with 'onChangeText'.
So I have to figure out the length of the input value, somehow store it in this.state.textLength while storing the value itself to this.state.text, but I don't know how to do this in "onChangeText = {(text) => ...}" function.
Here is my simplified code:
export class RequestScene extends Component {
constructor() {
super();
this.state={
text: '',
textLength: 0,
category: '',
time: ''
};
}
render(){
return(
<View style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}}>
<View style={{
height: 200
}}>
<TextInput style={{
height: 200,
width: 360,
borderColor: 'lightgray',
borderWidth: 1,
padding: 3,
borderRadius: 3,
fontSize: 24}}
maxLength={100}
placeholder='무슨 상황인지 간단하게 써주세요'
multiline={true}
// this is where I am stuck. What should I do with 'textLength'?
onChangeText={
(text)=>this.setState({text})
(text)=>this.setState({textLength})
}
/>
<Text style={{
fontSize:10,
color:'lightgrey',
textAlign: 'right'
}}>
// currently this counter only shows '0/100'
{this.state.textLength}/100
</Text>

The onChangeText() function returns a string. You can do something like:
const maxLength = 100;
this.setState({
textLength: maxLength - text.length,
text, // same as `text: text`
});

To make your code easier to read, you can call a separate method within your onChangeText handler which updates textLength the following way:
export class RequestScene extends Component {
constructor() {
super();
this.maxLength = 100;
this.state={
textLength: 0,
};
}
onChangeText(text){
this.setState({
textLength: this.maxLength - text.length
});
}
render(){
return (
<View style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}}>
<View style={{
height: 200
}}>
<TextInput style={{
height: 200,
width: 360,
borderColor: 'lightgray',
borderWidth: 1,
padding: 3,
borderRadius: 3,
fontSize: 24}}
maxLength={100}
placeholder='무슨 상황인지 간단하게 써주세요'
multiline={true}
onChangeText={this.onChangeText.bind(this)}
/>
<Text style={{
fontSize:10,
color:'lightgrey',
textAlign: 'right'
}}>
// currently this counter only shows '0/100'
{this.state.textLength}/100
</Text>
</View>
</View>
);
}
}

To get length of input field try
handleEmailOtp = emailOtpString => {
this.setState({emailOtp: emailOtpString});
const otpLength = emailOtpString.length.toString(); //here
otpLength == 6
? this.setState({otpTyped: true})
: this.setState({otpTyped: false});
};

import React, { Component } from 'react'
import { View, Text, TextInput } from 'react-native'
export default class App extends Component {
state = {
text: ''
}
render() {
return (
<View style={{ padding: 10 }}>
<TextInput style={{ borderWidth: 1 }} numberOfLines={3} maxLength={100} multiline={true} onChangeText={(text) => this.setState({ text: text })} />
<Text>{100 - this.state.text.length}/100 Characters</Text>
</View>
)
}
}

Related

I am trying to implement text change and edit ends in TextInput using react-native but it's not quite working. Can any one help me?

I am trying to implement text change and edit ends in TextInput using react-native but it's not quite working.
See the Screenshot Here
Currently, when changing the price by touch input, the price is not affected when click off.
Here are my files
CartItem.js:
import React from "react";
import {
View,
TextInput,
Image,
TouchableOpacity,
StyleSheet,
Platform,
Alert,
} from "react-native";
//Colors
import Colors from "../../../utils/Colors";
//NumberFormat
import NumberFormat from "../../../components/UI/NumberFormat";
//Icon
import { MaterialCommunityIcons } from "#expo/vector-icons";
import CustomText from "../../../components/UI/CustomText";
//PropTypes check
import PropTypes from "prop-types";
export class CartItem extends React.PureComponent {
render() {
const { item, onAdd, onDes, onRemove } = this.props;
const AddItemHandler = async () => {
await onAdd();
};
const sum = +item.item.price * +item.quantity;
const checkDesQuantity = async () => {
if (item.quantity == 1) {
Alert.alert(
"Clear cart",
"Are you sure you want to remove the product from the cart?",
[
{
text: "Cancel",
},
{
text: "Yes",
onPress: onRemove,
},
]
);
} else {
await onDes();
}
};
return (
<View style={styles.container}>
<View style={styles.left}>
<Image
style={{
width: "100%",
height: 90,
resizeMode: "stretch",
borderRadius: 5,
}}
source={{ uri: item.item.thumb }}
/>
</View>
<View style={styles.right}>
<View
style={{ flexDirection: "row", justifyContent: "space-between" }}
>
<CustomText style={styles.title}>{item.item.filename}</CustomText>
<View>
<TouchableOpacity onPress={onRemove}>
<MaterialCommunityIcons name='close' size={20} color='#000' />
</TouchableOpacity>
</View>
</View>
<CustomText style={{ color: Colors.grey, fontSize: 12 }}>
Provided by Brinique Livestock LTD
</CustomText>
<NumberFormat price={sum.toString()} />
<View style={styles.box}>
<TouchableOpacity onPress={checkDesQuantity} style={styles.boxMin}>
<MaterialCommunityIcons name='minus' size={16} />
</TouchableOpacity>
Code that I would like to be fixed starts here.
<View>
<TextInput
keyboardType='numeric'
onEndEditing={AddItemHandler}
style={styles.boxText}>{item.quantity}</TextInput>
</View>
Code that I would like to be fixed ends here.
<TouchableOpacity
onPress={AddItemHandler}
style={styles.boxMin}>
<MaterialCommunityIcons name='plus' size={16} />
</TouchableOpacity>
</View>
</View>
</View>
);
}
}
CartItem.propTypes = {
item: PropTypes.object.isRequired,
onAdd: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired,
onDes: PropTypes.func.isRequired,
};
const styles = StyleSheet.create({
container: {
flex: 1,
marginHorizontal: 10,
height: 110,
borderBottomWidth: 1,
borderBottomColor: Colors.light_grey,
flexDirection: "row",
paddingVertical: 10,
alignItems: "center",
backgroundColor: "#fff",
paddingHorizontal: 10,
borderRadius: 5,
marginTop: 5,
},
left: {
width: "35%",
height: "100%",
alignItems: "center",
},
right: {
width: "65%",
paddingLeft: 15,
height: 90,
// overflow: "hidden",
},
title: {
fontSize: 14,
},
box: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
height: Platform.OS === "ios" ? 30 : 25,
backgroundColor: Colors.grey,
width: 130,
borderRadius: 5,
paddingHorizontal: 15,
marginTop: 5,
},
boxMin: {
width: "30%",
alignItems: "center",
},
boxText: {
fontSize: 16,
backgroundColor: Colors.white,
padding: 5,
},
});
Use onBlur instead of onEndEditing.
How should the input end triggered?
After a time?
When user hits enter?
When user taps anywhere to close software keyboard?
Instead of
onEndEditing={AddItemHandler}
Use:
onBlur={(e) => {AddItemHandler(e.nativeEvent.text)}}
And ensure that AddItemHandler can handle the value in e.nativeEvent.text.

React Native My delete button does not work

My code seems to be good but I do not understand why it is just doing nothing when I hit the "X" look at the code below please and help me out!
import React, { useState } from 'react';enter code here
import { StyleSheet, FlatList, Text, View, Image, TextInput, Button, Keyboard, TouchableOpacity, CheckBox } from 'react-native';
import Interactable from 'react-native-interactable';
export default function App() {
const [enteredGoal, setEnteredGoal] = useState('');
const [courseGoals, setCourseGoals] = useState([]);
const goalInputHandler = (enteredText) => {
setEnteredGoal(enteredText);
};
const addGoalHandler = () => {
if (enteredGoal.length > 0) {
setCourseGoals(currentGoals => [...currentGoals, enteredGoal])
} else {
alert("You have to write something!")
}
}
const deleteItem = idx => {
const clonedGoals = [...courseGoals]
courseGoals.splice(idx, 1)
setCourseGoals(courseGoals)
}
return (
<View style={styles.container}>
<View style={styles.topPart}></View>
<View style={styles.navBar}>
<Image source={require('./assets/baseline_menu_black_18dp.png/')} />
<Text style={styles.heading}> Grocery List </Text>
</View>
<View style={styles.body}>
<TextInput
style={styles.textInput}
placeholder='Groceries'
maxLength={20}
onBlur={Keyboard.dismiss}
value={enteredGoal}
onChangeText={goalInputHandler}
/>
<View style={styles.inputContainer}>
<TouchableOpacity style={styles.saveButton}>
<Button title="ADD" onPress={addGoalHandler} color="#FFFFFF" style={styles.saveButtonText} />
</TouchableOpacity>
</View>
<View style={styles.container}>
<FlatList
data={courseGoals}
renderItem={(itemData, idx) => (
<View style={styles.groceryItem} >
<Text style={styles.groceryItemText}>{itemData.item}</Text>
<Text style={styles.groceryItemDelete} onPress={() => deleteItem(idx)}>X</Text>
</View>
)}
/>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
topPart: {
height: '3%',
backgroundColor: '#5498A7',
},
navBar: {
height: '10%',
backgroundColor: '#5498A7',
elevation: 3,
paddingHorizontal: 15,
flexDirection: 'row',
alignItems: 'center',
},
body: {
backgroundColor: '#edebe9',
height: '100%',
flex: 1
},
heading: {
fontWeight: "bold",
justifyContent: 'center',
paddingLeft: '13%',
fontSize: 25,
color: '#d6d4d3'
},
textInput: {
borderColor: '#CCCCCC',
borderTopWidth: 1,
borderBottomWidth: 1,
height: 50,
fontSize: 25,
paddingLeft: 20,
paddingRight: 20
},
saveButton: {
borderWidth: 1,
borderColor: '#5498A7',
backgroundColor: '#5498A7',
padding: 15,
margin: 5,
},
saveButtonText: {
color: '#FFFFFF',
fontSize: 20,
textAlign: 'center'
},
groceryItem: {
borderWidth: 1,
borderColor: 'black',
backgroundColor: '#6A686B',
padding: 15,
margin: 5,
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between'
},
groceryItemText: {
color: '#d6d4d3',
},
groceryItemDelete: {
color: 'red',
fontWeight: 'bold',
fontSize: 20
}
});
I am just a beginner and am trying to figure out how to make this work better, if you have an idea please say how to fix it I would really appreciate it. This is a project I am doing just to get started with the learning process but this has been taking a bit longer than expected. Thank you!
I guess you have to change deleteItem function as following.
const deleteItem = idx => {
const clonedGoals = [...courseGoals]
clonedGoals.splice(idx, 1)
setCourseGoals(clonedGoals)
}
You have to replace courseGoals with clonedGoals
You can use ES2015 to achieve it in fewer lines, update your delete function like this:
const deleteItem = element => {
const clonedGoals = courseGoals.filter((item) => item !== element);
setCourseGoals(clonedGoals);
}
also update your onPress instead of passing index pass the item itself like this
onPress={() => deleteItem(itemData.item)}

Component cannot read JSON properties

I am trying to figure out why my components are not rendering without errors. I am trying to parse a JSON file that has been saved into a redux store but whenever I call try to reference the redux state through props such as this.props.response.data.specificJSONObject. I will instead get an error that states cannot read property of 'specificJSONObject' of undefined. This problem has plagued all of my components that need the use of the JSON file in the redux store. I initially found that my redux store was not updating(asked question: Redux state not being updated after dispatched actions) when making dispatches but have fixed that and now my redux store is able to store the fetched data it needs but I still get the same error as before.
This is my Summary.js screen for Minimal reproducible code
import React, { Component } from "react";
import {
View,
ImageBackground,
StyleSheet,
Image,
Text,
FlatList
} from "react-native";
import { connect } from "react-redux";
import {
fetchingSuccess,
fetchingRequest,
fetchingFailure,
fetchData
} from "../data/redux/actions/appActions.js";
import PropTypes from "prop-types";
import LinkedName from "./LinkedName";
import ArticleBox from "./ArticleBox";
const styles = StyleSheet.create({
container: {
backgroundColor: "dimgrey",
flex: 1
},
posLeft: {
alignItems: "center",
position: "absolute",
top: 40,
left: 25
},
posRight: {
alignItems: "center",
position: "absolute",
top: 40,
left: 175
},
text: {
textAlign: "center",
color: "#FFF"
},
header_text: {
textAlign: "center",
fontSize: 20,
color: "#FFF"
},
image: {
position: "absolute",
top: 200,
height: 375,
width: 375,
flex: 1
},
topPanel: {
backgroundColor: "rgba(0.0,0.0,0.0,0.9)",
height: 150,
width: 300,
position: "absolute",
alignSelf: "center",
flex: 2,
flexDirection: "column",
borderRadius: 25,
borderWidth: 4
},
midPanel: {
backgroundColor: "rgba(0.0,0.0,0.0,0.9)",
height: 100,
width: 300,
position: "absolute",
bottom: 120,
alignSelf: "center",
flex: 2,
flexDirection: "column",
borderRadius: 25,
borderWidth: 4
}
});
class Summary extends Component<Props> {
constructor(props) {
super(props);
this.state = {
taxonA: this.props.response.articles.taxon_a,
taxonB: this.props.response.articles.taxon_b,
sciNameA: this.props.response.articles.scientific_name_a,
sciNameB: this.props.response.articles.scientific_name_b,
hitRecords: Object.keys(this.props.response.articles.hit_records).map(
key => ({ key, ...this.props.response.articles.hit_records[key] })
),
TTOL:
Math.round(this.props.response.articles.sum_simple_mol_time * 10) / 10,
median: Math.round(this.props.response.articles.sum_median_time * 10) / 10
};
// console.log(STORE, this.props.response.articles);
}
componentDidMount = () => {
console.log("STATE", this.state);
};
render() {
return (
<View style={styles.container}>
<ImageBackground
source={require("../assets/images/timescale.png")}
style={styles.image}
resizeMode="contain"
alignSelf="center"
>
<View style={styles.topPanel}>
<Text style={styles.header_text}>Query Taxa</Text>
<View style={styles.posLeft}>
<Text
style={{ textAlign: "center", color: "#FFF", fontSize: 17 }}
>
Taxon A
</Text>
<Text />
<Text style={styles.text}>{this.state.taxonA}</Text>
<Text />
<LinkedName
url={this.state.hitRecords[0].link_taxon_a}
latinName={this.state.sciNameA}
/>
</View>
<View style={styles.posRight}>
<Text
style={{ textAlign: "center", color: "#FFF", fontSize: 17 }}
>
Taxon B
</Text>
<Text />
<Text style={styles.text}>{this.state.taxonB}</Text>
<Text />
<LinkedName
url={this.state.hitRecords[0].link_taxon_b}
latinName={this.state.sciNameB}
/>
</View>
</View>
<View style={styles.midPanel}>
<Text style={styles.header_text}>Result</Text>
<Text />
<Text style={styles.text}>TTOL: {this.state.TTOL} MYA</Text>
<Text />
<Text style={styles.text}>median: {this.state.median} MYA</Text>
</View>
<View style={{ position: "absolute", bottom: -35, marginLeft: -5 }}>
<FlatList
horizontal
data={this.state.hitRecords}
renderItem={({ item }) => {
return (
<ArticleBox
title={item.title}
year={item.year}
time={item.time}
author={item.author}
/>
);
}}
itemSeparatorComponent={() => (
<View
style={{
backgroundColor: "#ff8c00",
height: 100,
width: 100
}}
/>
)}
/>
</View>
</ImageBackground>
</View>
);
}
}
Summary.propTypes = {
fetchData: PropTypes.func.isRequired,
response: PropTypes.object.isRequired
};
const mapStateToProps = state => {
return { response: state };
};
const mapStateToDispatch = dispatch => ({
fetchData: url => dispatch(fetchData(url))
});
export default connect(
mapStateToProps,
mapStateToDispatch
)(Summary);
This is the function where I reference the Redux state through props
constructor(props) {
super(props);
this.state = {
taxonA: this.props.response.articles.taxon_a,
taxonB: this.props.response.articles.taxon_b,
sciNameA: this.props.response.articles.scientific_name_a,
sciNameB: this.props.response.articles.scientific_name_b,
hitRecords: Object.keys(this.props.response.articles.hit_records).map(
key => ({ key, ...this.props.response.articles.hit_records[key] })
),
TTOL:
Math.round(this.props.response.articles.sum_simple_mol_time * 10) / 10,
median: Math.round(this.props.response.articles.sum_median_time * 10) / 10
};
// console.log(STORE, this.props.response.articles);
}
In this Instance, I built it right when the state was built but in other components, I will set the state through the componentWillMount function and the problem will still persist.
My expected output should be no errors and my components render properly. The received output is the red screen will the error message cannot read property of 'specificJSONObject' of undefined.
The Answer in the question linked up top helps to solve this problem, but will explained here as well.
In my Summary Screen, the problem is being mapped to response. If you see the MapStatetoProps functions is written like this
const mapStateToProps = state => {
return { response: state };
};
but should be written like this
const mapStateToProps = state => {
return { response: state.fetchingStatus };
};
due to how my Redux store is set up and dispatches(can be seen in the question linked above)

How to implement time picker functionality in react native

I want to implement a functionality that should allow me to pick time within a range.
it should be like this
I have used react-native-slider and my code is here
import React, { Component } from 'react';
import { View, Text, Image, TextInput, StyleSheet } from 'react-native';
import Slider from 'react-native-slider';
import colors from '../styles/colors';
import Strings from '../localization/strings';
class FindDoctor extends Component {
constructor(props) {
super(props);
this.state = {
value: 6,
};
}
getInitialState() {
return {
value: 2,
};
}
render() {
return (
<View style={styles.container}>
<View style={{ flexDirection: 'column', margin: 20 }}>
<Text style={styles.textStyle}>Looking for</Text>
<View style={styles.input}>
<TextInput
placeholder={Strings.InternalMedicine}
autoCorrect={this.props.autoCorrect}
autoCapitalize={this.props.autoCapitalize}
returnKeyType={this.props.returnKeyType}
placeholderTextColor={colors.dimgray}
underlineColorAndroid="transparent"
onChangeText={TextInputName => this.setState({ TextInputName })}
/>
</View>
</View>
<View style={{ flexDirection: 'column', margin: 20 }}>
<Text style={styles.textStyle}>Near</Text>
<View style={styles.input}>
<TextInput
placeholder={Strings.Place}
autoCorrect={this.props.autoCorrect}
autoCapitalize={this.props.autoCapitalize}
returnKeyType={this.props.returnKeyType}
placeholderTextColor={colors.dimgray}
underlineColorAndroid="transparent"
onChangeText={TextInputName => this.setState({ TextInputName })}
/>
</View>
</View>
<View style={{ flexDirection: 'column', margin: 20 }}>
<Text style={styles.textStyle}>Within</Text>
<View style={styles.sliderStyles}>
<Slider
value={this.state.value}
onValueChange={(value) => this.setState({ value })}
minimumValue={4}
maximumValue={10}
step={10}
/>
<Text>value: {this.state.value}</Text>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
textStyle: {
fontSize: 20,
color: colors.brandBlue,
},
input: {
backgroundColor: colors.white,
height: 40,
width: '80%',
marginTop: 5,
borderRadius: 4,
alignItems: 'flex-start',
justifyContent: 'flex-start',
borderWidth: 1,
borderColor: colors.light_gray,
fontSize: 20
},
sliderStyles: {
//margin: 10,
alignItems: 'stretch',
justifyContent: 'center',
},
});
export default FindDoctor;
and now the output look like this
But i want to add multiples values in slider like the image shown above
can anyone help me to add multiple values in slider

Place a view relative to other view in React Native

I try to create something like this:
As you see, the text should be placed relative to the marker representing the current day (centered above). When you're at the first few and the last few days, it should stay inside the frame rather than being centered above the tall marker.
How do I achieve this?
This is my current code:
Component
export default class DateLine extends Component {
render() {
const dots = (<View style={Styles.dotsContainer} >
{[...Array(DaysInCurrentMonth())].map((x, i) =>
<View key={i.toString()} style={[Styles.dot, (CurrentDayInMonth() === (i + 1) ? Styles.tallDot : null), (CurrentDayInMonth() < (i + 1) ? Styles.grayDot : null)]} />
)}
</View>);
return (
<View style={Styles.container}>
<Text style={Styles.text}>{GetDateWithMonthAsText(Date()).toUpperCase()}</Text>
{dots}
</View>
);
}
}
Style:
export default StyleSheet.create({
container: {
alignSelf: 'stretch',
marginHorizontal: 15
},
dotsContainer: {
height: 10,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'flex-end'
},
dot: {
backgroundColor: Colors.darkBlueGrey,
height: 5,
width: 5,
borderRadius: 2.5
},
tallDot: {
height: 10
},
grayDot: {
backgroundColor: Colors.pinkishGreyTwo
},
text: {
fontWeight: '600'
}
});
Not a perfect but fast solution that you can easily improve:
export default class DateLine extends Component {
_renderDay (x, i) {
let currentDay = CurrentDayInMonth();
if(currentDay > (i + 1)) {
return <PassedDay />;
}
if(currentDay === (i + 1)) {
return <ActiveDate dateTitle={ GetDateWithMonthAsText(Date()).toUpperCase() } />;
}
return <InactiveDay />;
}
render() {
return (
<View style={ Styles.container }>
<View style={ Styles.dotsContainer } >
{[...Array(DaysInCurrentMonth())].map(this._renderDay)}
</View>
</View>
);
}
}
export function PassedDay() {
return (<View>
<View style={{backgroundColor: '#1f1749', width: 5, height: 5, borderRadius: 5, marginHorizontal: 2.5,}} />
</View>);
}
export function ActiveDate(dateTitle) {
return (<View style={{position: 'relative'}}>
<View style={{position: 'absolute', minWidth: 70, bottom: 15, left: 0, right: 0,}}>
<Text>
{dateTitle}
</Text>
</View>
<View style={{backgroundColor: '#1f1749', width: 5, height: 10, borderRadius: 5, marginHorizontal: 2.5,}} />
</View>);
}
export function InactiveDay() {
return (<View>
<View style={{backgroundColor: 'gray', width: 5, height: 5, borderRadius: 5, marginHorizontal: 2.5,}} />
</View>);
}
The point is: you can manipulate your 'DOM' via react-native's positions in same manner as html.
Hope, it will help.