I am having an issue with React Native's this.setState() within a TextInput's onChangeText. I am trying to display the content of the TextInput in the Text tag below it. However, it displays nothing -- the setState() call never changes this.state.searchtext. I also get no errors. Thank you in advance for your help! Here is my code:
export default class ShowScreen extends Component {
constructor(props) {
super(props);
this.state = {
searchtext: ""
};
}
render() {
var thisscreen = (
<View>
<ScrollView
horizontal={true}
showsHorizontalScrollIndicator={false}
pagingEnabled={true}
>
<View
style={{
flex: 1,
height: totalheight,
justifyContent: "space-around",
alignItems: "center",
width: totalwidth,
backgroundColor: "#FF0000"
}}
>
<TextInput
style={{ height: 80, fontSize: 20 }}
placeholder="placeholder"
value={this.state.searchtext}
onChangeText={searchtext =>
this.setState({ searchtext })
}
ref={input => {
this.textInput = input;
}}
returnKeyType="go"
/>
<Text>{this.state.searchtext}</Text>
</View>
</ScrollView>
</View>
);
return thisscreen;
}
}
In your TextInput add value prop
<TextInput
style={{height: 80, fontSize: 20}}
placeholder="placeholder"
value={this.state.searchtext}
onChangeText={(searchtext) => this.setState({ searchtext })}
ref={input => { this.textInput = input }}
returnKeyType="go"
/>
Hey you have used a variable to store screen code which is thisscreen. This might be preventing it from updating state.
Your render function should be like this:
render () {
return (
<View>
<ScrollView
horizontal={true}
showsHorizontalScrollIndicator={false}
pagingEnabled={true}
>
<View style={{
flex: 1,
height: totalheight,
justifyContent: "space-around",
alignItems: "center",
width: totalwidth,
backgroundColor: "#FF0000"
}}>
<TextInput
style={{height: 80, fontSize: 20}}
placeholder="placeholder"
value={this.state.searchtext}
onChangeText={(searchtext) =>
this.setState({searchtext})}
ref={input => { this.textInput = input }}
returnKeyType="go"
/>
<Text>{this.state.searchtext}</Text>
</View>
</ScrollView>
</View>);
}
onChangeText={(search_text) => this.setState({searchtext:search_text})}
try this, that might do job.
When you use setState, we provide a JSON as parameter. Please follow below code.
<TextInput
style={{height: 80, fontSize: 20}}
placeholder="placeholder"
value={this.state.searchtext}
onChangeText={(searchtext) => this.setState({ searchtext: searchtext })} // <--
ref={input => { this.textInput = input }}
returnKeyType="go"
/>
Do let me know if it does not work.
I think there might bug in React onChangeText method. You just need to replace onChangetText to onChange then It will work fine.
<TextInput
style={{height: 80, fontSize: 20}}
placeholder="placeholder"
onChange={(text) => this.setState({searchtext : text})}
ref={input => { this.textInput = input }}
returnKeyType="go"
/>
Related
I have list of product and everyone have two inputs I want to use formik to validate and collected all the filled data in those inputs but I'm facing a problem with onChange of hook useField() after submitting the data an error pops up
the error:
ERROR Warning: This synthetic event is reused for performance
reasons. If you're seeing this, you're accessing the property
defaultPrevented on a released/nullified synthetic event. This is
set to null. If you must keep the original synthetic event around, use
event.persist(). See https://reactjs.org/link/event-pooling for more
information.
screenshot:
the code:
<Formik
initialValues={{
product: ProductListDB.map((pro: any) => {
return (
{
packingQuantity: '',
unitQuantity: ''
}
)
})
}}
onSubmit={(values) => {
console.log("values: ", values)
}}
>
{({
handleChange,
handleBlur,
handleSubmit,
touched,
values,
errors,
isValid,
dirty,
}) => (
<>
<FieldArray name="product">
{({ insert, remove, push }) => (
<>
<FlatList
data={values.product}
renderItem={({ item, index }) => (
<ProductItemComponent key={index} values={item} product={ProductListDB[index]} index={index}/>
)}
keyExtractor={(item, index) => index.toString()}
// refreshControl={
// <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
// }
/>
</>
)}
</FieldArray>
<Button
style={styles.buttonStyle}
disabled={!isValid || !dirty || loading}
onPress={handleSubmit}
accessoryLeft={loading ?
<View style={{ justifyContent: 'center', alignItems: 'center' }}><Spinner size='small' status='info' /></View>
: IconComponent((I18nManager.isRTL ? evaIcons.frontArrow : evaIcons.backArrow), Colors.white500)
}
>
{(props) => (
<Text
{...props}
style={{
fontFamily: I18nManager.isRTL ? 'almarai-bold' : 'montserrat-bold',
color: Colors.white500,
fontSize: 17,
marginRight: 35,
}}
>
{t('unloadScreen:submit')}
</Text>
)}
</Button>
</>
)}
</Formik>
ProductItemComponent:
const ProductItem: FC<Props> = ({ product, values, index }: Props): ReactElement => {
const [packingQuantity, PQMeta, PQhelpers] = useField({ name: `product[${index}].packingQuantity` })
const [unitQuantity, UQMeta, UQhelpers] = useField({ name: `product[${index}].unitQuantity` })
const { setFieldValue } = useFormikContext();
const validateValue = (v: string): number => {
if (isNaN(parseInt(v))) {
return (0);
} else {
return (parseInt(v))
}
};
return (
<View
style={styles.container}
>
<View style={{ flex: 2, flexDirection: 'row', justifyContent: 'space-around', alignItems: 'center' }}>
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<CachedImage
source={{
uri: `${env.apiUrl.concat(env.productImagePngById.replace(":productId", product.id)).concat("?thumbnail=true")}`, // (required) -- URI of the image to be cached
expiresIn: 43200, // 12 hours in seconds (optional), if not set -- will never expire and will be managed by the OS
}}
cacheKey={`${product.id}-thumbnail`} // (required) -- key to store image locally
placeholderContent={( // (optional) -- shows while the image is loading
<Image
source={{
uri: productDefaultImage
}}
resizeMode="contain"
style={{ width: 100, height: 70 }}
/>
)}
resizeMode="contain" // pass-through to <Image /> tag
style={{ width: 100, height: 70 }}
/>
</View>
<View style={{ flex: 3, justifyContent: 'center' }}>
<Text style={styles.nameStyleText}>{product.name}</Text>
<Text style={styles.nameStyleText}>{product.reference}</Text>
</View>
</View>
<View style={{ flexDirection: 'row' }}>
<Input
style={{ alignItems: 'center', marginRight: 10 }}
value={packingQuantity.value}
onChange={(value) => {
setFieldValue(packingQuantity.name, value)
}}
// onChangeText={handleChange("amount")}
keyboardType="numeric"
/>
<Input
style={{ alignItems: 'center' }}
value={unitQuantity.value}
onChange={(value) => {
setFieldValue(unitQuantity.name, value)
}}
// onChangeText={(value) => setUnitQuantity(validateValue(value))}
keyboardType="numeric"
/>
</View>
</View>
);
}
I have encountered a weird issue in the newest react native where the value in the text input in a component remains when a tab is switched.
I can't figure what is going on and I thought it should re-render when tab is changed but it doesn't.
Here's my code
app.js
export default function App() {
const [tab, setTab] = useState("TAB1")
return (
<View style={styles.container}>
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity
style={{ borderRadius: 5, borderWidth: 1, marginRight: 5, padding: 20 }}
onPress={() => setTab("TAB1")}
>
<Text>Tab 1</Text>
</TouchableOpacity>
<TouchableOpacity
style={{ borderRadius: 5, borderWidth: 1, padding: 20}}
onPress={() => setTab("TAB2")}
>
<Text>Tab 2</Text>
</TouchableOpacity>
</View>
<View style={{ marginTop: 20}}>
{
tab === "TAB1" ? (
<View>
<InputComponent tab={tab} />
</View>
) : (
<View>
<InputComponent tab={tab} />
</View>
)
}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
marginTop: 100,
padding: 10
},
});
inputcomponent.js
export function InputComponent({ tab }) {
const [value, setValue] = useState("");
return (
<View>
<Text>{tab}</Text>
<TextInput placeholder="INPUT HERE" value={value} onChangeText={setValue}/>
</View>
)
}
It seems like the input component re-renders but the text input within it doesn't change.
Demo Issue
This is such a good question. This is because we are importing it once and passing it to two different components. It changes the tab but uses the same textinput state because they are using the same key.
To fix this pass in the key prop so React knows that tab changed:
{
tab === "TAB1" ? (
<View>
<InputComponent key={1} tab={tab} />
</View>
) : (
<View>
<InputComponent key={2} tab={tab} />
</View>
)
}
Snack: https://snack.expo.io/mVVLb9uId
Read about keys: https://reactjs.org/docs/lists-and-keys.html#keys
Have been building a small Todo App on React Native based on a tutorial, I want to add an error message to the app if the user tries to save a list without any characters.
I have created the message, the state and a conditional for the state that is linked into a View containing the message. Although now, the user cannot save a list at all and the error message is constantly visible.
export default class AddTodoModal extends React.Component {
backgroundColors = ['#171219', '#225560', 'grey', '#EDF060', '#F0803C', '#310D20']
state = {
name: '',
color: this.backgroundColors[0],
errorState: false
};
createTodo = () => {
const {name, color} = this.state
if ({name}.length > 0) {
tempData.push({
name,
color,
todos: []
})
}
return
this.setState({name: ""})
this.props.closeModal();
}
renderColors() {
return this.backgroundColors.map(color => {
return (
<TouchableOpacity
key={color}
style={[styles.colorSelect, {backgroundColor: color}]}
onPress={() => this.setState({color})}
/>
);
});
}
render() {
return(
<KeyboardAvoidingView style={styles.container} behavior="padding">
<TouchableOpacity style={{position: 'absolute', top: 64, right: 32}} onPress={() => this.props.closeModal()}>
<AntDesign name="close" size={24} color={colours.blue} />
</TouchableOpacity>
<View visible={this.state.errorState}>
<Text style={styles.errorMessage}>Please Enter a Value!</Text>
</View>
<View style={{alignSelf: 'stretch', marginHorizontal: 32}}>
<Text style={styles.title}>Create Todo List</Text>
<TextInput style={styles.input} placeholder="List Name?" onChangeText={text => this.setState({ name: text })}/>
<View style={{flexDirection: 'row', justifyContent: 'space-between', marginTop: 12 }}>{this.renderColors()}</View>
<TouchableOpacity style={[styles.createList, { backgroundColor: this.state.color}]}
onPress={this.createTodo}>
<Text style={styles.buttonText}>Create List</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
}
}
Update:
With the answer below, now error message appears but saving a list is blocked, even with a valid input.
export default class AddTodoModal extends React.Component {
backgroundColors = ['#171219', '#225560', 'grey', '#EDF060', '#F0803C', '#310D20']
state = {
name: '',
color: this.backgroundColors[0],
errorState: false
};
createTodo = () => {
const {name, color} = this.state
if ({name}.length > 0) {
tempData.push({
name,
color,
todos: []
})
this.setState({name: ""})
this.setState({errorState: false})
this.props.closeModal();
} else {
({name}.length = 0)
this.setState({errorState: true})
return
}
}
renderColors() {
return this.backgroundColors.map(color => {
return (
<TouchableOpacity
key={color}
style={[styles.colorSelect, {backgroundColor: color}]}
onPress={() => this.setState({color})}
/>
);
});
}
render() {
return(
<KeyboardAvoidingView style={styles.container} behavior="padding">
<TouchableOpacity style={{position: 'absolute', top: 64, right: 32}} onPress={() => this.props.closeModal()}>
<AntDesign name="close" size={24} color={colours.blue} />
</TouchableOpacity>
{this.state.errorState && <View>
<Text style={styles.errorMessage}>Please Enter a Value!</Text>
</View>}
<View style={{alignSelf: 'stretch', marginHorizontal: 32}}>
<Text style={styles.title}>Create Todo List</Text>
<TextInput style={styles.input} placeholder="List Name?" onChangeText={text => this.setState({ name: text })}/>
<View style={{flexDirection: 'row', justifyContent: 'space-between', marginTop: 12 }}>{this.renderColors()}</View>
<TouchableOpacity style={[styles.createList, { backgroundColor: this.state.color}]}
onPress={this.createTodo}>
<Text style={styles.buttonText}>Create List</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
}
}
Update:
Fixing if statement for correct display of error message
createTodo = () => {
const {name, color} = this.state
let nameLength = this.state.name.length;
if (nameLength > 0) {
this.setState({errorState: false})
tempData.push({
name,
color,
todos: []
})
this.setState({name: ""})
this.props.closeModal();
} else {
(nameLength === 0)
this.setState({errorState: true})
}
}
You can use condtional rendering instead of visible property.
export default class AddTodoModal extends React.Component {
backgroundColors = ['#171219', '#225560', 'grey', '#EDF060', '#F0803C', '#310D20']
state = {
name: '',
color: this.backgroundColors[0],
errorState: false
};
createTodo = () => {
const {name, color} = this.state
if ({name}.length > 0) {
tempData.push({
name,
color,
todos: []
})
}
return
this.setState({name: ""})
this.props.closeModal();
}
renderColors() {
return this.backgroundColors.map(color => {
return (
<TouchableOpacity
key={color}
style={[styles.colorSelect, {backgroundColor: color}]}
onPress={() => this.setState({color})}
/>
);
});
}
render() {
return(
<KeyboardAvoidingView style={styles.container} behavior="padding">
<TouchableOpacity style={{position: 'absolute', top: 64, right: 32}} onPress={() => this.props.closeModal()}>
<AntDesign name="close" size={24} color={colours.blue} />
</TouchableOpacity>
{this.state.errorState && <View>
<Text style={styles.errorMessage}>Please Enter a Value!</Text>
</View>}
<View style={{alignSelf: 'stretch', marginHorizontal: 32}}>
<Text style={styles.title}>Create Todo List</Text>
<TextInput style={styles.input} placeholder="List Name?" onChangeText={text => this.setState({ name: text })}/>
<View style={{flexDirection: 'row', justifyContent: 'space-between', marginTop: 12 }}>{this.renderColors()}</View>
<TouchableOpacity style={[styles.createList, { backgroundColor: this.state.color}]}
onPress={this.createTodo}>
<Text style={styles.buttonText}>Create List</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
}
}
you can implement like this
render() {
return(
<KeyboardAvoidingView style={styles.container} behavior="padding">
<TouchableOpacity style={{position: 'absolute', top: 64, right: 32}} onPress={() => this.props.closeModal()}>
<AntDesign name="close" size={24} color={colours.blue} />
</TouchableOpacity>
{this.state.errorState ? <View> <Text style={styles.errorMessage}>Please Enter a Value!</Text> </View> :null}
<View style={{alignSelf: 'stretch', marginHorizontal: 32}}>
<Text style={styles.title}>Create Todo List</Text>
<TextInput style={styles.input} placeholder="List Name?" onChangeText={text => this.setState({ name: text })}/>
<View style={{flexDirection: 'row', justifyContent: 'space-between', marginTop: 12 }}>{this.renderColors()}</View>
<TouchableOpacity style={[styles.createList, { backgroundColor: this.state.color}]}
onPress={this.createTodo}>
<Text style={styles.buttonText}>Create List</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
}
I have a component called MDropDowlList which render the following view :
MDropDownList.js
render() {
return (
<View>
<View
style={{
backgroundColor: '#fff',
position: 'absolute',
left: 0,
right: 0,
top: 0,
maxHeight: 200,
}}
>
{
this.props.searchable && (
<TextInput
style={{ zIndex: 198 }}
onChangeText={(text) => this.search(text)}
/>
)
}
<FlatList
data={this.state.data}
keyboardShouldPersistTaps="always"
nestedScrollEnabled={true}
contentContainerStyle={{ zIndex: 199 }}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item, index }) => (
<TouchableHighlight
key={index.toString()}
style={{
padding: 10,
backgroundColor: '#fff',
}}
underlayColor="#5897fb"
onPress={() => this.props.handleOnPress(item)}
>
<MText size={18}>{item}</MText>
</TouchableHighlight>
)}
/>
</View>
</View>
);
}
Then I have called this component to another component called MDropDown which render method is as below :
MDropDown.js
render() {
return (
<View style={{ marginVertical: 5, zIndex: 3}}>
...
{/* Another components */}
...
{
this.state.displayDropDown && (
<MDropDownList data={this.props.data} searchable={this.props.searchable} handleOnPress={(item) => this.handleOnPress(item)} />
)
}
</View>
);
}
Now finally I called my MDropDown component in my main screen as follow :
render() {
return (
<KeyboardAvoidingView style={{ flex: 1, backgroundColor: Colors.bgGray }} behavior="padding">
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={styles.container} keyboardShouldPersistTaps="always" nestedScrollEnabled={true}>
<View style={{ backgroundColor: '#fff', padding: 10 }}>
<MDropDown
label="Category"
placeholder="Select Category"
data={["test1", "test2", "test3", "test4", "test5"]}
onSelectItem={(selectedItem) => alert(selectedItem)}
searchable={true}
/>
</View>
</ScrollView>
</KeyboardAvoidingView>
)
}
But I am not able to access Flatlist item or TextInput of MDropDownList component. Even I am not able to focus TextInput available in MDropDownList.
Please help me what's going wrong here ???
Note : This issue is only on android, on ios it is working properly. On ios I am able to click flatlist item and focus TextInput as well as.
Tried using zIndex to the absolute view?
Add property of zIndex : 500 to the style.
I want to submit value from text input, but when I console.log, the text that inputed before is undefined. I've already follow the instruction from react native get TextInput value, but still undefined.
this is state that I've made before:
this.state = {
comment_value: '',
comments: props.comments
}
submit = (args) => {
this.props.submit(args.comment_value)
}
this is the the function for submitted text:
addComment () {
var comment_value = this.state.comment_value
console.log('success!', comment_value)
})
this.props.submit('410c8d94985511e7b308b870f44877c8', '', 'e18e4e557de511e7b664b870f44877c8')
}
and this is my textinput code:
<TextInput
underlineColorAndroid='transparent'
placeholder='Enter your comment here'
multiline numberOfLines={4}
onChangeText={(text) => this.setState({comment_value: text})}
value={this.state.comment_value}
style={styles.textinput}
/>
</View>
<TouchableOpacity onPress={this.addComment.bind(this)}>
<View style={{flex: 1, flexDirection: 'column', backgroundColor: Colors.background, width: 70}}>
<Icon name='direction' type='entypo' color='#000'size={30} style={styles.icon} />
</View>
</TouchableOpacity>
This should definately be working try cleaning up your code like this
contructor(props) {
super(props);
this.state = {
comment_value: '',
}
}
addComment () {
console.log(this.state.comment_value); // should be defined
this.props.submit(this.state.comment_value);
}
render() {
return (
<View>
<TextInput
underlineColorAndroid='transparent'
placeholder='Enter your comment here'
multiline numberOfLines={4}
value={this.state.comment_value}
onChangeText={(text) => this.setState({comment_value: text})}
style={styles.textinput}
/>
<TouchableOpacity onPress={this.addComment.bind(this)}>
<View style={{flex: 1, flexDirection: 'column', backgroundColor: Colors.background, width: 70}}>
<Icon name='direction' type='entypo' color='#000'size={30} style={styles.icon} />
</View>
</TouchableOpacity>
</View>
);
}
EDIT: Based on your complete code sample it seems like you're incorrectly trying to update state by doing multiple this.state = ... which is incorrect, to update state you have to use this.setState({ ... })