React native - Webview video reload itself on parent state change - ios

I have the following structure:
class Parent extends Component {
state = { isHeaderCollapsed : false }
render() {
<ScrollView decelerationRate="fast" stickyHeaderIndices={isHeaderCollapsed && [0]} scrollEventThrottle={1} onScroll={(value) => this.setState({isHeaderCollpased: true})} style={styles.body}>
<JobHeader collapsed={isHeaderCollapsed} />
<WebView allowsInlineMediaPlayback={true} style={[styles.video, style]} javaScriptEnabled={true} source={{uri: mediaUrl}} />
</ScrollView>
}
On iOS , very time I change the state of the parent component, the youtube video in the webview refreshes itself. I don't want this to happen.
Thanks !

You can use shouldComponentUpdate to check if you need rerender or not:
class Parent extends Component {
constructor(){
super();
this.state = {
isHeaderCollapsed : false
}
}
shouldComponentUpdate(nextProps, nextState){
return nextState.isHeaderCollapsed !== this.state.isHeaderCollapsed;
}
render() {
<ScrollView decelerationRate="fast" stickyHeaderIndices={isHeaderCollapsed && [0]} scrollEventThrottle={1} onScroll={(value) => this.setState({isHeaderCollpased: true})} style={styles.body}>
<JobHeader collapsed={isHeaderCollapsed} />
<WebView allowsInlineMediaPlayback={true} style={[styles.video, style]} javaScriptEnabled={true} source={{uri: mediaUrl}} />
</ScrollView>
}

Related

differents buttons, differents images? react native

I'm a student and I developing a mobile app with React Native.
My target is this image:
(https://cdn.discordapp.com/attachments/403580119714889736/407172407049060352/Apercu_bingo_2_choisir_invites.jpg)
I could write the code with independent buttons, the problem appears when I want to add different images to each button. (I'm waiting for the back dev to create a boucle to add all the images with the shortest code possible (looking forward for some loop ideas ;) ).
import React, {Component} from 'react';
import Button from 'react-native-button';
import
{
AppRegistry,
StyleSheet,
View,
Text,
TouchableHighlight,
Alert,
Image,
ScrollView,
TouchableWithoutFeedback
}
from 'react-native';
import styles from './Styles';
class ToggleButton extends React.Component {
render() {
return (
<View style={styles.cont2}>
<TouchableHighlight style={styles.bubblechoice} onPress={this.props.onPress}>
<View style={[styles.overlay, this.props.selected ? {backgroundColor: '#3C1088'} : {}]}>
<Image style={styles.bubblechoice} source={require('./photo1.jpg')}/>
</View>
</TouchableHighlight>
</View>
);
}
}
export default class MyComponent extends Component
{
constructor(props) {
super(props);
this.state = {
inv1: false,
inv2: false,
};
}
updateChoice(type) {
let newState = {...this.state};
newState[type] = !newState[type];
this.setState(newState);
}
render() {
return (
<View style={styles.containerinvite}>
<ScrollView contentContainerStyle={styles.list}>
<ToggleButton label='inv1' onPress={() => { this.updateChoice('inv1') } } selected={this.state.inv1}/>
<ToggleButton label='inv2' onPress={() => { this.updateChoice('inv2') } } selected={this.state.inv2}/>
<TouchableWithoutFeedback
onPress={() => {Alert.alert('OK');}}>
<View style={styles.button}>
<Text style={styles.buttonText}>ok</Text>
</View>
</TouchableWithoutFeedback>
</View>
);
}
onPress1 = () => {
this.setState({
inv1: !this.state.inv1
});
}
onPress2 = () => {
this.setState({
inv2: !this.state.inv2
});
}
}
The result that I have is:
https://scontent-cdt1-1.xx.fbcdn.net/v/t35.0-12/28580783_10216099091730046_1132055272_o.png?oh=fdb33bbe2b82f29cac1d80b8e25f269e&oe=5A9B2488&dl=1, https://www.facebook.com/
The thing is that the View that changes the status color can't be without children, so I can't just change the image from there. I tried different options but I'm still can manage different photos with independents buttons.
From your parent component you should pass your photos to the child component and use that prop for your source instead of
<Image style={styles.bubblechoice} source={require('./photo1.jpg')}/> =>> This is wrong.
<Image style={styles.bubblechoice} source={require(photoUrls)}/> =>> It should be like this.
If you have further questions about it do not hesitate to ask.

React-Native. TouchableOpacity works only when tap with 2 fingers

I'm running my app via React-Native and native-base and I got some problems with component TouchableOpacity.
When I'm writing my component like
<TouchableOpacity onPress={() => {this.onPress()}}>
<Text>Some text</Text>
</TouchableOpacity
It works perfect, when I tap with 1 finger
But when I'm running like this - My Code:
renderList () {
return (
<List>
{this.state.data.map( ( restaurant, index ) => {
return (
<TouchableOpacity onPress={() => {
this.onPress();
}} key={index}>
<View style={styles.example}>
<ListItem>
<Thumbnail square size={80} source={{ uri: 'Image URL' }}/>
<Body>
<Text>{restaurant.title}</Text>
<Text note>{restaurant.shortDescription}</Text>
</Body>
</ListItem>
</View>
</TouchableOpacity>
);
} )}
</List>
);
}
It is ignoring 1 tap, twice tap etc, it is only working when I tap with 2 fingers. Didn't find any info about this problem. May be some one know how to solve this?
Thanks
Added FullCode:
import React, { Component } from 'react';
import {
Container,
Button,
Header,
Left,
Icon,
Body,
Right,
Text,
Content,
List,
ListItem,
Thumbnail
} from 'native-base';
import { StyleSheet, TouchableOpacity, View } from 'react-native';
export default class Restaurants extends Component {
constructor ( props ) {
super( props );
this.state = {
data: this.props.data
};
}
onPress () {
console.log( 'Hello' );
}
renderList () {
return (
<List>
{this.state.data.map( ( restaurant, index ) => {
return (
<TouchableOpacity onPress={() => {
this.onPress();
}} key={index}>
<View style={styles.example}>
<ListItem>
<Thumbnail square size={80} source={{ uri: 'Image URL' }}/>
<Body>
<Text>{restaurant.title}</Text>
<Text note>{restaurant.shortDescription}</Text>
</Body>
</ListItem>
</View>
</TouchableOpacity>
);
} )}
</List>
);
}
render () {
return (
<Container>
<Header style={styles.header}>
<Left>
<Button transparent>
<Icon style={styles.header_icon} name="arrow-back"/>
</Button>
</Left>
<Body>
<Text>Ресторанны</Text>
</Body>
<Right>
<Button transparent>
<Icon style={styles.header_icon} name="restaurant"/>
</Button>
</Right>
</Header>
<Content>
{this.renderList()}
</Content>
</Container>
);
}
}
const styles = StyleSheet.create( {
header: {
backgroundColor: '#606dff'
},
header_icon: {
color: 'black'
},
example: {
backgroundColor: 'red'
}
} );
As we spoken, I'll post it here so it's better to read and copy the code.
Try this:
renderList() {
return (
<List>
{
this.state.data.map( ( restaurant, index ) => {
return (
<ListItem>
<TouchableOpacity
onPress={() => {
this.onPress();
}}
key={index}
>
<View style={styles.example}>
<Thumbnail square size={80} source={{ uri: 'Image URL' }}/>
<Body>
<Text>{restaurant.title}</Text>
<Text note>{restaurant.shortDescription}</Text>
</Body>
</View>
</TouchableOpacity>
</ListItem>
);
});
}
</List>
);
}
I swapped <ListItem> and <TouchableOpacity>
There might be any conflict the way you wrote.
Let me know if it worked.

React Native Element Type is invalid Error While using ActivityIndicatorIOS

I have begun to develop a book-searching application using React and Google Books API. However, I have run into an error where my simulator reads:
Element Type is invalid: expected a string (for built-in components) or
a class/function (for composite components) but got: undefined. Check
the render method of 'BookList'.
Given that I am fairly new to React, I was hoping someone might be able to point out the error(s) in my code below. I have noted the place where I think there may be an error. Thanks!
class BookList extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
};
}
componentDidMount() {
this.fetchData();
}
fetchData() {
fetch(REQUEST_URL[0])
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.items),
isLoading: false
});
})
.done();
}
render() {
if (this.state.isLoading) {
return this.renderLoadingView();
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderBook.bind(this)}
style={styles.listView}
/>
);
}
// *** adding this function (and using it) began to cause issues ****
renderLoadingView() {
return (
<View style={styles.loading}>
<ActivityIndicatorIOS
size='large'/>
<Text>
Loading books...
</Text>
</View>
);
}
renderBook(book) {
return (
<TouchableHighlight>
<View>
<View style={styles.container}>
<Image
source={{uri: book.volumeInfo.imageLinks.thumbnail}}
style={styles.thumbnail} />
<View style={styles.rightContainer}>
<Text style={styles.title}>{book.volumeInfo.title}</Text>
<Text style={styles.author}>{book.volumeInfo.authors}</Text>
<Text style={styles.price}>{'Lowest Available Price: ' + book.volumeInfo.price}</Text>
</View>
</View>
<View style={styles.separator} />
</View>
</TouchableHighlight>
);
}
}
var REQUEST_URL = ['https://www.googleapis.com/books/v1/volumes?q=subject:fiction'];
ActivityIndicatorIOS is depreciated use ActivityIndicator instead.

