How to get value from TextInput using state in react native? - react-native

I am trying to implement TextInput and I stuck in moment when I need to get text from this.
I tried a lot of options from other StackOverflow questions but nothing works for me.
This is my code where I am using TextInput:
export default class HomeScreen extends Component {
constructor(props) {
super(props)
this.state = {
text: ''
};
}
show = () => {
console.log(this.state.text)
}
render() {
return (
<View>
<View style={{backgroundColor: 'purple', height: '100%'}}>
<View>
<Button title={'test'} onPress={this.show}></Button>
<MyTextInput
btn={1}
placeholder={'test'}
isDateTime={false}
onChangeText={(value) => this.setState({text: value})
value={this.state.text} />
</View>
</View>
</View>
)
}
and here is MyTextInput code:
export function MyTextInput(props) {
const [show, setShow] = useState(false);
const btn = props.btn;
const inputType = () => {
if(props.isDateTime) {
setShow(true);
}
else {
setShow(false);
}
}
return (
<View style={btn ? styles.container : styles.container2}>
<TextInput
style={btn ? styles.input : styles.input2}
{...this.props}
placeholder={props.placeholder}
onFocus={inputType}
showSoftInputOnFocus={props.isDateTime ? false : true} />
{show && (
<DTPicker />
)}
</View>
);
}
When I hit the button I get this:
[Info] 06-01 07:24:59.158 6962 7031 I ReactNativeJS:
Where is my mistake or what I should do different?

you need to pass onChangeText={(value) => props.onChangeText(value)} in TextInput
export function MyTextInput(props) {
const [show, setShow] = useState(false);
const btn = props.btn;
const inputType = () => {
if(props.isDateTime) {
setShow(true);
}
else {
setShow(false);
}
}
return (
<View style={btn ? styles.container : styles.container2}>
<TextInput
onChangeText={(value) => props.onChangeText(value)}
style={btn ? styles.input : styles.input2}
{...this.props}
placeholder={props.placeholder}
onFocus={inputType}
showSoftInputOnFocus={props.isDateTime ? false : true} />
{show && (
<DTPicker />
)}
</View>
);
}

Related

react-native, conditional render does not show on state change. using react navigation setOptions here

in react native, i am using react navigation ,and when using setOptions i am setting up headerright, and onPress of headerRight, i am updating a boolean state to true, to show up the popup, but it does not showup untill i click on the screen once.
this view Media component is passed to a Flatlist as a Renderitem. does it make any problem here.
import { AntDesign, Feather, Ionicons, Octicons } from "#expo/vector-icons";
import { useNavigation } from "#react-navigation/native";
import { Video } from "expo-av";
import { LinearGradient } from "expo-linear-gradient";
import React, { useContext, useEffect, useLayoutEffect, useRef, useState } from "react";
import {
Image,
ImageBackground,
LayoutAnimation,
Text,
TouchableOpacity,
TouchableWithoutFeedback,
View,
Alert
} from "react-native";
import Animated from "react-native-reanimated";
import { colors, radius } from "../../../../styles/variables";
import { AuthContext } from "../../../components/layout/layout";
import MemberModal from "../../../components/member-modal/member-modal";
import { DEFAULT_PROFILE, SUCCESS } from "../../../utils/constants/user-constants";
import { concatMediaData } from "../../../utils/helpers/concatQueryPages";
import { VIDEO } from "../../add-content/utils/constants";
import { useMediaListQuery } from "../../add-content/utils/hooks";
import { POST } from "../../report/utils/constants";
import DeletePostModal from "../deletePostModal";
import { likeKeyframe, styles } from "./style";
import {
downloadMediaSendRequest
} from "../utils/api";
import { LIKE, LIKED_BY, UNLIKE } from "../utils/constants";
import { useDeleteMediaQuery, useLikedListQuery, useLikeUnlikeMediaQuery } from "../utils/hooks";
import { updateImgixCDN } from "../../../utils/helpers/updateImgixCDN";
const ViewMedia = ({ media }) => {
const navigation = useNavigation()
const { authContext, state } = useContext(AuthContext);
const video = useRef(null);
const [mediaDetail, setMediaDetail] = useState(null)
const [mediaLoaded, setLoaded] = useState(false);
const [status, setStatus] = React.useState({});
const [showModal, setModal] = useState(false);
const [showDeleteModal, setDeleteModal] = useState(false);
const [showKebabModal, setKebabModal] = useState(false);
const [profileURI, setURI] = useState(null);
const [liked, setLiked] = useState(false);
const [totalLikes, setTotalLikes] = useState(0);
const [totalComments, setTotalComments] = useState(null);
const [likedBy, setLikedBy] = useState([]);
const [showMediaText, setMediaText] = useState(true);
const [showPlayPause, setPlayPauseIcon] = useState(null);
const [doubleTapCount, setDoubleTapCount] = useState(0);
const [showDoubleTapIcon, setDoubleTapIcon] = useState(false);
const {
data: likedListData,
isLoading: isLikesLoading,
status: likedStatus,
fetchStatus: likedFetchStatus,
} = useLikedListQuery({
mediaId: media?._id,
circleId: media.circleId._id,
pageParam: 1,
feature: LIKED_BY,
authToken: state.authToken,
});
// console.log(media.s3BucketObject)
const { mutateAsync: mutateDeleteMedia } = useDeleteMediaQuery(mediaDetail)
const { mutateAsync: mutateLikeUnlikedMedia } = useLikeUnlikeMediaQuery(mediaDetail)
// useEffect(() => {
// if (!mediaLoaded) {
// authContext.loading(true);
// }
// return () => {
// authContext.loading(false);
// };
// }, [mediaLoaded]);
useEffect(() => {
// setMediaDetail(state?.selectedMediaData)
setMediaDetail(media)
setTotalLikes(media.likes);
setTotalComments(media.comments);
setURI({ uri: updateImgixCDN(media?.uploadedBy?.profile, 'w=200&h=200') });
}, [state])
useEffect(() => {
navigation.setOptions({
headerLeft: () => (
<Ionicons
name="ellipsis-vertical"
size={26}
color={colors.color_white}
onPress={() => {
setKebabModal(!showKebabModal)
console.log(showKebabModal);
}}
/>
),
headerShown: showMediaText,
});
});
useEffect(() => {
if (likedStatus == SUCCESS && likedListData && likedListData.pages.length)
setLiked(!!likedListData?.pages[0]?.isLikedByUser);
}, [isLikesLoading, media]);
useEffect(() => {
if (mediaDetail) setPlayPause();
}, [status.isPlaying]);
const setPlayPause = () => {
if (mediaDetail?.mediaType == VIDEO) {
if (status.isPlaying) {
setPlayPauseIcon(
<Ionicons name="play" size={60} color={colors.color_white} />
);
setTimeout(() => setPlayPauseIcon(null), 1500);
} else {
setPlayPauseIcon(
<AntDesign name="pause" size={60} color={colors.color_white} />
);
}
}
};
const toggle = () => {
setModal(!showModal);
};
const toggleDeleteModal = () => {
setDeleteModal(!showDeleteModal);
setKebabModal(false);
};
const setLikes = async (likeFlag) => {
setLiked(likeFlag);
const args = {
mediaId: mediaDetail._id,
circleId: mediaDetail?.circleId._id,
authToken: state.authToken,
};
if (likeFlag) {
authContext.setSelectedMediaData({ ...state?.selectedMediaData, likes: totalLikes + 1 })
setTotalLikes((prevState) => prevState + 1);
mutateLikeUnlikedMedia({ ...args, feature: LIKE })
} else {
setDoubleTapCount(0)
authContext.setSelectedMediaData({ ...state?.selectedMediaData, likes: totalLikes - 1 })
setTotalLikes((prevState) => prevState - 1);
mutateLikeUnlikedMedia({ ...args, feature: UNLIKE })
}
};
const deleteMedia = async () => {
const args = {
mediaId: mediaDetail._id,
circleId: mediaDetail?.circleId._id,
authToken: state.authToken,
};
authContext.loading(true);
Alert.alert('Deleting post');
await mutateDeleteMedia(args)
navigation.navigate("AddContent");
authContext.loading(false);
};
return (
<TouchableOpacity activeOpacity={1}
onLongPress={() => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.linear);
setMediaText(false);
}}
onPressOut={() => setMediaText(true)}
onPress={() => {
if (showKebabModal) setKebabModal(!showKebabModal);
if (mediaDetail?.mediaType == VIDEO)
status.isPlaying
? video.current.pauseAsync()
: video.current.playAsync();
// Added Double Tap Like feature
let doubleTapTimer;
setDoubleTapCount((prevCount) => prevCount + 1)
if (doubleTapCount % 2 == 1 && !showDoubleTapIcon) {
setDoubleTapIcon(true)
if (doubleTapCount == 1 && !liked) setLikes(true)
clearTimeout(doubleTapTimer)
} else {
doubleTapTimer = setTimeout(() => {
(function () {
setDoubleTapCount(prevCount => prevCount + 1)
setDoubleTapIcon(false)
})()
}, 1000)
}
}} style={styles.media_screen}>
<>
<TouchableWithoutFeedback
>
<>
{mediaDetail?.mediaType == VIDEO ? (
<Video
ref={video}
style={styles.card}
source={{
uri: mediaDetail?.s3BucketObject,
}}
useNativeControls
isLooping
shouldPlay={true}
resizeMode={"contain"}
onReadyForDisplay={() => setLoaded(true)}
onPlaybackStatusUpdate={(status) => setStatus(() => status)}
/>
) : (
<Image
source={{ uri: updateImgixCDN(mediaDetail?.s3BucketObject, '?auto=compress&w=720') }}
borderRadius={radius.border_radius_ternary}
style={styles.card}
resizeMode={"contain"}
// resizeMode={FastImage.resizeMode.contain}
onLoadEnd={() => setLoaded(true)}
/>
)}
<TouchableOpacity
activeOpacity={0.7}
style={styles.play_pause}
onPress={() => {
mediaDetail?.mediaType == VIDEO &&
(status.isPlaying
? video.current.pauseAsync()
: video.current.playAsync());
}}
>
{showPlayPause}
</TouchableOpacity>
{showDoubleTapIcon ? <Animated.View
entering={likeKeyframe} style={[styles.play_pause, styles.center_like_icon]}>
<AntDesign
name="heart"
size={60}
color={colors.color_green}
/>
</Animated.View> : null}
<LinearGradient
colors={[
colors.color_modal_black_bg,
colors.color_transparent,
colors.color_transparent,
colors.color_modal_black_bg,
]}
style={[showMediaText && styles.linearGradient]}
/>
</>
</TouchableWithoutFeedback>
{showMediaText && (
<>
{showKebabModal && (
<View style={styles.kebab}>
<TouchableOpacity
activeOpacity={0.7}
style={styles.kebab_feature}
onPress={async () => {
authContext.loading(true);
setKebabModal(false);
await downloadMediaSendRequest(
updateImgixCDN(mediaDetail?.s3BucketObject, 'auto=compress'),
mediaDetail?.mediaType
);
authContext.loading(false);
}}
>
<Feather
name="download"
size={24}
color={colors.color_blue}
/>
<Text style={[styles.feature_text, styles.download]}>
Download
</Text>
</TouchableOpacity>
{mediaDetail?.uploadedBy?._id == state?.userData?._id ? (
<>
<TouchableOpacity
activeOpacity={0.7}
style={styles.kebab_feature}
onPress={toggleDeleteModal}
>
<AntDesign
name="delete"
size={24}
color={colors.color_error}
/>
<Text style={[styles.feature_text, styles.delete]}>
Delete
</Text>
</TouchableOpacity>
</>
) : (
<TouchableOpacity
activeOpacity={0.7}
style={styles.kebab_feature}
onPress={() => {
navigation.navigate("Report", {
mediaId: mediaDetail?._id,
circleId: mediaDetail?.circleId?._id,
reportType: POST,
});
setKebabModal(false);
}}
>
<Octicons name="stop" size={24} color={colors.color_blue} />
<Text style={[styles.feature_text, styles.download]}>
Report post
</Text>
</TouchableOpacity>
)}
</View>
)}
<View style={styles.detail}>
<TouchableOpacity
activeOpacity={0.7}
style={styles.detail_top}
onPress={toggle}
>
<Image
source={profileURI || DEFAULT_PROFILE}
style={styles.profile}
/>
<Text style={styles.name}>
{mediaDetail?.uploadedBy?.name || "Earth Cups User"}
</Text>
</TouchableOpacity>
<View style={[styles.detail_bottom, styles.bottom]}>
<View style={styles.detail_bottom}>
<TouchableOpacity
activeOpacity={0.9}
style={styles.like_icon}
// taking current feature as a param after hitting the icon.
onPress={() => setLikes(!liked)}
>
{liked ? (
<Animated.View entering={likeKeyframe} exiting={likeKeyframe}>
<AntDesign
name="heart"
size={15}
color={colors.color_green}
/>
</Animated.View>
) : (
<Animated.View entering={likeKeyframe} exiting={likeKeyframe}>
<AntDesign
name="hearto"
size={15}
color={colors.color_black_500}
/>
</Animated.View>
)}
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.8}
onPress={() =>
navigation.navigate("MembersList", {
mediaDetail: mediaDetail,
circleId: mediaDetail?.circleId._id,
likePage: true,
})
}
>
<Text style={styles.name}>
{totalLikes} like{totalLikes > 1 && "s"}
</Text>
</TouchableOpacity>
</View>
<TouchableOpacity
activeOpacity={0.8}
style={[
styles.detail_bottom,
styles.bottom_right,
styles.like_icon,
]}
onPress={() =>
navigation.navigate("Comments", {
mediaDetail: media,
circleId: media?.circleId._id,
})
}
>
<Image
source={require("../../../../assets/icons/comment.png")}
/>
<Text style={[styles.name, styles.comments]}>
{totalComments}
</Text>
</TouchableOpacity>
</View>
</View>
</>
)}
<MemberModal
toggle={toggle}
showModal={showModal}
memberDetail={mediaDetail?.uploadedBy}
/>
<DeletePostModal
toggle={toggleDeleteModal}
showModal={showDeleteModal}
deleteMedia={() => deleteMedia()}
/>
</>
</TouchableOpacity>
);
};
export default React.memo(ViewMedia);

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?

