React Native how to change the focus automatically to the next TextInput? - react-native

I would love to make a bank card number input function .
Here's the idea , when the user complete an input box ,it will focus on the next input box automatically.
Like the picture before :
The TextInput component(AutoTextInput):
import React from 'react';
import { StyleSheet, TextInput, View } from 'react-native'
import colors from '../config/colors';
import AppText from './AppText/AppText';
function AutoTextInput(
{input_width="100%",
input_limit,
input_maxLength =16,
input_keyboardType ='numeric',
title,
...ortherProps}) {
return (
<View style={styles.container,{width:input_width}}>
<AppText style={styles.title}>{title}</AppText>
<TextInput
style={styles.input_text}
maxLength = {input_maxLength}
keyboardType ={input_keyboardType}
{...ortherProps}
/>
</View>
);
}
const styles = StyleSheet.create({
container : {
flexDirection : 'column',
} ,
input_text: {
fontSize: 18,
color : colors.medium,
borderColor : colors.primary,
borderWidth :0.5,
padding :10,
borderRadius :5,
textAlign :'center',
},
title : {
fontWeight : 'bold',
},
})
export default AutoTextInput;
The Screen I use this component :
import React,{useState} from 'react';
import {StyleSheet, View} from 'react-native'
import AppText from './AppText/AppText';
import AutoTextInput from './AutoTextInput';
function Visa({title}) {
const [value,setValue] = useState("");
return (
<View style={styles.container}>
<AppText>Card Number</AppText>
<View style={styles.card_num_container}>
<AutoTextInput
input_maxLength ={4}
input_width="23%"
onChangeText={(text)=>setValue(text)}
placeholder ="0000"
/>
<AutoTextInput
input_maxLength ={4}
input_width="23%"
onChangeText={(text)=>setValue(text)}
placeholder ="0000"
/>
<AutoTextInput
input_maxLength ={4}
input_width="23%"
onChangeText={(text)=>setValue(text)}
placeholder ="0000"
/>
<AutoTextInput
input_maxLength ={4}
input_width="23%"
onChangeText={(text)=>setValue(text)}
placeholder ="0000"
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container : {
width :"100%",
height :"100%",
marginTop : 25,
flexDirection : 'column',
} ,
card_num_container :{
flexDirection : 'row',
width : "100%",
justifyContent : 'space-around',
},
})
export default Visa;
I have no idea how to make it automatically focus on the next input box ..Anyone can help ?
Thanks sooo much !

You can create an array of ref like this
const refs = React.useRef([])
Then pass those refs to the custom text input component then when the user submits 4 number on the input box focus the next input like this
<AutoTextInput
input_maxLength ={4}
input_width="23%"
onChangeText={(text)=>setValue(text)}
placeholder ="0000"
forwardedRef={refs[0]}
onSubmitEditing={() => refs[0 + 1].current.focus()}
forwardRef={refs[index]}/>
Inside AutoTextInput
<TextInput
style={styles.input_text}
ref={this.props.forwardedRef}
maxLength = {input_maxLength}
keyboardType ={input_keyboardType}
onSubmitEditing={this.props.onSubmitEditing}/>

Related

React Native : onChangeText how to handle two events?