Presenting & Closing a modal

So I have an Initial Screen below which allows you to click on a button and present a modal screen:
import SettingsScreen from './SettingsScreen';
...
export default class InitialScreen extends Component {
constructor(props) {
super(props);
this.state = {modalVisible: false};
}
setModalVisible(visible) {
this.setState({modalVisible: visible});
}
render() {
return (
<View>
<NavigationBar
title={{ title: 'Initial Screen', tintColor: 'white', }}
leftButton={
<TouchableOpacity onPress={() => {
this.setModalVisible(!this.state.modalVisible)
}}>
<Image style={styles.leftButton} source={require('./../img/settings.png')} />
</TouchableOpacity>
}
rightButton={{ title: 'Forward', tintColor: 'white' }}
style={{ backgroundColor: '#2196F3', }}
statusBar={{ tintColor: '#1976D2', style: 'light-content' }}
/>
<Modal
animationType={"slide"}
transparent={false}
visible={this.state.modalVisible}
onRequestClose={() => {alert("Modal has been closed.")}}
>
<SettingsScreen />
</Modal>
</View>
);
}
}
It works, but now in my Modal screen I have a button which allows you to dismiss the modal:
import ...
export default class ModalScreen extends Component {
render() {
return (
<View>
<TouchableOpacity onPress={() => {
this.props.setModalVisible(!this.state.modalVisible)
}}>
<Image source={require('./../img/close.png')} />
</TouchableOpacity>
</View>
);
}
}
But it's giving me an error saying this.props.setModalVisible(!this.state.modalVisible) is not a function.
How would I dismiss the modal within the ModalScreen?
Thank you.
you should pass the setModalVisible() function to the modal component
Initial Component:
<ModalComponent closeModal={this.setModalVisible} />
and on ModalComponent call it like this:
<TouchableOpacity onPress={() => {
this.props.closeModal(false)
}}>
<Image source={require('./../img/close.png')} />
</TouchableOpacity>
For custom functions in react native use ES6 style:
setModalVisible = (visible) => {
this.setState({modalVisible: visible});
}
And the code should be: this.setModalVisible(!this.state.modalVisible)

