Issue in onBlur event of TextInput in react-native - react-native

I want to do some task on press of "Do Some Work" button but after calling of onBlur event of any of the focused TextInput.
Here is my sample code:-
'use strict';
var React = require('react-native');
var {AppRegistry,View,Text,ScrollView,TextInput,TouchableOpacity} = React;
var MyApp = React.createClass({
doSomeWork: function () {
console.log("doSomeWork called....");
},
render: function () {
return (
<View>
<TouchableOpacity style={{padding:10,alignItems:"center",backgroundColor:"blue"}}
onPress={this.doSomeWork}>
<Text>Do Some Work</Text>
</TouchableOpacity>
<View style={{height:400}}>
<ScrollView keyboardShouldPersistTaps={true}>
<View>
<TextInput ref="first" onBlur={()=>{console.log("TextInput 1 blurred....")}}/>
</View>
<View>
<TextInput ref="second" onBlur={()=>{console.log("TextInput 2 blurred....")}}/>
</View>
<View>
<TextInput ref="third" onBlur={()=>{console.log("TextInput 3 blurred....")}}/>
</View>
</ScrollView>
</View>
</View>
);
}
});
AppRegistry.registerComponent('nativeApp', () => MyApp);
I have tried to call
this.refs.first.blur()
manually, but that is in async and i don't know TextInput's event has called or not.
Any help will be appreciated.

work for me
import React, { useState } from 'react';
import { TextInput } from 'react-native';
const MyTextInput = () => {
const [value, setValue] = useState('');
const handleBlur = () => {
console.log('Input lost focus');
}
return (
<TextInput
value={value}
onChangeText={setValue}
onBlur={handleBlur}
/>
);
}

Related

How does onBlur event work in react native? it doesn't work i want it to

I made an expo app.
I want to make an AutoCompleteInput.
When focusing and changing TextInput, It will render the same words.
When ListItem clicks, TextInput's value will update it and it doesn't render.
When TextInput focus out, ListItem doesn't render.
In my code, the onPress event works first. onPress event works after.
how can i do?
and how does work onBlur event?
i moved onBlur event other component, but it doesn't work...
// App.js
...
return (
<View style={styles.container}>
<View style={styles.contentContainer}>
<Text>my content</Text>
</View>
<AutoInput />
<StatusBar style="auto" />
</View>
);
// AutoInput.jsx
import { ListItem, TextInput } from '#react-native-material/core';
import { useCallback, useState } from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import { locations } from '../../utils/constants';
const initialLocation = locations.map(({ name }) => name);
const AutoInput = () => {
const [location, setLocation] = useState('');
const [searchedLocations, setSearchedLocations] = useState([]);
const handleChangeInputText = useCallback(
(newString) => {
setLocation(newString);
if (!newString) {
setSearchedLocations(initialLocation);
return;
}
const filteredLocations = locations
.filter(({ name }) => name.includes(newString))
.map(({ name }) => name);
setSearchedLocations(filteredLocations);
},
[initialLocation]
);
return (
<View style={styles.container}>
<TextInput
placeholder="지역을 검색해주세요"
onBlur={() => {
console.log('blur');
setSearchedLocations([]);
}}
onFocus={() => console.log('focus')}
onChangeText={handleChangeInputText}
value={location}
/>
<ScrollView style={styles.scroll}>
{searchedLocations.map((searchLocation) => (
<ListItem
key={searchLocation}
title={searchLocation}
onPress={() => alert(searchLocation)}
style={styles.listItem}
/>
))}
</ScrollView>
</View>
);
};
const styles = StyleSheet.create({
container: { position: 'absolute', width: '100%' },
scroll: { maxHeight: 300 },
listItem: { width: '100%' },
});
export default AutoInput;
enter image description here

component is not defined while opened on web

