React Native TextInput ref always undefined - react-native

I have a simple TextInput that I want to put a reference on in my render:
<View>
<TextInput ref={(component) => this._inputElement = component}>Input</TextInput>
{console.log(this._inputElement)}
<Button
onPress={this.addAddress}
title="Submit"
color="#841584"
/>
</View>
I want to then use that ref in a function above that is bound in my contructor:
constructor(props) {
super(props);
this.state = {
addresses: []
};
this.addAddress = this.addAddress.bind(this);
}
addAddress function:
addAddress(event, result) {
console.log("reference:", this._inputElement.value);
}
The console log in both the render and addAddress are always undefined.
I have looked around but no one seems to be having my problem, usually they have a typo or didn't bind the function they then want to call.
Why do I seem unable to have references?

Using State
Usually the way to use TextInput is to store the value in state.
Remember to initialize the address in your state as an empty string, otherwise having a null value for address could cause an error.
constructor(props) {
super(props)
this.state = {
....
address: ''
}
}
Then you could define your text input as follows
<TextInput
onChangeText={address => this.setState({address})}
value={this.state.address}
/>
Then in your addAddress
addAddress(event, result) {
console.log("reference:", this.state.address);
}
Using Refs
Alternatively you could use ._lastNativeText to access it from the reference
<TextInput
ref={ref => { this._inputElement = ref }}>
Input
</TextInput>
then in your addAddress
addAddress(event, result) {
// I always check to make sure that the ref exists
if (this._inputElement) {
console.log("reference:", this._inputElement._lastNativeText);
}
}
I wouldn't recommend the second method as you are accessing private methods that could be liable to change in a future release.

Textinput self-encloses
<View>
<TextInput ref={ref=> (this._inputElement = ref)}/>
<Button
onPress={this.addAddress}
title="Submit"
color="#841584"
/>
</View>
addAddress(event, result) {
console.log("reference:", this._inputElement._lastNativeText); //this get's the value, otherwise it's undefined
}