React Native: How to select the next TextInput after pressing the "next" keyboard button?

I defined two TextInput fields as follows:
<TextInput
style = {styles.titleInput}
returnKeyType = {"next"}
autoFocus = {true}
placeholder = "Title" />
<TextInput
style = {styles.descriptionInput}
multiline = {true}
maxLength = {200}
placeholder = "Description" />
But after pressing the "next" button on my keyboard, my react-native app isn't jumping to the second TextInput field. How can I achieve that?
Thanks!
Set the second TextInput focus, when the previous TextInput's onSubmitEditing is triggered.
Try this
Adding a Ref to second TextInput
ref={(input) => { this.secondTextInput = input; }}
Bind focus function to first TextInput's onSubmitEditing event.
onSubmitEditing={() => { this.secondTextInput.focus(); }}
Remember to set blurOnSubmit to false, to prevent keyboard flickering.
blurOnSubmit={false}
When all done, it should looks like this.
<TextInput
placeholder="FirstTextInput"
returnKeyType="next"
onSubmitEditing={() => { this.secondTextInput.focus(); }}
blurOnSubmit={false}
/>
<TextInput
ref={(input) => { this.secondTextInput = input; }}
placeholder="secondTextInput"
/>
Thought I would share my solution using a function component... 'this' not needed!
React 16.12.0 and React Native 0.61.5
Here is an example of my component:
import React, { useRef } from 'react'
...
const MyFormComponent = () => {
const ref_input2 = useRef();
const ref_input3 = useRef();
return (
<>
<TextInput
placeholder="Input1"
autoFocus={true}
returnKeyType="next"
onSubmitEditing={() => ref_input2.current.focus()}
/>
<TextInput
placeholder="Input2"
returnKeyType="next"
onSubmitEditing={() => ref_input3.current.focus()}
ref={ref_input2}
/>
<TextInput
placeholder="Input3"
ref={ref_input3}
/>
</>
)
}
You can do this without using refs. This approach is preferred, since refs can lead to fragile code. The React docs advise finding other solutions where possible:
If you have not programmed several apps with React, your first
inclination is usually going to be to try to use refs to "make things
happen" in your app. If this is the case, take a moment and think more
critically about where state should be owned in the component
hierarchy. Often, it becomes clear that the proper place to "own" that
state is at a higher level in the hierarchy. Placing the state there
often eliminates any desire to use refs to "make things happen" –
instead, the data flow will usually accomplish your goal.
Instead, we'll use a state variable to focus the second input field.
Add a state variable that we'll pass as a prop to the DescriptionInput:
initialState() {
return {
focusDescriptionInput: false,
};
}
Define a handler method that will set this state variable to true:
handleTitleInputSubmit() {
this.setState(focusDescriptionInput: true);
}
Upon submitting / hitting enter / next on the TitleInput, we'll call handleTitleInputSubmit. This will set focusDescriptionInput to true.
<TextInput
style = {styles.titleInput}
returnKeyType = {"next"}
autoFocus = {true}
placeholder = "Title"
onSubmitEditing={this.handleTitleInputSubmit}
/>
DescriptionInput's focus prop is set to our focusDescriptionInput state variable. So, when focusDescriptionInput changes (in step 3), DescriptionInput will re-render with focus={true}.
<TextInput
style = {styles.descriptionInput}
multiline = {true}
maxLength = {200}
placeholder = "Description"
focus={this.state.focusDescriptionInput}
/>
This is a nice way to avoid using refs, since refs can lead to more fragile code :)
EDIT: h/t to #LaneRettig for pointing out that you'll need to wrap the React Native TextInput with some added props & methods to get it to respond to focus:
// Props:
static propTypes = {
focus: PropTypes.bool,
}
static defaultProps = {
focus: false,
}
// Methods:
focus() {
this._component.focus();
}
componentWillReceiveProps(nextProps) {
const {focus} = nextProps;
focus && this.focus();
}
As of React Native 0.36, calling focus() (as suggested in several other answers) on a text input node isn't supported any more. Instead, you can use the TextInputState module from React Native. I created the following helper module to make this easier:
// TextInputManager
//
// Provides helper functions for managing the focus state of text
// inputs. This is a hack! You are supposed to be able to call
// "focus()" directly on TextInput nodes, but that doesn't seem
// to be working as of ReactNative 0.36
//
import { findNodeHandle } from 'react-native'
import TextInputState from 'react-native/lib/TextInputState'
export function focusTextInput(node) {
try {
TextInputState.focusTextInput(findNodeHandle(node))
} catch(e) {
console.log("Couldn't focus text input: ", e.message)
}
}
You can, then, call the focusTextInput function on any "ref" of a TextInput. For example:
...
<TextInput onSubmit={() => focusTextInput(this.refs.inputB)} />
<TextInput ref="inputB" />
...
I created a small library that does this, no code change needed other than replacing your wrapping view and import of TextInput:
import { Form, TextInput } from 'react-native-autofocus'
export default () => (
<Form>
<TextInput placeholder="test" />
<TextInput placeholder="test 2" />
</Form>
)
https://github.com/zackify/react-native-autofocus
Explained in detail here: https://zach.codes/autofocus-inputs-in-react-native/
Using react-native 0.45.1 I also encountered problems trying to set focus on a password TextInput after pressing return key on a username TextInput.
After having tried most of the top rated solutions here on SO I found a solution on github that fulfilled my needs:
https://github.com/shoutem/ui/issues/44#issuecomment-290724642
To sum it up:
import React, { Component } from 'react';
import { TextInput as RNTextInput } from 'react-native';
export default class TextInput extends Component {
render() {
const { props } = this;
return (
<RNTextInput
{...props}
ref={(input) => props.inputRef && props.inputRef(input)}
/>
);
}
}
And then I use it like this:
import React, {Component} from 'react';
import {
View,
} from 'react-native';
import TextInput from "../../components/TextInput";
class Login extends Component {
constructor(props) {
super(props);
this.passTextInput = null
}
render() {
return (
<View style={{flex:1}}>
<TextInput
style={{flex:1}}
placeholder="Username"
onSubmitEditing={(event) => {
this.passTextInput.focus()
}}
/>
<TextInput
style={{flex:1}}
placeholder="Password"
inputRef={(input) => {
this.passTextInput = input
}}
/>
</View>
)
}
}
Combining #Eli Johnson's solution for functional components with #Rodrigo Tessarollo's solution for a CustomTextInput:
import React, { useRef } from 'react';
import { CustomTextInput } from 'path/to/CustomTextInput';
...
export const MyFormComponent = () => {
const ref_to_input2 = useRef();
return (
<>
<CustomTextInput
placeholder="Input 1"
autoFocus={true}
returnKeyType="next"
onSubmitEditing={() => ref_to_input2.current.focus()}
/>
<CustomTextInput
placeholder="Input 2"
returnKeyType="done"
refInner={ref_to_input2}
onSubmitEditing={/* Do something! */}
/>
</>
)
}
and in your CustomTextInput component:
import { TextInput } from "react-native";
export const CustomTextInput = (props) => {
<TextInput
ref={props.refInner}
{...props}
/>
}
For me on RN 0.50.3 it's possible with this way:
<TextInput
autoFocus={true}
onSubmitEditing={() => {this.PasswordInputRef._root.focus()}}
/>
<TextInput ref={input => {this.PasswordInputRef = input}} />
You must see this.PasswordInputRef._root.focus()
My scenario is < CustomBoladonesTextInput /> wrapping a RN < TextInput />.
I solved this issue as follow:
My form looks like:
<CustomBoladonesTextInput
onSubmitEditing={() => this.customInput2.refs.innerTextInput2.focus()}
returnKeyType="next"
... />
<CustomBoladonesTextInput
ref={ref => this.customInput2 = ref}
refInner="innerTextInput2"
... />
On CustomBoladonesTextInput's component definition, I pass the refField to the inner ref prop like this:
export default class CustomBoladonesTextInput extends React.Component {
render() {
return (< TextInput ref={this.props.refInner} ... />);
}
}
And voila. Everything get back works again.
Hope this helps
If you happen to be using tcomb-form-native as I am, you can do this, too. Here's the trick: instead of setting the props of the TextInput directly, you do it via options. You can refer to the fields of the form as:
this.refs.form.getComponent('password').refs.input.focus()
So the final product looks something like this:
var t = require('tcomb-form-native');
var Form = t.form.Form;
var MyForm = t.struct({
field1: t.String,
field2: t.String,
});
var MyComponent = React.createClass({
_getFormOptions () {
return {
fields: {
field1: {
returnKeyType: 'next',
onSubmitEditing: () => {this.refs.form.getComponent('field2').refs.input.focus()},
},
},
};
},
render () {
var formOptions = this._getFormOptions();
return (
<View style={styles.container}>
<Form ref="form" type={MyForm} options={formOptions}/>
</View>
);
},
});
(Credit to remcoanker for posting the idea here: https://github.com/gcanti/tcomb-form-native/issues/96)
This is the way I achieved it. And the example below has used the React.createRef() API introduced in React 16.3.
class Test extends React.Component {
constructor(props) {
super(props);
this.secondTextInputRef = React.createRef();
}
render() {
return(
<View>
<TextInput
placeholder = "FirstTextInput"
returnKeyType="next"
onSubmitEditing={() => { this.secondTextInputRef.current.focus(); }}
/>
<TextInput
ref={this.secondTextInputRef}
placeholder = "secondTextInput"
/>
</View>
);
}
}
I think this will help you.
Try this solution on React Native's GitHub issues.
https://github.com/facebook/react-native/pull/2149#issuecomment-129262565
You need to use the ref prop for the TextInput component.
Then you need a create a function that gets called on onSubmitEditing prop that moves the focus on the second TextInput ref.
var InputScreen = React.createClass({
_focusNextField(nextField) {
this.refs[nextField].focus()
},
render: function() {
return (
<View style={styles.container}>
<TextInput
ref='1'
style={styles.input}
placeholder='Normal'
returnKeyType='next'
blurOnSubmit={false}
onSubmitEditing={() => this._focusNextField('2')}
/>
<TextInput
ref='2'
style={styles.input}
keyboardType='email-address'
placeholder='Email Address'
returnKeyType='next'
blurOnSubmit={false}
onSubmitEditing={() => this._focusNextField('3')}
/>
<TextInput
ref='3'
style={styles.input}
keyboardType='url'
placeholder='URL'
returnKeyType='next'
blurOnSubmit={false}
onSubmitEditing={() => this._focusNextField('4')}
/>
<TextInput
ref='4'
style={styles.input}
keyboardType='numeric'
placeholder='Numeric'
blurOnSubmit={false}
onSubmitEditing={() => this._focusNextField('5')}
/>
<TextInput
ref='5'
style={styles.input}
keyboardType='numbers-and-punctuation'
placeholder='Numbers & Punctuation'
returnKeyType='done'
/>
</View>
);
}
});
<TextInput placeholder="Nombre"
ref="1"
editable={true}
returnKeyType="next"
underlineColorAndroid={'#4DB6AC'}
blurOnSubmit={false}
value={this.state.First_Name}
onChangeText={First_Name => this.setState({ First_Name })}
onSubmitEditing={() => this.focusNextField('2')}
placeholderTextColor="#797a7a" style={{ marginBottom: 10, color: '#808080', fontSize: 15, width: '100%', }} />
<TextInput placeholder="Apellido"
ref="2"
editable={true}
returnKeyType="next"
underlineColorAndroid={'#4DB6AC'}
blurOnSubmit={false}
value={this.state.Last_Name}
onChangeText={Last_Name => this.setState({ Last_Name })}
onSubmitEditing={() => this.focusNextField('3')}
placeholderTextColor="#797a7a" style={{ marginBottom: 10, color: '#808080', fontSize: 15, width: '100%', }} />
and add method
focusNextField(nextField) {
this.refs[nextField].focus();
}
Using callback refs instead of the legacy string refs:
<TextInput
style = {styles.titleInput}
returnKeyType = {"next"}
autoFocus = {true}
placeholder = "Title"
onSubmitEditing={() => {this.nextInput.focus()}}
/>
<TextInput
style = {styles.descriptionInput}
multiline = {true}
maxLength = {200}
placeholder = "Description"
ref={nextInput => this.nextInput = nextInput}
/>
For the accepted solution to work if your TextInput is inside another component, you'll need to "pop" the reference from ref to the parent container.
// MyComponent
render() {
<View>
<TextInput ref={(r) => this.props.onRef(r)} { ...this.props }/>
</View>
}
// MyView
render() {
<MyComponent onSubmitEditing={(evt) => this.myField2.focus()}/>
<MyComponent onRef={(r) => this.myField2 = r}/>
}
in your component:
constructor(props) {
super(props);
this.focusNextField = this
.focusNextField
.bind(this);
// to store our input refs
this.inputs = {};
}
focusNextField(id) {
console.log("focus next input: " + id);
this
.inputs[id]
._root
.focus();
}
Note: I used ._root because it is a ref to TextInput in NativeBase'Library' Input
and in your text inputs like this
<TextInput
onSubmitEditing={() => {
this.focusNextField('two');
}}
returnKeyType="next"
blurOnSubmit={false}/>
<TextInput
ref={input => {
this.inputs['two'] = input;
}}/>
<TextInput
keyboardType="email-address"
placeholder="Email"
returnKeyType="next"
ref="email"
onSubmitEditing={() => this.focusTextInput(this.refs.password)}
blurOnSubmit={false}
/>
<TextInput
ref="password"
placeholder="Password"
secureTextEntry={true} />
And add method for onSubmitEditing={() => this.focusTextInput(this.refs.password)} as below:
private focusTextInput(node: any) {
node.focus();
}
Here is how achieved this for reactjs phone code inputs
import React, { useState, useRef } from 'react';
function Header(props) {
const [state , setState] = useState({
phone_number:"",
code_one:'',
code_two:'',
code_three:'',
code_four:'',
submitted:false,
})
const codeOneInput = useRef(null);
const codeTwoInput = useRef(null);
const codeThreeInput = useRef(null);
const codeFourInput = useRef(null);
const handleCodeChange = (e) => {
const {id , value} = e.target
if(value.length < 2){
setState(prevState => ({
...prevState,
[id] : value
}))
if(id=='code_one' && value.length >0){
codeTwoInput.current.focus();
}
if(id=='code_two' && value.length >0){
codeThreeInput.current.focus();
}
if(id=='code_three' && value.length >0){
codeFourInput.current.focus();
}
}
}
const sendCodeToServer = () => {
setState(prevState => ({
...prevState,
submitted : true,
}))
let codeEnteredByUser = state.code_one + state.code_two + state.code_three + state.code_four
axios.post(API_BASE_URL, {code:codeEnteredByUser})
.then(function (response) {
console.log(response)
})
}
return(
<>
<div className="are">
<div className="POP-INN-INPUT">
<input type="text" id="code_one" ref={codeOneInput} value={state.code_one} onChange={handleCodeChange} autoFocus/>
<input type="text" id="code_two" ref={codeTwoInput} value={state.code_two} onChange={handleCodeChange}/>
<input type="text" id="code_three" ref={codeThreeInput} value={state.code_three} onChange={handleCodeChange}/>
<input type="text" id="code_four" ref={codeFourInput} value={state.code_four} onChange={handleCodeChange}/>
</div>
<button disabled={state.submitted} onClick={sendCodeToServer}>
</div>
</>
)
}
export default
There is a way to capture tabs in a TextInput. It's hacky, but better than nothing.
Define an onChangeText handler that compares the new input value with the old, checking for a \t. If one is found, advance the field as shown by #boredgames
Assuming the variable username contains the value for the username and setUsername dispatches an action to change it in the store (component state, redux store, etc), do something like this:
function tabGuard (newValue, oldValue, callback, nextCallback) {
if (newValue.indexOf('\t') >= 0 && oldValue.indexOf('\t') === -1) {
callback(oldValue)
nextCallback()
} else {
callback(newValue)
}
}
class LoginScene {
focusNextField = (nextField) => {
this.refs[nextField].focus()
}
focusOnPassword = () => {
this.focusNextField('password')
}
handleUsernameChange = (newValue) => {
const { username } = this.props // or from wherever
const { setUsername } = this.props.actions // or from wherever
tabGuard(newValue, username, setUsername, this.focusOnPassword)
}
render () {
const { username } = this.props
return (
<TextInput ref='username'
placeholder='Username'
autoCapitalize='none'
autoCorrect={false}
autoFocus
keyboardType='email-address'
onChangeText={handleUsernameChange}
blurOnSubmit={false}
onSubmitEditing={focusOnPassword}
value={username} />
)
}
}
Really annoying that RN doesn't have some sort of Tabindex system.
A functional component, for my use case, I have an array of string IDs for inputs which I iterate through and show one text input each. The following code will automatically jump the user through all of them, stopping the keyboard from disappearing/reappearing between fields and dismissing it at the end, also showing the appropriate "action" button on the keyboard.
Typescript, Native Base.
const stringFieldIDs = [
'q1', 'q2', 'q3'
];
export default () => {
const stringFieldRefs = stringFieldIDs.map(() => useRef < any > ());
const basicStringField = (id: string, ind: number) => {
const posInd = stringFieldIDs.indexOf(id);
const isLast = posInd === stringFieldIDs.length - 1;
return ( <
Input blurOnSubmit = {
isLast
}
ref = {
stringFieldRefs[posInd]
}
returnKeyType = {
isLast ? 'done' : 'next'
}
onSubmitEditing = {
isLast ?
undefined :
() => stringFieldRefs[posInd + 1].current._root.focus()
}
/>
);
};
return stringFieldIDs.map(basicStringField);
};
import React, { useState, useEffect, useRef, } from 'react';
const OTP = (props) => {
const OTP = [];
const ref_input = [];
ref_input[0] = useRef();
ref_input[1] = useRef();
ref_input[2] = useRef();
ref_input[3] = useRef();
const focusNext = (text, index) => {
if (index < ref_input.length - 1 && text) {
ref_input[index + 1].current.focus();
}
if (index == ref_input.length - 1) {
ref_input[index].current.blur();
}
OTP[index] = text;
}
const focusPrev = (key, index) => {
if (key === "Backspace" && index !== 0) {
ref_input[index - 1].current.focus();
}
}
return (
<SafeAreaView>
<View>
<ScrollView contentInsetAdjustmentBehavior="automatic" showsVerticalScrollIndicator={false}>
<View style={loginScreenStyle.titleWrap}>
<Title style={loginScreenStyle.titleHeading}>Verify OTP</Title>
<Subheading style={loginScreenStyle.subTitle}>Enter the 4 digit code sent to your mobile number</Subheading>
</View>
<View style={loginScreenStyle.inputContainer}>
<TextInput
mode="flat"
selectionColor={Colors.primaryColor}
underlineColorAndroid="transparent"
textAlign='center'
maxLength={1}
keyboardType='numeric'
style={formScreenStyle.otpInputStyle}
autoFocus={true}
returnKeyType="next"
ref={ref_input[0]}
onChangeText={text => focusNext(text, 0)}
onKeyPress={e => focusPrev(e.nativeEvent.key, 0)}
/>
<TextInput
mode="flat"
selectionColor={Colors.primaryColor}
underlineColorAndroid="transparent"
textAlign='center'
maxLength={1}
keyboardType='numeric'
style={formScreenStyle.otpInputStyle}
ref={ref_input[1]}
onChangeText={text => focusNext(text, 1)}
onKeyPress={e => focusPrev(e.nativeEvent.key, 1)}
/>
<TextInput
mode="flat"
selectionColor={Colors.primaryColor}
underlineColorAndroid="transparent"
textAlign='center'
maxLength={1}
keyboardType='numeric'
style={formScreenStyle.otpInputStyle}
ref={ref_input[2]}
onChangeText={text => focusNext(text, 2)}
onKeyPress={e => focusPrev(e.nativeEvent.key, 2)}
/>
<TextInput
mode="flat"
selectionColor={Colors.primaryColor}
underlineColorAndroid="transparent"
textAlign='center'
maxLength={1}
keyboardType='numeric'
style={formScreenStyle.otpInputStyle}
ref={ref_input[3]}
onChangeText={text => focusNext(text, 3)}
onKeyPress={e => focusPrev(e.nativeEvent.key, 3)}
/>
</View>
</ScrollView>
</View>
</SafeAreaView >
)
}
export default OTP;
If you are using NativeBase as UI Components you can use this sample
<Item floatingLabel>
<Label>Title</Label>
<Input
returnKeyType = {"next"}
autoFocus = {true}
onSubmitEditing={(event) => {
this._inputDesc._root.focus();
}} />
</Item>
<Item floatingLabel>
<Label>Description</Label>
<Input
getRef={(c) => this._inputDesc = c}
multiline={true} style={{height: 100}} />
onSubmitEditing={(event) => { this._inputLink._root.focus(); }} />
</Item>
Here a reagent solution for a input component that has a :focus property.
The field will be focused as long as this prop is set to true and will not have focus as long as this is false.
Unfortunately this component needs to have a :ref defined, I could not find an other way to call .focus() on it. I am happy about suggestions.
(defn focusable-input [init-attrs]
(r/create-class
{:display-name "focusable-input"
:component-will-receive-props
(fn [this new-argv]
(let [ref-c (aget this "refs" (:ref init-attrs))
focus (:focus (ru/extract-props new-argv))
is-focused (.isFocused ref-c)]
(if focus
(when-not is-focused (.focus ref-c))
(when is-focused (.blur ref-c)))))
:reagent-render
(fn [attrs]
(let [init-focus (:focus init-attrs)
auto-focus (or (:auto-focus attrs) init-focus)
attrs (assoc attrs :auto-focus auto-focus)]
[input attrs]))}))
https://gist.github.com/Knotschi/6f97efe89681ac149113ddec4c396cc5

Resources