I have a component that work fine on android but it shows an error when I try to open it on the web using expo
Uncaught ReferenceError: TextInputMask is not defined
at ./src/components/atoms/TextInputMask.js (TextInputMask.js:6:1)
Here is TextInputMask code that gives an error
import React, { useEffect, useState } from "react";
import { View, StyleSheet } from "react-native";
import { TextInput, Text } from "react-native-paper";
import { TextInputMask as TextInputMaskDefault } from "react-native-masked-text";
export default TextInputMask = React.forwardRef(
(
{
label,
placeholder,
style,
mode = "outlined",
onChangeValue = () => {},
type = "custom",
options = {},
value,
icon,
disabled,
error,
...props
},
ref
) => {
const [data, setData] = useState();
const [maskValue, setMaskValue] = useState();
useEffect(() => {
if (value === null || value === undefined) {
setMaskValue("");
return;
}
if (value) {
value = String(value);
setMaskValue(value);
}
}, [value]);
useEffect(() => {
let rawValue = data?.getRawValue();
onChangeValue(rawValue, maskValue);
}, [maskValue]);
return (
<View style={style}>
<View style={styles.viewInput}>
<TextInput
style={styles.textInput}
label={label}
placeholder={placeholder}
mode={mode}
autoCapitalize="none"
autoCorrect={false}
autoCompleteType="off"
ref={ref}
disabled={disabled}
value={maskValue}
error={error}
render={(inputProps) => {
return (
<TextInputMaskDefault
{...inputProps}
type={type}
options={options}
onChangeText={(text) => setMaskValue(text)}
ref={(ref) => setData(ref)}
/>
);
}}
{...props}
/>
{icon ? (
<Text style={styles.textInputIcon}>
<TextInput.Icon
name={icon}
onPress={onPress}
size={30}
color={error ? Colors.red800 : null}
disabled={disabled}
/>
</Text>
) : null}
</View>
</View>
);
}
);
Any idea where the error might be? I wonder if there is a difference in creating component for android and web?

How to pass selected data to another screen from Flatlist