This snippet works properly in react native and react native web:
const txtRef = useRef(null)
return(
<TextInput
ref={txtRef}
onChangeText={text => txtRef.current.value = text}
/>
<Button
title='log and reset'
onPress={() => {
console.log(txtRef.current.value)
txtRef.current.clear()
txtRef.current.value = ''
}}
/>
)
`

Related

Cannot display firestore info in react native view

I am trying ti display basic profile data from firestore on a profile page, but the information will not show up in the view. My code is below: I create a constructor with the state ethnicity
var db = firebase.firestore();
export default class Dashboard extends Component {
constructor() {
super();
this.state = {
displayName: firebase.auth().currentUser.displayName,
uid: firebase.auth().currentUser.uid,
ethnicity: ''
}
}
And I then update the state with componentDidMount() as shown below and then try to display it in view
componentDidMount() {
db.collection("profiles").doc(this.state.uid).get()
.then(doc => {
this.state.ethnicity= doc.data().ethnicity.toString();
console.log(this.state.ethnicity)
})
.catch(function(error) {
console.log("Error getting document:", error);
})
}
render() {
return (
<View style={styles.container}>
<Text style = {styles.textStyle}>
Mixee!
</Text>
<Text style = {styles.textStyle}>
Hello, {this.state.displayName}
</Text>
<Text style = {styles.textStyle}>
Profile:
</Text>
<Text style = {styles.textStyle}>
Ethnicity: {this.state.ethnicity}
</Text>
<Button
color="#3740FE"
title="Logout"
onPress={() => this.signOut()}
/>
</View>
);
}
The console.log(this.state.ethnicity) correctly prints out the ethnicity so I do not know why it isn't showing up. Any help appreciated thanks!
It looks like you are modifying state directly, with this.state.ethnicity = doc.data().ethnicity.toString(); This might not cause a re-render, so your component does not update with the new state. Instead, try:
this.setState({
ethnicity: doc.data().ethnicity.toString()
});
Calling setState will let React Native know that the state has changed, and that it should call render again, as explained here.

Get user input from input field in react similar to getElementById in react native using props

I am doing a loan calculation app and i run into the trouble since i am new to react native and previously i have been manipulating the DOM using querySelector or getElementById functions. However this does not work in react, and i am using state to store the value from the user, but i just can't seem to get it right, What am i doing wrong?
I've inserted the calculation element that is later rendered in app.js. All elements are showing up with no error, but the problem is to get user input data and then be able to use that data and do calculations.
Here is my Class
class LanKalkylElement extends React.Component {
constructor(props) {
super(props);
this.state = {
loanAmount: 20000,
loanInterest: 2.5,
loanYear: 10,
};
}
changeAmount(loanAmount) {
this.setState(() => {
return {
loanAmount: parseFloat(loanAmount),
};
});
}
changeInterest(loanInterest) {
this.setState(() => {
return {
loanInterest: parseFloat(loanInterest),
};
});
}
changeYear(loanYear) {
this.setState(() => {
return {
loanYear: parseFloat(loanYear),
};
});
}
calcButton() {
Alert.alert(this.props.loanAmount);
}
buttonHomeFunc() {
this.props.navigation.navigate('Start');
}
render() {
const {loanAmount, loanInterest, loanYear} = this.state;
return(
<View style={styles.contentStyle}>
<Text style={styles.text}> Lånebelopp </Text>
<TextInput style={styles.numericInput}
onBlur={Keyboard.dismiss}
keyboardType={'numeric'}
value={loanAmount}
onValueChange={this.changeAmount.bind(this)} />
<Text style={styles.text}> Ränta </Text>
<TextInput style={styles.numericInput}
onBlur={Keyboard.dismiss}
keyboardType={'numeric'}
value={loanInterest}
onValueChange={this.changeInterest.bind(this)} />
<Text style={styles.text}> Antal år: {String(loanYear)}</Text>
<Slider step={1}
maximumValue={15}
value={loanYear}
onValueChange={this.changeYear.bind(this)} />
<Button title='Kalkylera' onPress={() => this.calcButton()}/>
<Text style={styles.textResult}>Total summa att återbetala:</Text>
<Text style={styles.textResult}>varav räntekostnad:</Text>
<Button title='Tillbaka' onPress={() => this.buttonHomeFunc()}/>
</View>
)
}
}
export default withNavigation(LanKalkylElement);
When a user changes a value in a text input, onValueChange is called. You have bound this prop to functions that modify the state for this component.
This means the value in the text input will always match the value in the state. Therefore, if you need to access the value in a text input you would simply retrieve it from the state, like this:
const loanAmount = this.state.loanAmount;
doSomethingWithLoanAmount(loanAmount);

undefined is not a function in TouchableOpacity onPress

The question is almost similar to this one :
touchableopacity onpress function undefined (is not a function) React Native
But the problem is, I am getting the error despite the fact that I have bind the function. Here is my TouchableOpacity component:
<TouchableOpacity style={styles.eachChannelViewStyle} onPress={() => this.setModalVisible(true)}>
{item.item.thumbnail ?
<Image style={styles.everyVideoChannelThumbnailStyle} source={{uri: item.item.thumbnail}} />
: <ActivityIndicator style= {styles.loadingButton} size="large" color="#0000ff" />}
<Text numberOfLines={2} style={styles.everyVideoChannelVideoNameStyle}>
{item.item.title}
</Text>
</TouchableOpacity>
And this is my setModalVisible function:
setModalVisible(visible) {
console.error(" I am in set modal section ")
this.setState({youtubeModalVisible: visible});
}
Also, I have bind the function in constructor as follows:
this.setModalVisible = this.setModalVisible.bind(this);
But, I am still getting same error that undefined is not a function. Any help regarding this error?
The render method and your custom method must be under the same scope. In code below I have demonstrated the same. I hope you will modify your code accordingly as I assume you got the gist :)
class Demo extends Component {
onButtonPress() {
console.log("click");
}
render() {
return (
<View>
<TouchableOpacity onPress={this.onButtonPress.bind(this)}>
<Text> Click Me </Text>
</TouchableOpacity >
<View>
);
}
}
Alternatively binding method in constructor will also work
class Demo extends Component {
constructor(props){
super(props);
this.onButtonPress= this.onButtonPress.bind(this);
}
onButtonPress() {
console.log("click");
}
render() {
return (
<View>
<TouchableOpacity onPress={this.onButtonPress()}>
<Text> Click Me </Text>
</TouchableOpacity >
<View>
);
}
}
I'm not sure if this will help but I write my functions this way and haven't encountered this problem.
If I were you I'd try binding the function in the place where you declare it.
setModalVisible = (visible) => {
this.setState({ youtubeModalVisible: visible });
}
If you do this, you don't have to bind in the constructor.
constructor(props) {
...
// Comment this out to see it will still bind.
// this.setModalVisible = this.setModalVisible.bind(this);
...
}
Lastly, if this function will only set the modal's state to visible, you might want to remove the argument and pass it this way.
<TouchableOpacity style={styles.eachChannelViewStyle} onPress={this.setModalVisible}>
...
</TouchableOpacity>
// Refactored function declaration would look like this
setModalVisible = () => {
this.setState({ youtubeModalVisible: true });
}

Flat List not updated on state change

I am creating dinner decider app and want to add items into an array from the user input. input added to an array list and shows in alert box but not reloat the FlatList. Please help
Here is my code for the same.
constructor(props){
super(props);
this.state = {items:[{key:'Pasta'},{key:'Pizza'}],userdata:''}
}
render() {
return (
<Container>
<Header></Header>
<Content style={{padding:20}}>
<Text style={{fontSize: 30, textAlign:'center', marginVertical:30,}}>Food Decider</Text>
<Item floatingLabel>
<Label>Add Item</Label>
<Input
onChangeText={(text) => this.setState({userdata:text})}
/>
</Item>
<Button block success style={{marginTop: 20,}}
onPress={this.onSubmit.bind(this)}
>
<Text>Add Item</Text>
</Button>
<FlatList
data = {this.state.items}
renderItem={({item}) => <Text style={styles.item}>{item.key}</Text>}
/>
</Content>
</Container>
);
}
onSubmit(){
this.state.items.push({key:this.state.userdata});
alert(JSON.stringify(this.state.items));
}
Thanks.
You need to use prop legacyImplementation=true in FlatList so that it does the real time changes to the FlatList.
Use:
<FlatList legacyImplementation=true />
first of all in you onSubmit function you didn't set the state just push a value
onSubmit(){
this.state.items.push({key:this.state.userdata});
alert(JSON.stringify(this.state.items));
}
whereas it should be something like this
onSubmit(){
let newState = this.state.items;
newState.push({key:this.state.userdata});
this.setState({items:newState});
alert(JSON.stringify(this.state.items));
}
and also bind your onSubmit function in the constructor like this
constructor(props){
super(props);
this.state = {items:[{key:'Pasta'},{key:'Pizza'}],userdata:''}
this.onSubmit = this.onSubmit.bind(this);
}
In Flat list there is also one more option
"extraData={this.state.metaData}"
you can use this to reset/reload the FlatList on your action complete ,you can use like this:
constructor(props){
super(props);
this.state={
metaData:false,
}
}
<FlatList
data = {this.state.items}
renderItem={({item}) => <Text style={styles.item}>{item.key}</Text>}
extraData={this.state.metaData}
/>
after button click you can use this
onSubmit(){
let {metaTAbBarData}=this.state
this.state.items.push({key:this.state.userdata});
this.setState({metaData:!metaData})
}
it will reload when you click on submit button
it will help you , its help me
onSubmit(){
/*
* FlatList is a PureComponent which means that it will not re-render
* if props remain shallow-equal So, We are Cloning the items array to
* clonedArray, So that the reference to this.state.items will change.
* Means this.state.items === clonedArray will give false value.
*/
let clonedArray = this.state.items.slice();
clonedArray.push({key:this.state.userdata});
this.setState({items: clonedArray});
}

React Native + Redux Form : Wizard Form handleSubmit

I am trying to create wizard form in react native with the help of this example. but handleSubmit is not working.
Signup.js
submitForm(values){
console.log("formValues",values);
}
nextPage(){
this.setState({ page: this.state.page + 1 });
}
render(){
const { page } = this.state;
{page === 1 && <WizardFormFirstPage nextPage={this.nextPage} />}
{page === 2 && <WizardFormSecondPage nextPage={this.nextPage} />}
{page === 3 && <WizardFormThirdPage onSubmit={this.submitForm} />}
}
WizardFormFirstPage and WizardFormSecondPage works fine. but when it comes on WizardFormThirdPage it doesn't do anything (I can't see any console log in my terminal for validations and submitForm function). here is the code written.
WizardFormThirdPage.js
const WizardFormThirdPage = props => {
const { handleSubmit, onSubmit } = props;
return (
<View>
<Field name="street" component={InputField} label="Street" />
<Button style= {{ margin: 10 }} block primary onPress={handleSubmit(onSubmit)}>
<Text>Continue</Text>
</Button>
</View>
);
};
export default reduxForm({
form: 'signup', // <------ same form name
destroyOnUnmount: false, // <------ preserve form data
forceUnregisterOnUnmount: true, // <------ unregister fields on unmount
validate,
})(WizardFormThirdPage);
This is probably too late but I figured out what I was doing wrong.
While wrapping the react native InputText component with the redux form Field component. We have to pass props to the InputText component. I was passing props like this ...
<InputText {...props} />
The {...props} attaches all the event handlers like onChange, onSubmit, etc to the component so we don't have to do it manually. The issue lies here, that the InputText component has a onChangeText property rather than onChange which redux form injects into the props.
The correct way to do this is ..
const renderInput = ({ input: { onChange, ...restInput }}) => {
return <TextInput style={styles.input} onChangeText={onChange} {...restInput} />
}
const Form = props => {
const { handleSubmit } = props
return (
<View style={styles.container}>
<Text>Email:</Text>
<Field name="email" component={renderInput} />
<TouchableOpacity onPress={handleSubmit(submit)}>
<Text style={styles.button}>Submit</Text>
</TouchableOpacity>
</View>
)
}
This answer came from the article Simple React Native forms with redux-form.