Call a function from static method

I have something like this:
const BasketButton2 = ({ isWhite, style, navigation }) => (
<TouchableOpacity
style={[styles.button, style]}
onPress={() => Header.addInstaPost()}
>
<Icon
family="Entypo"
size={16}
name="new-message"
color={theme.COLORS[isWhite ? 'WHITE' : 'ICON']}
/>
</TouchableOpacity>
);
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {
notifications: false,
loading: true,
error: null,
modalVisible: false,
modalThanksVisible: false,
reportSubmitted: false,
reportError: false,
};
}
handleLeftPress = () => {
const { back, navigation } = this.props;
return back ? navigation.goBack() : navigation.openDrawer();
};
renderRight = () => {
const { white, title, navigation, scene } = this.props;
if (global.loggedUser === true) {
return [
<BasketButton2
key="basket-search"
navigation={navigation}
isWhite={white}
/>,
<ChatButton
key="chat-search"
navigation={navigation}
isWhite={white}
/>,
];
} else {
return [
<BasketButton
key="basket-search"
navigation={navigation}
isWhite={white}
/>,
<ChatButton
key="chat-search"
navigation={navigation}
isWhite={white}
/>,
];
}
};
renderSearch = () => {
const { navigation } = this.props;
return (
<Input
right
color="black"
style={styles.search}
placeholder="What are you looking for?"
onFocus={() => navigation.navigate('Search')}
iconContent={
<Icon
size={16}
color={theme.COLORS.MUTED}
name="magnifying-glass"
family="entypo"
/>
}
/>
);
};
renderOptions = () => {
const { navigation, optionLeft, optionRight } = this.props;
return (
<Block row style={styles.tabs}>
<Button
shadowless
style={[styles.tab, styles.divider]}
onPress={() => navigation.navigate('Categories')}
>
<Block row middle>
<Icon name="globe" family="feather" style={{ paddingRight: 8 }} />
<Text size={16} style={styles.tabTitle}>
{optionLeft || 'Locations'}
</Text>
</Block>
</Button>
<Button
shadowless
style={styles.tab}
onPress={() => navigation.navigate('Deals')}
>
<Block row middle>
<Icon name="grid" family="feather" style={{ paddingRight: 8 }} />
<Text size={16} style={styles.tabTitle}>
{optionRight || 'Categories'}
</Text>
</Block>
</Button>
</Block>
);
};
renderTabs = () => {
const { tabs, tabIndex, navigation } = this.props;
const defaultTab = tabs && tabs[0] && tabs[0].id;
if (!tabs) return null;
return (
<Tabs
data={tabs || []}
initialIndex={tabIndex || defaultTab}
onChange={(id) => navigation.setParams({ tabId: id })}
/>
);
};
renderHeader = () => {
const { search, tabs, options } = this.props;
if (search || tabs || options) {
return (
<Block center>
{search ? this.renderSearch() : null}
{options ? this.renderOptions() : null}
{tabs ? this.renderTabs() : null}
</Block>
);
}
return null;
};
addInstaPost = () => {
this.setState({ modalVisible: true });
};
render() {
const { back, title, white, transparent, navigation, scene } = this.props;
const noShadow = ['Profile'].includes(title);
const noShadowWhite = ['Search'].includes(title);
const headerStyles = [
!noShadow ? styles.shadow : null,
transparent ? { backgroundColor: 'rgba(0,0,0,0)' } : null,
];
var myHeaderStyle = styles.shadow;
if (noShadow) {
var myHeaderStyle = '';
} else if (transparent) {
var myHeaderStyle = "{ backgroundColor: 'rgba(0,0,0,0)' }";
} else if (noShadowWhite) {
var myHeaderStyle = styles.searchShadow;
}
return (
<Block style={myHeaderStyle}>
<View style={styles.imageContainer} transparent={transparent}>
{
this.renderInstaPostButton()
}
</View>
<View style={styles.item}>
<NavBar
back={back}
title={title}
style={styles.navbar}
transparent={transparent}
right={this.renderRight()}
rightStyle={{ alignItems: 'center' }}
leftStyle={{ paddingTop: 3, flex: 0.3 }}
leftIconName={back ? 'leftcircle' : 'menu-fold'}
leftIconFamily="AntDesign"
leftIconSize="1.6"
leftIconColor={
white ? materialTheme.COLORS.NAVICON : theme.COLORS.ICON
}
titleStyle={[
styles.title,
{ color: theme.COLORS[white ? 'WHITE' : 'ICON'] },
]}
onLeftPress={this.handleLeftPress}
/>
</View>
{this.renderHeader()}
</Block>
);
}
}
So basically inside BasketButton2 I am trying to make a call to a function which is inside the class Header.
onPress={() => Header.addInstaPost() is not working
as well as onPress={() => this.addInstaPost()
I am getting Header.addInstaPost is not defined.
How I can refer to function inside class?
Thanks!!
You can pass addInstaPost as a property to BasketButton2
class Header extends React.Component {
addInstaPost = () => {
this.setState({ modalVisible: true });
};
renderRight = () => {
const { white, title, navigation, scene } = this.props;
if (global.loggedUser === true) {
return [
<BasketButton2
key="basket-search"
navigation={navigation}
isWhite={white}
onPress={this.addInstaPost}
/>,
<ChatButton
key="chat-search"
navigation={navigation}
isWhite={white}
/>,
];
} else {
return [
<BasketButton
key="basket-search"
navigation={navigation}
isWhite={white}
/>,
<ChatButton
key="chat-search"
navigation={navigation}
isWhite={white}
/>,
];
}
};
}
const BasketButton2 = ({ isWhite, style, navigation, onPress }) => (
<TouchableOpacity
style={[styles.button, style]}
onPress={onPress}
>
<Icon
family="Entypo"
size={16}
name="new-message"
color={theme.COLORS[isWhite ? 'WHITE' : 'ICON']}
/>
</TouchableOpacity>
);
IMO this is the most prefered way for such case

Appium React Native Not Ready for Text Input

I've recently switched over to Appium + webdriverIO for E2E testing. Everything is working pretty well except for one test case relating to text input.
Basically, the component under test is a login screen that uses redux-form for form management. I'm constantly getting the error "'"login-field" Other' is not ready for a text input. Neither the accessibility element itself nor its accessible descendants have the input focus". The components are as follow:
SignInScreen.tsx
export class SignInScreen extends React.Component<any> {
render() {
const { handleSubmit, submitting, style } = this.props;
return (
<View style={style}>
<View>
<View>
<Field
name="login"
component={Input}
accessibilityLabel="login-field"
testID="login-field"
/>
<Field
secureTextEntry
name="password"
component={Input}
accessibilityLabel="password-field"
testID="password-field"
/>
</View>
</View>
</View>
);
}
}
Input.tsx
export class Input extends React.Component {
render() {
const {
input,
meta: { error, active, focused },
accessibilityLabel,
testID
} = this.props;
const showError = !active && !!error && !focused;
const errorText = "ERROR!"
return (
<View style={[style, styles.container]}>
<TextInput
autoCapitalize="none"
value={input.value}
onChangeText={input.onChange}
onFocus={input.onFocus}
onBlur={input.onBlur}
accessibilityLabel={accessibilityLabel},
testID={testID}
/>
<View style={{height: 30}}>
{showError && (
<Text>{errorText}</Text>
)}
</View>
</View>
);
}
}
SignInScreen.test.ts
describe('Sign In Screen Test', () => {
let client;
beforeAll(async () => {
// set up code
});
afterAll(async () => {
// tear down code
});
it('Can login', async () => {
const loginField = await client.$('~login-field');
await loginField.setValue('test#gmail.com'); // error here
const passwordField = await client.$('~password-field');
await passwordField.set('password' + '\n');
});
});
I do realize that the test cases work when I either add an additional <TextInput /> on top of the existing <TextInput /> component in the Input.tsx component as follows:
Input.tsx
export class Input extends React.Component {
render() {
const {
input,
meta: { error, active, focused },
accessibilityLabel,
testID
} = this.props;
const showError = !active && !!error && !focused;
const errorText = "ERROR!"
return (
<View style={[style, styles.container]}>
<TextInput />
<TextInput
autoCapitalize="none"
value={input.value}
onChangeText={input.onChange}
onFocus={input.onFocus}
onBlur={input.onBlur}
accessibilityLabel={accessibilityLabel},
testID={testID}
/>
<View style={{height: 30}}>
{showError && (
<Text>{errorText}</Text>
)}
</View>
</View>
);
}
}
or I remove the fixed height in the View component that nests the error message as follows:
Input.tsx
export class Input extends React.Component {
render() {
const {
input,
meta: { error, active, focused },
accessibilityLabel,
testID
} = this.props;
const showError = !active && !!error && !focused;
const errorText = "ERROR!"
return (
<View style={[style, styles.container]}>
<TextInput
autoCapitalize="none"
value={input.value}
onChangeText={input.onChange}
onFocus={input.onFocus}
onBlur={input.onBlur}
accessibilityLabel={accessibilityLabel},
testID={testID}
/>
<View>
{showError && (
<Text>{errorText}</Text>
)}
</View>
</View>
);
}
}
So what gives? I'm really lost as to what's causing Appium to not pick up the input focus without making the above adjustments.
I believe this is a recent bug with Appium - https://github.com/appium/java-client/issues/1386
You should NOT specify accessibilityLabel for ios separate props for ios and android like so try next workaround:
export default function testID(id) {
return Platform.OS === 'android'
? {
accessible : true,
accessibilityLabel: id,
}
: {
testID: id,
};
}
and then
<TextInput
{...otherProps}
{...testID('some-testID')}
/>

Loading Indicator while loading item

I am making react native expo app. And i have page where articles from database are displayed. When i click to button of article i go to page with article. When i click button i am sending all data of article to another screen. I want to load ActivityIndicator while data is displaying.
Help me please
Code:
class HomeScreen extends React.Component {
const { navigate } = this.props.navigation;
return (
<SafeAreaView style={styles.MainContainer}>
<ScrollView
>
<ListView
dataSource={this.state.dataSource}
renderSeparator={this.ListViewItemSeparator}
renderRow={rowData => (
<>
<TouchableOpacity style={{flex:2}}
activeOpacity={.7}
onPress={() => navigate('Articles', {
otherParam: rowData.article_title,
mobile_image: rowData.mobile_image,
article_category: rowData.article_category,
views: rowData.views,
article_content: rowData.article_content
})}>
<Image
source = {{ uri: rowData.mobile_image }}
style={{resizeMode:'cover',width:null,height:null, flex:1, borderRadius:4,
borderWidth:1,
borderColor:'#dddddd'}}
/>
<Text
style={styles.textOfArticle}
>
{rowData.article_title}
</Text>
</TouchableOpacity>
</>
)}
/>
</ScrollView
>
</SafeAreaView>
);
}
}
class ArticleScreen extends React.Component {
static navigationOptions = ({ navigation, navigationOptions }) => {
const { params } = navigation.state;
return {
title: params ? params.otherParam : '',
};
};
state = { animating: true }
componentDidMount = () => {
const { params } = this.props.navigation.state;
const article_title = params ? params.otherParam : '';
const mobile_image = params ? params.mobile_image : '';
const article_category = params ? params.article_category : '';
const views = params ? params.views : '';
const article_content = params ? params.article_content : '';
this.closeActivityIndicator()
}
render() {
const animating = this.state.animating
return (
<View style = {styles.container}>
<ActivityIndicator
animating = {animating}
color = '#bc2b78'
size = "large"
style = {styles.activityIndicator}/>
</View>
)
return (
<SafeAreaView>
<ScrollView>
<Text>{article_title}</Text>
</ScrollView>
</SafeAreaView>
);
}
}
Add a boolean value for activityIndicator, which will used to show or hide activityIndicator.
state = {
showIndicator: true,
}
closeActivityIndicator = () => {
this.setState({ showIndicator: false });
}
In your render use conditional rendering based on showIndicator value
render() {
const animating = this.state.animating;
return (
<SafeAreaView>
{this.state.showIndicator ? (
<View style={styles.container}>
<ActivityIndicator
animating={animating}
color="#bc2b78"
size="large"
style={styles.activityIndicator}
/>
</View>
) : (
<ScrollView>
<Text>{article_title}</Text>
</ScrollView>
)}
</SafeAreaView>
);
}