I'm trying to update a text input as it changes but it doesn't seem to work.
Here's my simplified example:
var Message = React.createClass({
getInitialState: function() {
return {textValue: ''};
},
render: function() {
return (
<View style={[styles.container]}>
<ProfilePicture userId={this.props.userId}/>
<TextInput
ref={component => this._textInput = component}
style={{flex: 8, paddingLeft: 5, fontSize: 15}}
onChangeText={this._handleChange}
//multiline={true}
defaultValue={""}
value={this.state.textValue}
returnKeyType={"send"}
blurOnSubmit={false}
autoFocus={true}
enablesReturnKeyAutomatically={true}
onSubmitEditing={this._onSubmit}
/>
</View>
);
},
_handleChange: function(text) {
this.setState({textValue: "foo"});
},
_onSubmit: function(event) {
this._clearText();
},
_clearText: function() {
this._textInput.setNativeProps({text: ''});
},
});
I'm expecting that as soon as someone enters in some text it gets automatically altered to read "foo" but this doesn't work.
Any ideas?
UPDATE
Plot thickens,
If I call the same function for onBlur it works but only when there is no text already in the text input. If I change the function to set the value using this._textInput.setNativeProps({text: 'foo'}); instead of this.setState({textValue: "foo"}); then it works both when the text input is empty and has data.
Example:
render: function() {
return (
<TextInput
ref={component => this._textInput = component}
style={{flex: 8, paddingLeft: 5, fontSize: 15}}
onChangeText={this._handleChange}
onBlur={this._handleChange}
//multiline={true}
defaultValue={""}
value={this.state.textValue}
returnKeyType={"send"}
blurOnSubmit={false}
autoFocus={true}
enablesReturnKeyAutomatically={true}
onSubmitEditing={this._onSubmit}
/>
);
},
_handleChange: function(text) {
// what to do here check if there are youtube videos?
this._textInput.setNativeProps({text: 'foo'});
}
So in the above the _handleChange works for onBlur but not for onChangeText. Weird right?
Not really an optimal solution but looking at the react native code for react-native v 0.17.0 it looks like any changes made to the component's value during onChange don't take affect.
The code has changed on HEAD and this could fix it. https://github.com/facebook/react-native/blob/master/Libraries/Components/TextInput/TextInput.js#L542
To get around this you can wrap the code to reset the text inputs value in a setTimeout like this
var self = this;
setTimeout(function() {self._textInput.setNativeProps({text: newText}); }, 1);
This creates a new change outside of the current change event.
Like I said not an optimal solution but it works.
There is another issue that the cursor position needs to be updated if the new text is larger than the old text, this isn't available on master yet but there is a PR that looks like it is close to being merged. https://github.com/facebook/react-native/pull/2668
You need to bind your onChangeText to this. Without that in the _handleChange function "this" does not refer to the component and thus setState is not going to work the way you expect it to.
<TextInput
ref={component => this._textInput = component}
style={{flex: 8, paddingLeft: 5, fontSize: 15}}
onChangeText={this._handleChange.bind(this)} // <-- Notice the .bind(this)
//multiline={true}
defaultValue={""}
value={this.state.textValue}
returnKeyType={"send"}
blurOnSubmit={false}
autoFocus={true}
enablesReturnKeyAutomatically={true}
onSubmitEditing={this._onSubmit}
/>
Related
Hello I am trying to navigate creating a text input on React-Native that lets the user enter their email password and name and stores it in the object. But it is not working how I expected and I am not sure where I have gone wrong. I have looked at similar projects online but most are done with React not react-natie and even still when i try their implementation it still doesnt fix the text not displaying as i type or allowing it to be store when the user presses the register button.
import { Text, View, TextInput, Button, StyleSheet} from 'react-native';
import {useState} from 'react';
export default function Home() {
const [input, setInput] = useState({
fullName:"",
email:"",
password: "",
});
const inputHandler = (e) => {
let name = e.target.fullName
let value = e.target.value;
setInput({
...input,
[name]: value,
});
};
const registerUser = (e) => {
e.preventDefault();
console.log(input)
setInput({
fullName:"",
email: "",
password:"",
});
};
return (
<View >
<Text style={styles.welcome}>
Welcome! Please Register Below
</Text>
<TextInput style={styles.input} placeholder="Name" value={input.fullName} onChangeText={inputHandler}/>
<TextInput style={styles.input} placeholder="Email" value={input.email} onChangeText={inputHandler}/>
<TextInput style={styles.input} placeholder="Password" value={input.password} onChangeText={inputHandler} />
<View style={styles.button}>
<Button title="Register" onPress={registerUser} />
</View>
<View style={styles.button}>
<Button title="Already Registered? Click here to login" onPress={()=> register()} />
</View>
</View>
);
}
const styles = StyleSheet.create({
welcome:{
padding:10
},
input: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
borderColor:'black',
borderWidth: 2,
margin:3
},
button:{
margin:5
}
});```
I have tried this.input.fullName in the value prop but that give me an error as that is not the way i defined it with my handlers. I also could go back to just using seperate states for each input like [name,setName] [password,setPassword] ect... but I would really like to understand where my knowledge is lacking for utilizing TextInput to pass user information and storing it.
From looking through similar stackoverflow questions I also tried changing my onChangeText to this
`onChangeText={(t) =>inputHandler(t)}`
but that also doesnt solve it
Thanks!
Text is not displayed, because you react on changes, but not change state properly. onTextChanges callback return to you only text (as string), not as Event. If you need track Event you can use onChange callback.
But, I recommend to you write your logic as clean and simple as possible.
Firstly, you should separate inputHandler to independent text changes functions, because it easily to manage for now in your case. For example:
const onNameChanges = (text) => {
setInput(prev => ({
...prev,
fullName: text
}))
}
And then just assign it to props of your TextInput
<TextInput style={styles.input} placeholder="Name" value={input.fullName} onChangeText={onNameChanges}/>
That's it. You code works as expected. I prepared working example for you here
https://snack.expo.dev/BkQi-kXr-8
const entryInput = forwardRef((props, ref) => {
return (
<View
style={{
fontFamily: "roboto-regular",
color: "rgba(255,0,0,0.6)",
fontSize: hp("1.5%")
}}>
<Text style={styles.text}>{props.show_err ? props.err : null}</Text>
<TextInput
ref={ref}
style={{
borderColor:
!props.err || props.err === "" || props.err === props.empty_err
? "gray"
: "rgba(255,0,0,0.6)",
backgroundColor: "rgba(213, 213, 213, 0.1)",
borderWidth: wp("0.3%"),
borderRadius: wp("1%"),
width: wp("85%"),
height: hp("5.2%"),
fontFamily: "roboto-regular",
fontSize: hp("2%"),
fontWeight: "normal"
}}
returnKeyType={props.last ? "done" : "next"}
blurOnSubmit={props.last ? true : false}
placeholderTextColor={"gray"}
paddingLeft={wp("2%")}
paddingRight={hp("2%")}
placeholder={props.placeholder}
onSubmitEditing={() => {
if (props.next_input) {
props.next_input.current.focus();
} else if (props.action) {
props.action();
}
}}
onChangeText={(text) => {
if (props.setText) props.setText(text);
if (props.validate) props.validate(text);
}}
/>
</View>
);});
New to react native... trying to create an input field for a password.
This custom component works great, but when I add the secureTextEntry={true} the font changes for no reason (it's not roboto-regular), it doesn't even change to the default font.
I noticed that when I remove the fontFamily key from the style object then save my code and the expo client reloads, then add fontFamily again and reload again the TextInput behaves as expected and the font is the one I set (roboto-regular), however the bug reappears when manually reloading the app.
The accepted answer will work on luck. The refs can be both defined or undefined when the component is being mounted.
Add the following to your component to properly solve the issue:
const _inputRef = useRef(null);
const setRef = useCallback((node) => {
if (_inputRef.current) {
// Make sure to cleanup any events/references added to the last instance
}
if (node) {
// Check if a node is actually passed. Otherwise node would be null.
// You can now do what you need to, setNativeProps, addEventListeners, measure, etc.
node.setNativeProps({
style: { fontFamily: "Quicksand-Medium" },
});
}
// Save a reference to the node
_inputRef.current = node;
}, []);
Make sure your TextInput has this ref assigned:
<TextInput ref={setRef} ... />
Adding the following to my custom component fixed the problem:
useEffect(() => {
if (ref) {
ref.current.setNativeProps({
style: { fontFamily: "roboto-regular" }
});
}
}, []);
I am using these tags in my react native app: https://github.com/peterp/react-native-tags
Being a react rookie I have a problem with SetState. I have set-up the tags like this:
<Tags
initialText=""
textInputProps={{
placeholder: "Servicable Postcodes"
}}
initialTags={["3121"]}
onChangeTags={(tags) => this.changeTags(tags)}
inputStyle={{ backgroundColor: "white", borderBottomWidth: 1 }}
renderTag={({ tag, index, onPress, deleteTagOnPress, readonly }) => (
<TouchableOpacity key={${tag}-${index}} onPress={onPress}>
<Text style={styles.tagStyle}>{tag}</Text>
</TouchableOpacity>
)}
/>
changeTags(newtags) {
//this.setState({ myTags: newtags.toString()});
var tagList = newtags.toString();
/*this.setState({myTags: tagList}, function () {
console.log(this.state.myTags);
});*/
}
Adding tags works like this [3121] [1111] [2222]
So that code works well. As soon as I uncomment the setState it will run everything like normal but the tag will not get added to the list. So if I add 1111 my console log will be 3121, 1111 (But 1111 will not show as as a tag) then if i try to add another it will be 3121, 2222 etc.
I think because it re-renders with setState the tag is never added/overwritten and I just keep getting left with the single 3121 tag. Or maybe i'm a react noob and have no idea.
Any help is appreciated.
Thanks
You are right in saying that each render will cause the Tag component to reset back to 3121.
To avoid this you need to store your current tags in your state like this:
export default class App extends React.Component {
state = { tags: ['3121'], myTags: "3121" }; // This line
render() {
return (
<View style={styles.container}>
<Tags
initialText=""
textInputProps={{
placeholder: 'Servicable Postcodes',
}}
initialTags={this.state.tags} // This line
onChangeTags={tags => this.changeTags(tags)}
inputStyle={{ backgroundColor: 'white', borderBottomWidth: 1 }}
renderTag={({ tag, index, onPress, deleteTagOnPress, readonly }) => (
<TouchableOpacity key={`${tag}-${index}`} onPress={onPress}>
<Text style={styles.tagStyle}>{tag}</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
changeTags(newtags) { // This function
this.setState({ tags: newtags, myTags: newtags.toString() }, () =>
console.log(this.state)
);
}
}
This means for each render (which will happen every time a tag is added as you are calling setState), the Tag component will render the tags you have stored in the state.
I have also included another member in your state called myTags, which maps to the string value of your tags as it seems like this was necessary from your previous code.
Here is a working snack
I'm using TextInput to allow only numbers using state, it works on android but not on iOS. Here's how I'm using state to allow only numbers.
handleInputChange = (text) => {
if (/^\d+$/.test(text) || text === '') {
this.setState({
text: text
});
}
}
my render method
render = () => {
return (
<View style={{
flex: 0,
flexDirection: 'row',
alignItems: 'flex-end',
marginTop: 50
}}>
<View style={{ flex: 0, marginLeft: 10 }}>
<Text style={{ fontSize: 20}}>$</Text>
</View>
<View style={{flex: 1,}}>
<TextInput
onChangeText={this.handleInputChange}
value={this.state.text}
underlineColorAndroid='transparent'
autoCorrect={false}
spellCheck={false}
style={{ paddingLeft: 5, fontSize: 20 }} />
</View>
</View>
);
}
This only works in Android, I guess because the state has changed react doesn't update the ui.
pls try this:
keyboardType='numeric' in the tag TextInput
when you prove don't put the numbers with the keyboard of your pc, pls use the keyboard of the emulator
if still not working put this textContentType='telephoneNumber'
As Ravi Rupareliya said this's a bug, which TextInput doesn't update, when the state text is shorter than the current TextInput value. Seems like the bug has been fixed in react-native 0.57.RC. For now I'm using the following fix.
handleInputChange = (text) => {
const filteredText = text.replace(/\D/gm, '');
if(filteredText !== text) {
// set state text to the current TextInput value, to trigger
// TextInput update.
this.setState({ text: text });
// buys us some time until the above setState finish execution
setTimeout(() => {
this.setState((previousState) => {
return {
...previousState,
text: previousState.text.replace(/\D/gm, '')
};
});
}, 0);
} else {
this.setState({ text: filteredText });
}
}
React native not provided keyboardType which remove punctuation from keyboard. You need to use regular expression with replace method to remove punctuation from text and set keyboardType = 'numeric'.
Regular Expression
/[- #*;,.<>{}[]/]/gi
Example code
onTextChanged(value) {
// code to remove non-numeric characters from text
this.setState({ number: value.replace(/[- #*;,.<>\{\}\[\]\\\/]/gi, '') });
}
Please check snack link
https://snack.expo.io/#vishal7008/1e004c
Worked for me:
<TextInput
...
textContentType='telephoneNumber'
dataDetectorTypes='phoneNumber'
keyboardType='phone-pad'
/>
While having input from user you can change the keyBoard type for that particular text input box like this i.e. numeric or alphabatic etc...
<TextInput>
//contains some code
keyboardType="Numeric"
</TextInput>
<TextInput>
keyboardType="number-pad"
</TextInput>
onChangeText={value => setuserPrimaryPhone(value.replace(/[^0-9]/g, ''))}
javascript simple method replace(/[^0-9]/g, ''))}
I want to directly update the value of a component due to performance reasons.
render(){
<View>
<Text style={styles.welcome} ref={component => this._text = component}>
Some Text
</Text>
<TouchableHighlight underlayColor='#88D4F5'
style={styles.button}>
<View>
<Text style={styles.buttonText}
onPress={this.useNativePropsToUpdate.bind(this)}>
Iam the Child
</Text>
</View>
</TouchableHighlight>
</View>
}
This is the method I use to update the text component. I dont know if I am setting the right attribute/ how to figure out which attribute to set:
useNativePropsToUpdate(){
this._text.setNativeProps({text: 'Updated using native props'});
}
Essentially trying to follow the same approach from this example:
https://rnplay.org/plays/pOI9bA
Edit:
When I attempt to explicitly assign the updated value:
this._text.props.children = "updated";
( I know this this the proper way of doing things in RN ). I get the error "Cannot assign to read only property 'children' of object'#'"
So maybe this is why it cant be updated in RN for some reason ?
Instead of attempting to change the content of <Text> component. I just replaced with <TextInput editable={false} defaultValue={this.state.initValue} /> and kept the rest of the code the same. If anyone know how you can change the value of <Text> using setNativeProps OR other method of direct manipulations. Post the answer and ill review and accept.
The text tag doesn't have a text prop, so
this._text.setNativeProps({ text: 'XXXX' })
doesn't work.
But the text tag has a style prop, so
this._text.setNativeProps({ style: { color: 'red' } })
works.
We can't use setNativeProps on the Text component, instead, we can workaround and achieve the same result by using TextInput in place of Text.
By putting pointerEvent='none' on the enclosing View we are disabling click and hence we can't edit the TextInput (You can also set editable={false} in TextInput to disbale editing)
Demo - Timer (Count changes after every 1 second)
import React, {Component} from 'react';
import {TextInput, StyleSheet, View} from 'react-native';
class Demo extends Component {
componentDidMount() {
let count = 0;
setInterval(() => {
count++;
if (this.ref) {
this.ref.setNativeProps({text: count.toString()});
}
}, 1000);
}
render() {
return (
<View style={styles.container} pointerEvents={'none'}>
<TextInput
ref={ref => (this.ref = ref)}
defaultValue={'0'}
// editable={false}
style={styles.textInput}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 0.7,
justifyContent: 'center',
alignItems: 'center',
},
textInput: {
fontSize: 60,
width: '50%',
borderColor: 'grey',
borderWidth: 1,
aspectRatio: 1,
borderRadius: 8,
padding: 5,
textAlign: 'center',
},
});
export default Demo;
As setNativeProps not solving the purpose to alter the content of <Text />, I have used below approach and is working good. Create Simple React Component like below...
var Txt = React.createClass({
getInitialState:function(){
return {text:this.props.children};
},setText:function(txt){
this.setState({text:txt});
}
,
render:function(){
return <Text {...this.props}>{this.state.text}</Text>
}
});