I am still new in using React Native and Mobile Apps Development. I tried to copy the code from another tutorial and have little bit of understanding it.
I have Save.js, Feed.js and Details.js. I have successfully retrieved the data from Save.js to Feed.js using FlatList and RenderItem. Now, I want to pass only selected data from Feed.js to Details.js. But I am confused which way to use, whether useNavigation, getParam, withNavigation or anything else? And is there any difference between using Hooks and Class? Btw I'm using Hooks.
Save.js
import { View, TextInput, Image, Button, StyleSheet, TouchableOpacity, Text} from 'react-native'
import { NavigationContainer } from '#react-navigation/native'
export default function Save(props, navigation) {
const [productName, setProductName] = useState("")
const [category, setCategory] = useState("")
return (
<View style={styles.inputView}>
<TextInput
placeholder="Product name..."
onChangeText={(productName) => setProductName(productName)}
/>
</View>
<View style={styles.inputView}>
<TextInput
placeholder="Category..."
onChangeText={(category) => setCategory(category)}
/>
</View>
Feed.js
function Feed(props, navigation) {
const { currentUser, posts } = props;
const { navigate } = useNavigation();
return (
<FlatList
data={posts}
keyExtractor={(item, index) => item.key}
contentContainerStyle={{
padding: 20,
paddingTop: StatusBar.currentHeight || 42,
}}
renderItem={({item, index}) => (
<TouchableOpacity
onPress={() => props.navigation.navigate("Details", {productName: item.productName})}
<View>
<Text>{item.productName}</Text>
<Text>Category : {item.category}</Text>
</View>
/>
)}
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts
})
export default connect(mapStateToProps, null)(Feed);
Details.js
export default function Details({ props, navigate, route }) {
const productName = props.navigation.route.params.productName;
const { navigate } = useNavigation();
const productName = useNavigationParam('productName');
return (
<View>
<Text>{productName}</Text>
<Text>{Category}</Text>
</View>
)
}
I am not sure which way to use in Details.js, so I just put all code I have used and tested.
the code bellow will help you and I think you have problem in destructing context this will help you. and remember navigation is an object inside props
Feed.js
function Feed(props) {
const { currentUser, posts, navigation } = props;
return (
<FlatList
data={posts}
keyExtractor={(item, index) => item.key}
contentContainerStyle={{
padding: 20,
paddingTop: StatusBar.currentHeight || 42,
}}
renderItem={({item, index}) => (
<TouchableOpacity
onPress={() => props.navigation.navigate("Details", {productName: item.productName})}
<View>
<Text>{item.productName}</Text>
<Text>Category : {item.category}</Text>
</View>
/>
)}
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts
})
export default connect(mapStateToProps, null)(Feed);
in Feed you dont need to use useNavigation() because props argument contain navigation.
Details.js
export default function Details(props) {
const {productName, category} = props.navigation.route.params;
return (
<TouchableOpacity onPress={()=>props.navigation.navigate("Save",{productName, category})}>
<Text>{productName}</Text>
<Text>{Category}</Text>
</TouchableOpacity>
)
}

Error when simulate change text react native with jest

Let's say I create a login screen. Inside that screen, I import form component. And inside form component I have a text input.
Then, I want to simulate text input on change text, but always get an error
Method “simulate” is meant to be run on 1 node. 0 found instead.
This is my test file
it('calls the login submit method', () => {
const fieldPhoneNumber = wrapper
.find('Form')
.dive()
.find('TextInput[id="fieldPhoneNumber"]');
fieldPhoneNumber
.at(0)
.simulate('changeText', { target: { value: '082262366193' } });
});
This is my component login file
import React, { useState, useEffect } from 'react';
import { ScrollView, StatusBar, Platform } from 'react-native';
import Header from './components/Header';
import Form from './components/Form';
import ButtonSocialMedia from './components/ButtonSocialMedia';
function LoginScreen() {
const [phoneNumber, setPhoneNumber] = useState('');
const [focus, setFocus] = useState(false);
useEffect(() => {
}, [phoneNumber]);
const changePhoneNumber = (value) => {
setPhoneNumber(value);
};
const showAppleButton = () => {
if (Platform.OS === 'ios') {
const version = Platform.Version.split('.')[0];
if (version >= 13) {
return true;
} else {
return false;
}
} else {
return false;
}
};
const loginSubmit = () => {
console.log('Login Submit');
};
return (
<ScrollView>
<StatusBar
translucent
backgroundColor="transparent"
barStyle="light-content"
/>
<Header />
<Form
phoneNumber={phoneNumber}
changePhoneNumber={(value) => changePhoneNumber(value)}
focus={focus}
setFocus={() => setFocus(true)}
loginSubmit={() => loginSubmit()} />
<ButtonSocialMedia showAppleButton={() => showAppleButton()} />
</ScrollView>
);
}
export default LoginScreen;
This is my form component
/* eslint-disable prettier/prettier */
import React from 'react';
import { View, Text, TextInput } from 'react-native';
import styles from '../styles/StyleForm';
import color from '../../../../__global__/styles/themes/colorThemes';
import regex from '../../../../constant/regex';
import * as yup from 'yup';
import { Formik } from 'formik';
import ButtonFull from '../../../../__global__/button/buttonFull';
const regexPhoneNumber = regex.phone;
function Form(props) {
const renderFocus = () => {
if (props.focus) {
return (
<Text style={styles.textFocus}>Type your phone number</Text>
);
}
};
return (
<Formik
enableReinitialize={true}
initialValues={{
phoneNumber: props.phoneNumber,
}}
onSubmit={values => {
console.log('Login Submit');
}}
validateOnMount={true}
validationSchema={yup.object().shape({
phoneNumber: yup
.string()
.required()
.min(8)
.matches(regexPhoneNumber, 'Phone number is not valid'),
})}>
{({
// values,
handleChange,
errors,
setFieldTouched,
touched,
isValid,
handleSubmit,
}) => (
<View style={styles.form}>
<View style={styles.subContainer}>
<View style={styles.containerTitle}>
<Text style={styles.textTitle}>+62</Text>
</View>
<View style={styles.containerPhoneNumber}>
{renderFocus()}
<TextInput
id={'fieldPhoneNumber'}
onFocus={() => props.setFocus(true)}
value={props.phoneNumber}
style={styles.subContainerPhoneNumber}
placeholderStyle={styles.placeholder}
placeholder={'Type your phone number'}
onChangeText={(value) => {
handleChange('phoneNumber');
props.changePhoneNumber(value);
setFieldTouched('phoneNumber', true);
}}
keyboardType={'numeric'}
onBlur={() => setFieldTouched('phoneNumber', true)}
/>
</View>
</View>
{touched.phoneNumber && errors.phoneNumber && (
<View style={styles.containerError}>
<Text style={styles.textError}>Phone number is not valid</Text>
</View>
)}
<View style={styles.containerButton}>
<ButtonFull
isDisabled={!isValid}
id={'buttonLogin'}
color={isValid ? color.thema : color.grey}
handleSubmit={() => props.loginSubmit()}
title={'Next'}
/>
</View>
</View>
)}
</Formik>
);
}
export default Form;
The error you're facing implies that the statement const fieldPhoneNumber wrapper.find('Form').dive().find('TextInput[id="fieldPhoneNumber"]'); couldn't find the TextInput component and hence the simulate function cannot be called. Try searching for the string "TextInput" inside the wrapper, and see if that works.

How to call the redux actions of one component in another

I'm rendering the data got from an API into the Cards, I created a CardContainers component that map the data I get from the API then use that component in another component.
CardContainers.js
import React from 'react';
import {View} from 'react-native';
import {withNavigation} from 'react-navigation';
class CardContainers extends React.Component{
addPlace(){
return this.props.addPlace;
}
renderCards(){
return this.props.data.map((item, index) => {
return (
<View key={index}>
{this.props.renderCard(item)}
</View>
)
})
}
render(){
return(
<View>{this.renderCards()}</View>
)
}
}
PlacesList.js
import React from 'react';
import ProgressiveInput from 'react-native-progressive-input';
import {StyleSheet, View, Alert, Text, TouchableOpacity, ListView, ScrollView} from 'react-native';
import {Button, Card, Icon} from 'react-native-elements';
import {connect} from 'react-redux';
import CardContainers from './CardContainers';
import * as placesActions from '../redux/actions/placesActions';
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
});
class PlacesList extends React.Component{
static navigationOptions = ({navigation}) => {
return {
title:'Finding new places',
}
}
constructor(props){
super(props)
const position = this.props.navigation.getParam('position')
const tripId = JSON.stringify(this.props.navigation.getParam('tripId')).replace(/\"/g,"");
const date = JSON.stringify(this.props.navigation.getParam('date')).replace(/\"/g,"");
this.state={
position: position,
tripId: tripId,
date: date,
dataSource:ds.cloneWithRows([]),
}
}
renderCard(item){
return(
<Card
key={item.id}
title={item.title}
titleStyle={styles.title}
containerStyle={{marginTop: 20, marginBottom:20}}
>
<View style={{flexDirection: 'row'}}>
<Text style={{margin:10}}>
Category: {item.category.title}
</Text>
<Text style={{margin:10}}>
Rating: {item.averageRating}
</Text>
</View>
<View style ={{flexDirection:'row', alignItems:'center', alignSelf:'center'}}>
<Button
icon={<Icon name='add' color='#ffffff'/>}
buttonStyle={{marginLeft:15, borderRadius:10}}
title='ADD THIS PLACE'
type='solid'
onPress={()=>this.props.addPlace()}
/>
</View>
</Card>
);
}
getPlacesAroundDestination = () => {
this.props.aroundPlaces(this.state.position);
this.setState({dataSource: ds.cloneWithRows(this.props.placesAround)})
}
autoComplete = async(query) => {
this.setState({destination: query})
await this.props.suggestPlaces(this.state.position, query)
this.setState({dataSource: ds.cloneWithRows(this.props.searchSuggests)})
}
inputCleared = () => {
this.setState({
destination:'',
isLoading: false,
dataSource: ds.cloneWithRows({}),
});
}
onListItemClicked = (searchSuggests) => {
this.setState({
title: searchSuggests.title,
placeId: searchSuggests.id,
openingHours: searchSuggests.openingHours,
category: searchSuggests.category,
position:searchSuggests.position.toString(),
dataSource:ds.cloneWithRows([]),
})
}
renderRow = (searchSuggests) => {
return(
<TouchableOpacity
style={{padding:10}}
onPress={()=>this.onListItemClicked(searchSuggests)}
>
<Text style={{fontSize:20}}>{searchSuggests.title}</Text>
<Text style={{fontSize:10}}>{searchSuggests.vicinity}</Text>
</TouchableOpacity>
)
}
renderSeparator = () => {
return <View style={{borderWidth:0.5, borderColor:'lightgrey',}}> </View>
}
renderContent(){
return (
<CardContainers
data={this.props.placesAround.items}
renderCard={this.renderCard}
/>
)
}
render(){
return(
<View style={styles.container}>
<ProgressiveInput
style={{marginTop:20, marginLeft:10, marginRight:10}}
placeholder='Your destination...'
value={this.state.destination}
isLoading={this.props.isLoading}
onChangeText={this.autoComplete}
onInputCleared={this.inputCleared}
/>
<View style={{flex:0}}>
<ListView
enableEmptySections
style={{backgroundColor:'white', margin:20}}
dataSource={this.state.dataSource}
renderRow={this.renderRow}
renderSeparator={this.renderSeparator}
/>
</View>
<Button
title= 'SUGGEST'
style={{alignSelf:'center'}}
onPress={() => this.props.aroundPlaces(this.state.position)}
/>
<ScrollView>
{this.props.placesAround.items? this.renderContent():null}
</ScrollView>
</View>
)
}
}
const mapStateToProps = (state) => {
return{
searchSuggests: state.places.searchSuggests,
isLoading: state.places.isLoading,
placesAround: state.places.placesAround,
geolo: state.location.latitude + ',' + state.location.longitude,
}
}
const mapDispatchToProps = (dispatch) => {
return {
addPlace:(tripId, date, title, category, rating, placeID) => dispatch (placesOfPlanActions.addPlace(tripId, date, title, category, rating, placeID)),
aroundPlaces: (geolo) => dispatch(placesActions.aroundPlaces(geolo)),
suggestPlaces: (geolo, destination) => dispatch(placesActions.suggestPlaces(geolo, destination))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(PlacesList);
As you can see in the code below, I want to call the addPlace() function which is a redux action in the onPress event of each Card rendered, but I cannot do it because the CardContainers does not have that function inside. So is there any way that I can do it? I'm quite new to react-native and redux, just spent 4 months on this and I do not think that I fully understand it.
Yup, simply add the connect HOC to CardContainers and you should be able to set the function in mapDispatchToProps like you did in PlacesList.