I am making a upload user introduction function ,
the idea is :
the input box of introduction is able to add a characters count ;
and is able to use formik to upload also ..
I have noticed that ,the onChangeText only able to handle one event once ..therefore ,I made a function ,in order to handle two events ...
However ,it dose't work ..
Could you please take a look my code ?
Thank you so much !
import React,{useState} from 'react';
import { StyleSheet,View,Text } from 'react-native';
import ErrorMsg from './ErrorMsg';
import { useFormikContext } from 'formik';
import AppTextInput from './AppTextInput';
import colors from '../config/colors';
function AppFormFliedUserInfo({name,width='90%',number,style,...otherProps}) {
const{setFieldTouched, handleChange, errors, touched }= useFormikContext();
const [value,setValue] = useState('');
function twoCalls(name){
handleChange(name);
setValue(text);
}
return (
<View style={[styles.container,{width:width} ,style]}>
<View style={styles.first_container}>
<ErrorMsg error={errors[name]} visible={touched[name]}/>
<AppTextInput
onBlur = {()=>setFieldTouched(name)}
onChangeText = {twoCalls}
width = {width}
number ={number}
maxLength={number}
{...otherProps}
/>
</View>
{/**Count */}
{number&&(
<View style={styles.count_container}>
<Text>{value === "" ? "0" : value.length}/{number}</Text>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
count_container : {
width : "100%",
alignItems : 'flex-end',
marginTop : 10,
},
container :{
padding: 15,
flexDirection: "column",
marginVertical : 10,
borderRadius: 25,
backgroundColor: colors.light,
},
first_container : {
flexDirection : "row",
flex : 1,
},
})
export default AppFormFliedUserInfo;
It looks like it's just a typo:
function twoCalls(name){
handleChange(name);
setValue(name); //Change "text" to "name"
}

react native flat list how to force list items to be the same height?

I have a React-Native application where I am using FlatList to display a list of items obtained from the server. The list has 2 columns and I need my list items to be the same height. I put a border around the code rendering my list items but the list items are not the same height. I have tried using flexbox settings to make the view fill the container, but everything I try makes no difference.
I have created a simplified version of my app to illustrate the issue:
See that the red bordered areas are NOT the same height. I need to get these to be the same height.
The grey border is added in the view wrapping the component responsible for a list item and the red border is the root view of the component responsible for a list item. See the code below for clarity.
I can not use the grey border in my application because my application shows empty boxes whilst the component responsible for a list item is getting additional information from the server before it renders itself
Furthermore I can not used fixed sizes for heights.
Application Project structure and code
My code is split up in a manner where the files ending in "container.js" get the data from the server and pass it to its matching rendering component. For example, "MainListContainer" would be getting the list from the server and then pass the list data to "MainList", and "ListItemContainer" would get additional information about the single list item from the server and pass it to "ListItem" to render the actual item. I have kept this model in my simplified application so its as close to my real application as possible.
index.js
import {AppRegistry} from 'react-native';
import MainListContainer from './app/components/MainListContainer';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => MainListContainer);
MainListContainer.js
import React from 'react';
import MainList from './MainList';
const data = [
{id: '1', title: 'Item 1', subtitle: 'A', description: 'This is the first item.'},
{id: '2', title: 'Item 2', subtitle: 'B', description: 'The Big Brown Fox Jumped over the lazy dogs. The Big Brown Fox Jumped over the lazy dogs.',},
];
const MainListContainer = () => {
return ( <MainList items={data} /> );
};
export default MainListContainer;
MainList.js
import React from 'react';
import {StyleSheet, FlatList, View} from 'react-native';
import ListItemContainer from './ListItemContainer';
export default class MainList extends React.Component {
constructor(props) {
super(props);
this.state = { numColumns: 2};
this.renderItem = this.renderItem.bind(this);
}
renderItem({item, index}) {
return (
<View style={styles.flatListItemContainer}> <!-- THIS IS WHERE THE GREY BORDER IS ADDED -->
<ListItemContainer key={index} item={item} />
</View>
);
}
render() {
const {items} = this.props;
const {numColumns} = this.state;
return (
<View>
<FlatList
data={items}
renderItem={this.renderItem}
numColumns={numColumns}
key={numColumns}
keyExtractor={(item) => item.id}
/>
</View>
);
}
};
const styles = StyleSheet.create({
flatListItemContainer: {
flex: 1,
margin: 10,
borderColor: '#ccc',
borderWidth: 1,
},
});
ListItemContainer.js
import React from 'react';
import ListItem from './ListItem';
const ListItemContainer = (props) => {
const { item } = props;
return (
<ListItem item={item} />
);
};
export default ListItemContainer;
ListItem.js
import React from 'react';
import {TouchableHighlight, View, StyleSheet, Image, Text} from 'react-native';
const ListItem = (props) => {
const { item } = props;
return (
<TouchableHighlight
underlayColor="white"
>
<View style={styles.containerView}> <!-- THIS IS WHERE THE RED BORDER IS ADDED -->
<View style={styles.top_row}>
<Image style={styles.image} source={require('../images/placeholder.png')} />
<View style={styles.title_texts}>
<Text style={{fontWeight:'bold'}}>{item.title}</Text>
<Text style={{color: 'rgb(115, 115, 115)'}}>{item.subtitle}</Text>
</View>
</View>
<Text>{item.description}</Text>
</View>
</TouchableHighlight>
);
};
export default ListItem;
const styles = StyleSheet.create({
containerView: {
padding: 14,
borderColor: 'red',
borderWidth: 1,
},
top_row: {
flex: 1,
flexDirection: 'row',
marginBottom: 10,
},
title_texts: {
flex: 1,
flexDirection: 'column',
},
image: {
alignSelf: 'flex-end',
resizeMode: 'cover',
height: 40,
width: 40,
marginRight: 20
},
});
What I have tried
ListItem.js : move the style onto the "TouchableHighlight" view
ListItem.js : add a view wrapping "TouchableHighlight" view and adding style there
ListItem.js : added "alignItems:'stretch' on the "TouchableHighlight, added it to the "containerView" style, tried it on the description field too
same as "alignItems" but used "alignedSelf" instead
same as "alignItems" but used "alignedContent" instead
tried using "flexGrow" on different views (container, description)
You can measure the height of every element in the list and when you determine the maximum height, you can use that height for every element in the list.
const Parent = ({ ...props }) => {
const [maxHeight, setMaxHeight] = useState<number>(0);
const computeMaxHeight = (h: number) => {
if (h > maxHeight) setMaxHeight(h);
}
return (
<FlatList
data={props.data}
renderItem={({ item }) => (
<RenderItem
item={item}
computeHeight={(h) => computeMaxHeight(h)}
height={maxHeight}
/>
)}
....
/>
)
}
The Items:
const RenderItem = ({...props }) => {
return (
<View
style={{ height: props.height }}
onLayout={(event) => props.computeHeight(event.nativeEvent.layout.height)}
>
<Stuffs />
</View>
)
}
This is a very non-performant way of achieving this. I would avoid this if I have a long list or any list of more than a few items. You however can put certain checks in place to limit rerendering etc. Or alternatively if it is only text that will affect the height, then you can only measure the height of the element with the most text and use that element's height for the rest.
Instead of set fixed width height, you can use flex box to achieve it. I just solved the issue by removing alignSelf at the FlatList and add alignItems center on it.
Wrap the flatList in flex box with align item center, you can add the code in your MainList.js file, the first <View>, i.e:
render() {
const {items} = this.props;
const {numColumns} = this.state;
return (
<View style={{flex: 1, alignItems: 'center'>
<FlatList
data={items}
renderItem={this.renderItem}
numColumns={numColumns}
key={numColumns}
keyExtractor={(item) => item.id}
/>
</View>
);
If still not reflected, you may try to add flex:1, alignItems center in FlatList style props.
You are missing a very basic concept of giving fixed height to the flatlist items, in your ListItem.js, try to set height:200 in containerView. Let me know if that works for you

How do I display a random image retrieved from an array in react native

I am trying to randomly display images from a local file that have been stored in an array in a Card component which is then rendered in the App, the root of the application.
Here is the Images file containing an array of images in the same directory.
const woolyImages = [
require('images/wooly1'),
require('images/wooly2'),
require('images/wooly3'),
require('images/wooly4'),
require('images/wooly5'),
];
export default woolyImages;
Here is the Card component where I attempt to pick a random image from the array and display it using the Image component via 'source'.
import React from 'react';
import {Image, View, StyleSheet} from 'react-native';
import woolyImages from '../images/Images';
function Card() {
const randomImage =
woolyImages[Math.floor(Math.random() * woolyImages.length)];
console.log(randomImage);
return (
<View style={styles.cardContainer}>
<Image source={randomImage} />
</View>
);
}
const styles = StyleSheet.create({
cardContainer: {
width: '50%',
height: '35%',
backgroundColor: 'pink',
},
});
export default Card;
(I inserted the pink background so I could be sure it was rendering in the view. It is.)
Finally, here is the root of the application where I render the card.
import React from 'react';
import {View, StyleSheet} from 'react-native';
import Header from './components/Header';
import Card from './components/Card';
import GenerateWoolyButton from './components/GenerateWoolyButton';
function App() {
return (
<View style={styles.container}>
<Header />
<Card />
<GenerateWoolyButton style={styles.button} />
</View>
);
}
const styles = StyleSheet.create({
container: {
justifyContent: 'flex-start',
alignItems: 'center',
flex: 1,
},
});
export default App;
I am getting the below error.
Can anyone please tell me how to display a randomly generated picture in the Card component and display it in the root of my application? Thank you.
You need to add width & height styles to Image of your Card Component. Such as,
<Image source={randomImage} style={{height: 200, width: 200}} />
Hope this will helps you. Feel free for doubts.

React-native : show content on two rows

I want to show 6 options per row instead of showing only one option per row.
I'm still new in react-native
Here is the code :
import React from 'react';
import {fromJS} from 'immutable';
import {ScrollView, StyleSheet, TouchableOpacity} from 'react-native';
import {Text} from 'src/components';
import Option from './OptionVariable';
import {checkOption} from 'src/modules/product/helper';
import {margin} from 'src/components/config/spacing';
class AttributeVariable extends React.Component {
onSelectOption = (option) => {
const {onSelectAttribute, attribute} = this.props;
onSelectAttribute(attribute.get('id'), attribute.get('name'), option.get('option'));
};
render() {
const {attribute, meta_data, variations} = this.props;
// Attribute selected
const attributeSelected = meta_data.find(attr => attr.get('id') === attribute.get('id') && attr.get('name') === attribute.get('name'));
return (
<>
<Text>
{attribute.get('name')}: <Text colorSecondary>{attributeSelected ? attributeSelected.get('option') : ''}</Text>
</Text>
<ScrollView
style={styles.attribute}
vertical
showsHorizontalScrollIndicator={false}>
{attribute.get('options').map(option => {
const disabled = meta_data.size === 0 ? false : !checkOption(variations, meta_data, fromJS({
id: attribute.get('id'),
name: attribute.get('name'),
option: option.get('option'),
}));
return (
<TouchableOpacity
activeOpacity={disabled ? 1 : 0}
key={option}
onPress={() => disabled ? {} : this.onSelectOption(option)}
>
<Option
type={attribute.get('type')}
selected={attributeSelected && option.get('option') === attributeSelected.get('option')}
disabled={disabled}
option={option}
/>
</TouchableOpacity>
)
})}
</ScrollView>
</>
);
}
}
const styles = StyleSheet.create({
attribute: {
marginTop: margin.small,
},
});
export default AttributeVariable;
Here is how it looks :
I hope everything is clear to you.
Sorry for my bad english it's not my main language....
Thanks in advance for any help.
you can say in your attribute style:
attribute: {
marginTop: margin.small,
flexDirection: 'row'
},
so it should align in rows instead of columns
That should help you to learn more about styling components in react native: https://facebook.github.io/react-native/docs/flexbox
Try using the Flatlist (instead of ScrollView) component passing the horizontal={true} props.
Like this:
FlatList
horizontal={true}
data={attribute.option}
renderItem={({ item }) => "Pass your component"}
keyExtractor={item => item.id}
/>
UPDATE 2 :
I used View instead of ScrollView with the following styles and it worked just fine..
<View style={[styles.container, ]}
horizontal
style={{AlignContent: 'flex-start', flexDirection: 'row', alignItems: 'flex-start',}}
style={styles.attribute}
showsHorizontalScrollIndicator={false}>
UPDATE 1 :
I solved the problem using this :
container: {
marginTop: margin.small,
flexDirection: 'row-reverse',
position: 'relative',
width: 'auto',
alignSelf: 'stretch',
maxWidth: 29,
height: 29,
marginBottom: margin.big,
borderRadius: borderRadius.base,
borderWidth: 1,
},
I changed the size of option so all of them appear on the same row (not exactly what I want but it works)
If anyone have a better answer please share.

Is there any way to override iOS TextInput default behavior of putting an ellipsis when text overflows in React Native?

Is there any way of overriding iOS default behavior of placing ellipsis at the end of TextInput when text overflows? What I want to be able to see are the last characters before the text is truncated.
You can reproduce this with any TextInput. Below is some example code:
import React, { Component } from 'react';
import { TextInput } from 'react-native';
export default function UselessTextInput() {
const [value, onChangeText] = React.useState('Useless Placeholder');
return (
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={text => onChangeText(text)}
value={value}
/>
);
}
You can use multiline attribute as follows. numberOflines specify the number of visible lines (height) in screen. If text more than 4 lines, then it will add scroll automatically.
Please note, I have removed height from the style.
<TextInput
multiline
editable
numberOfLines={4}
maxLength={4000}
style={{ borderColor: 'gray', borderWidth: 1 }}
onChangeText={text => onChangeText(text)}
value={value}
/>