I'm new to react native and trying event handling and came up with a problem.
Suppose I have a code like this
class Demo extends React.Component {
constructor(props) {
this.textValues = {a: null, b: null};
}
handleChange(event) {
this.textValues['a'] = this.props.customProps;
this.textValues['b'] = event.nativeEvent.text;
}
render() {
return (
<View>
<TextInput
customProps = 'T1'
onChange = {this.handleChange.bind(this)}
/>
<TextInput
customProps = 'T2'
onChange = {this.handleChange.bind(this)}
/>
</View>
)
}
}
I want to access textValues from parent component i.e., Demo as well as customProps from TextInput but
If I bind this with handleChange then this refrence to Demo class and this.props.customProps gives undefined
If I do not bind this with handleChange then this.textValues is not defined and this.props.customProps gives perfect value
But I want to acess both textValues of Demo and customProps of TextInput in handleChange function.
class Demo extends React.Component {
constructor(props) {
super(props);
this.textValues = { a: null, b: null };
}
handleChange = field => (event) => {
console.log(field)
this.textValues[field] = event.nativeEvent.text
}
render() {
return (
<View>
<TextInput onChange={this.handleChange('a')} />
<TextInput onChange={this.handleChange('b')} />
</View>
)
}
}
ReactDOM.render(
<Demo />,
document.getElementById('container')
);
do you want the handleChange function to know which TextInput is called from?
try these codes
<TextInput
customProps = 'T1'
onChange = {(input)=>{
this.handleChange("T1", input)
}}
/>
<TextInput
customProps = 'T2'
onChange = = {(input)=>{
this.handleChange("T2", input)
}}
/>
and the hanleChange function is
handleChange(inputTag, inputText) {...}
Related
I've got a react native form with a button that should autofill some of the field based on some user's information. The point is that even if I update the state variable related to a TextInput, the TextInput does not display such data. Here's a short snippet for the sake of simplicity
export default class Component extends React.Component {
constructor(props) {
super(props);
this.state = {
value: null
}
}
autocompile = () => {
this.setState({"value": "new value"})
}
render() {
return (
<View>
<TouchableOpacity
onPress={() => {
this.autocompile
}}>
<Text>Autocompile</Text>
</TouchableOpacity>
<TextInput
onChangeText={(value) => this.setState({'value': value})}
value={this.state.value}
/>
</View>
)
}
}
}
Following this example, if I clicked "Autocompile", the TextInput below wouldn't show the new value, even though the state variable would be updated. My question is, how can I update a TextInput displayed value from the external without typing in?
Class Component Solution
import React from 'react';
import { Text, View, TextInput, TouchableOpacity } from 'react-native';
export default class Component extends React.Component {
constructor(props) {
super(props);
this.state = { value: '' };
this.autocompile = this.autocompile.bind(this);
}
autocompile() {
this.setState({ value: 'new value' });
}
render() {
return (
<View>
<TouchableOpacity onPress={this.autocompile}>
<Text>Autocompile</Text>
</TouchableOpacity>
<TextInput
onChangeText={(value) => this.setState({ value: value })}
value={this.state.value}
/>
</View>
);
}
}
Function Component Solution
import React, { useState } from 'react';
import { View, TouchableOpacity, Text, TextInput } from 'react-native';
const App = () => {
const [value, setValue] = useState('');
const autocompile = () => setValue('new value');
return (
<View>
<TouchableOpacity onPress={() => autocompile()}>
<Text>Autocompile</Text>
</TouchableOpacity>
<TextInput onChangeText={(value) => setValue(value)} value={value} />
</View>
);
};
export default App;
You need to change this
autocompile = () => {
this.setState({"value": "new value"})
}
to
autocompile = () => {
this.setState({ value: 'new value' })
}
and this one also
onChangeText={(value) => this.setState({'value': value})}
to
onChangeText={(value) => this.setState({ value })}
I have a TextInput below :
Normally I am able to read TextInput when there is a change , the problem is the TextInput for password is populated with a default password .
So the user may not need to edit(change) it - therefore not triggering onChangeText method .
import React,{Component} from 'react'
import {View, Text, TextInput } from 'react-native'
export default class App extends Component {
constructor(props){
super(props);
this.state = {
username: '',
password: 12345
}
}
onChangeText = (key, val) => {
this.setState({ [key]: val})
}
render() {
return (
<View style={styles.container}>
<Text>Login Form</Text>
<TextInput
placeholder='Username'
onChangeText={val => this.onChangeText('username', val)}
style={styles.input}
value={this.state.username}
/>
<TextInput
placeholder='Password'
onChangeText={val => this.onChangeText('password', val)}
style={styles.input}
value={this.state.password}
secureTextEntry={true}
/>
</View>
);
}
}
Now , my question is how do I read TextInputs which are not being changed ?
change TextInput value prop to defaultValue. i think that might work. TextInput value prop does't allow to modify existing value.
<TextInput
placeholder='Password'
onChangeText={val => this.onChangeText('password', val)}
style={styles.input}
defaultValue={this.state.password}
secureTextEntry={true}
/>
There is a way to get TextInput value directly from the component via refs.
If the input receives text from value prop you can use _lastNativeText method as in the example below.
export default class App extends Component {
state = {
text: 'Hello!'
}
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
componentDidMount() {
this.printTextInputValue();
}
printTextInputValue = () => {
const input = this.inputRef.current;
console.log(input._lastNativeText);
}
render() {
return (
<View style={styles.container}>
<TextInput value={this.state.text} ref={this.inputRef} />
<Button onPress={this.printTextInputValue} title="Get input value" />
</View>
);
}
}
If the TextInput uses defaultValue prop use _getText method to read the initial value.
export default class App extends Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
componentDidMount() {
this.printDefaultInputValue();
}
printDefaultInputValue = () => {
const input = this.inputRef.current;
console.log(input._getText());
}
printTextInputValue = () => {
const input = this.inputRef.current;
console.log(input._lastNativeText);
}
render() {
return (
<View style={styles.container}>
<TextInput defaultValue="Hello Default!" ref={this.inputRef} />
<Button onPress={this.printTextInputValue} title="Get input value" />
</View>
);
}
}
Do note however that these methods are not officially documented and may be removed in future versions of React Native, so use them at your own discretion.
in Parent component, I'm trying to focus the TextInput of second Child component when submit button is pressed in the TextInput of the first Child component but this error comes up: error message
Child.js
import React from "react";
import { View, TextInput} from "react-native";
export default class Child extends React.Component {
focus = ref => {
ref.input.focus();
};
render() {
return (
<View>
<TextInput
placeholder='enter text'
onSubmitEditing={() => {
this.focus(this.props.destinationRef);
}}
ref={input => {
this.input = input;
}}
/>
</View>
);
}
}
Parent.js
import React from "react";
import Child from "./Child";
import { View, StyleSheet } from "react-native";
export default class Parent extends React.Component {
// componentDidMount() {
// setTimeout(() => {
// this.two.input.focus();
// }, 3000);
// }
render() {
return (
<View style={styles.container}>
<Child
destinationRef={() => {
if (!this.two) return this.two;
}}
ref={input => {
this.one = input;
}}
/>
<Child ref={input => (this.two = input)} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center"
}
});
Note that when I uncomment the componendDidMount, second TextInput successfully focuses three seconds after mounting.
I think the problem in render update. In first render time destinationRef is undefined, parent state not updated or force updated so props not updated too.
I try little bit optimize your code. You can use arrow function to bind this:
import React from "react";
import Child from "./Child";
import { View, StyleSheet } from "react-native";
export default class Parent extends React.Component {
_makeFocus(){
this.two.input.focus();
}
handleOnSubmit(){
this._makeFocus();
}
render() {
return (
<View style={styles.container}>
<Child
onSubmit={this.handleOnSubmit.bind(this)}
ref={input => {
this.one = input;
}}
/>
<Child ref={input => (this.two = input)} />
</View>
);
}
}
I would change the logic a little bit since I think the ref is giving you problems.
Parent:
<View style={styles.container}>
<Child next={this.two} ref={comp => this.one = comp}/>
<Child next={this.one} ref={comp => this.two = comp}/>
</View>
Child:
<TextInput
placeholder='enter text'
onSubmitEditing={() => {
if (this.props.next)
this.props.next.focus();
}}
/>
EDIT:
Your approach was correct I believe, I would just update the parent into this:
export default class Parent extends React.Component {
constructor(props){
super(props);
this.references = {};
}
onFocus = (ref) => {
this.references[ref].focus();
}
render() {
return (
<View style={styles.container}>
<Child
destinationRef={'two'}
onFocus={this.onFocus}
ref={input => this.references['one'] = input}
/>
<Child ref={input => this.references['two'] = input} />
</View>
);
}
}
and your child to:
export default class Child extends React.Component {
render() {
return (
<View>
<TextInput
placeholder='enter text'
onSubmitEditing={() => {
this.props.onFocus(this.props.destinationRef);
}}
/>
</View>
);
}
}
I click a button on the navigator, the current screen's one TextInput's is focus.
I need to set the TextInput's instance blur to trigger the textChange function to do something.
In my case, the cursor of TextInput is blinking all the time.
refs.username.blur() does not work.
My code :
class UploadRecordII extends Component{
static navigationOptions = ({ navigation, screenProps }) => ({
headerRight: (<Button containerStyle={{marginRight: 12,}} onPress={ UploadRecordII.onSubmit}>
<Text style={{color: 'white',fontSize: 18,}}>next</Text>
</Button>),
});
.....
static onSubmit() {
let that = UploadRecordII.instance;
debugger;
that.refs.username.blur();
debugger;
that.props.commitCreateRecordUpdate(that.refs.username._lastNativeText,that.imageList)
that.props.navigation.navigate('Record3');
}
...... in render(){
....
<TextInput ref = 'username' placeholder='' editable = {true} maxLength = {500}
keyboardType='default' multiline={true}
style={styles.input} underlineColorAndroid='rgba(0,0,0,0)'
onBlur={ ()=> this.textChange()}
clearButtonMode='always' placeholderTextColor='#D6D0D8'></TextInput>
...
}
refs don't work as strings anymore as expected. You need to define your ref e.g.
class UploadRecordII extends Component {
constructor(props) {
super(props);
this.usernameInput = null;
}
onSubmit() {
this.usernameInput.blur();
}
render() {
return (
<TextInput ref={(input) => this.usernameInput = input} ... />
)
}
}
I have a custom TextInput. When I edit the first TextInput and hit the "Next" in the keyboard, I want it to focus the second TextInput. I have searched before in Stack Overflow and it seems I can do it using ref. However I'm not sure how to do that with custom TextInput.
Here is my basic CustomTextInput code:
let CustomTextInput = React.createClass({
propTypes: {
refName: React.PropTypes.string,
returnKeyType: React.PropTypes.string,
onSubmitEditing: React.PropTypes.func
},
getDefaultProps: function(){
return {
refName: "",
returnKeyType: "default",
onSubmitEditing: () => {}
}
},
render: function(){
return(
<View>
<TextInput
ref={this.props.refName}
returnKeyType={this.props.returnKeyType}
onSubmitEditing={this.props.onSubmitEditing}
/>
</View>
)
}
});
module.exports = CustomTextInput
And here is my Parent class that calls it:
let MyParent = React.createClass({
render: function(){
return(
<View>
<CustomTextInput
refName={'firstNameInput'},
returnKeyType={'next'}
onSubmitEditing={(event) => {
this.refs.lastNameInput.focus();
}}
/>
<CustomTextInput
refName={'lastNameInput'}
/>
</View>
)
}
});
Right now, when I press the Next in the keyboard, after selecting the firstName, I got an exception:
undefined is not an object (evaluating '_this2.refs.lastNameInput.focus')
I'm not sure what I did wrong there.. Any help is appreciated. :)
Let's start from the CustomTextInput component.
export default class CustomTextInput extends Component {
componentDidMount() {
if (this.props.onRef != null) {
this.props.onRef(this)
}
}
onSubmitEditing() {
this.props.onSubmitEditing();
}
focus() {
this.textInput.focus()
}
render() {
return (
<View>
<View style={this.state.isFocused ? styles.onFocusedStyle : styles.onBlurStyle}>
<TextInput
ref={input => this.textInput = input}
onSubmitEditing={this.onSubmitEditing.bind(this)}
/>
</View>
<Text style={styles.errorMessageField}>{this.state.errorStatus && this.props.errorMessage}</Text>
</View>
);
}}
Here i have a sample customTextInput. Important things to note here is the componentDidMount(), focus() method and ref property in the TextInput view in render method.
componentDidMount() method passes the ref of the whole CustomTextInput component to it's parent component. Through that reference we will call the focus method of CustomTextInput component from the parent component.
focus() method here focuses the textInput inside the CustomTextInput component by using the ref of TextInput component inside the CustomTextInput component.
The ref property of TextInput stores the reference of the TextInput. This reference is used by the focus() method.
Now let's see the parent component
export default class ParentComponent extends Component {
constructor(props) {
super(props);
this.focusNextField = this.focusNextField.bind(this);
this.inputs = {};
}
focusNextField(id) {
this.inputs[id].focus();
}
render() {
return (
<ScrollView
contentContainerStyle={{paddingBottom:100}}
keyboardDismissMode={'on-drag'}
scrollToTop={true}>
<View>
<View style={{marginTop: 10}}>
<CustomTextInput
onRef={(ref) => {
this.inputs['projectName'] = ref;
}}
onSubmitEditing={() => {
this.focusNextField('projectDescription');
}}
/>
</View>
<View style={{marginTop: 10}}>
<CustomTextInput
onRef={(ref) => {
this.inputs['projectDescription'] = ref;
}}
onSubmitEditing={() => {
this.focusNextField('subDivision');
}}
/>
</View>
<View style={{marginTop: 10}}>
<CustomTextInput
onRef={(ref) => {
this.inputs['subDivision'] = ref;
}}
onSubmitEditing={() => {
this.focusNextField('plan');
}}
/>
</View>
<View style={{marginTop: 10}}>
<CustomTextInput
onRef={(ref) => {
this.inputs['plan'] = ref;
}}
</View>
</View>
</ScrollView>
);
}}
Here in the parent component we store the ref of each CustomTextInput with onRef property and when the submit button from keyboard is pressed we call the focus method of the next CustomTextInput and the focus method of CustomTextInput focuses the TextInput inside the child component.
Here is a solution is you are using functional component:
Your custom component need to use React.forwardRef
const CustomTextInput = React.forwardRef((props, ref) => {
return (
<TextInput
{...props}
ref={ref}
/>
);
});
export default CustomTextInput;
The parent component that imports your custom component:
import React, { createRef } from 'react';
const ParentComponent = () => {
let customTextInputRef = createRef();
return (
<View>
<CustomTextInput
onSubmitEditing={() => customTextInputRef.current.focus()}}
/>
<CustomTextInput
ref={customTextInputRef}
/>
</View>
);
};
Here is a solution that worked for me — basically you make a reference within your custom component, which you can access from your reference in your parent component:
let CustomTextInput = React.createClass({
propTypes: {
refName: React.PropTypes.string,
returnKeyType: React.PropTypes.string,
onSubmitEditing: React.PropTypes.func
},
getDefaultProps: function(){
return {
refName: "",
returnKeyType: "default",
onSubmitEditing: () => {}
}
},
render: function(){
return(
<View>
<TextInput
ref="input"
returnKeyType={this.props.returnKeyType}
onSubmitEditing={this.props.onSubmitEditing}
/>
</View>
)
}
});
module.exports = CustomTextInput
And in the parent component:
let MyParent = React.createClass({
render: function(){
return(
<View>
<CustomTextInput
refName={'firstNameInput'},
returnKeyType={'next'}
onSubmitEditing={(event) => {
this.lastNameInput.refs.input.focus();
}}
/>
<CustomTextInput
refName={ref => this.lastNameInput = ref}
/>
</View>
)
}
});
let CustomTextInput = React.createClass({
componentDidMount() {
// this is to check if a refName prop is FUNCTION;
if (typeof this.props.rex === "function") {
this.props.refName(this.refs.inp);
}
}
render: function() {
return(
<View>
<TextInput ref={"inp"}/>
</View>
)
}
});
let MyParent = React.createClass({
render: function() {
return (
<View>
<CustomTextInput
refName={ (firstNameInput) => this.firstNameInput = firstNameInput }
/>
</View>
)
}
});
try this:
let AwesomeProject = React.createClass({
onSubmitEditing:function(event){
if (this.myTextInput !== null) {
this.myTextInput.focus();
}
},
render(){
return(
<View>
<CustomTextInput
returnKeyType={'next'}
onSubmitEditing={this.onSubmitEditing}
/>
<CustomTextInput
refName={(ref) => this.myTextInput = ref}
/>
</View>
)
}
});