I'm working on a React Native app with a typeahead component. The typeahead displays options that overlay other content on the route (see right image below). When a user clicks one of those options, an onPress listener runs a function:
This all works just fine on iOS. On Android though, the onPress event is never received. Even more strangely, when I try to click on an option lower in the list (like Boston, MA, USA), the onPress event is received by the card below the pressed option (Djerba).
Does anyone know what might cause this behavior? I'd be super grateful for any insights others can offer on this query.
Here's the code for the Explore view and the typeahead components.
Explore.js
import React from 'react'
import { connect } from 'react-redux'
import { Text, View, ScrollView, TouchableOpacity } from 'react-native'
import { gradients, sizing } from '../../style'
import { LinearGradient } from 'expo-linear-gradient'
import { MountainHero } from '../Heros'
import { CardRow } from '../Card'
import Loading from '../Loading'
import { setExploreSearch, onExploreTypeaheadClick } from '../../actions/locations'
import { Typeahead } from '../Typeahead'
const styles = {
container: {
flex: 1,
flexDirection: 'column',
},
scrollView: {
paddingBottom: sizing.margin,
},
loadingContainer: {
position: 'absolute',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
zIndex: 100,
elevation: 100,
top: 53,
width: '100%',
},
typeahead: {
margin: sizing.margin,
marginBottom: 0,
width: sizing.screen.width - (2*sizing.margin),
zIndex: 100,
elevation: 100,
}
}
const Explore = props => {
const { authenticated: a, spotlight, loading } = props;
let r = (a.recommendedLocations || []);
if (!r || !spotlight) return null;
// remove spotlight locations from the recommended locations
const ids = spotlight.map(i => i.guid);
const recommended = r.filter(i => ids.indexOf(i.guid) == -1);
return (
<LinearGradient style={styles.container} colors={gradients.teal}>
<ScrollView contentContainerStyle={styles.scrollView}>
{loading && (
<View style={styles.loadingContainer}>
<Loading />
</View>
)}
<MountainHero text='Explore' />
<Typeahead
style={styles.typeahead}
placeholder='Search Cities'
value={props.exploreSearch}
onChange={props.setExploreSearch}
vals={props.exploreTypeahead}
valKey={'place_id'}
onTypeaheadClick={props.onExploreTypeaheadClick}
/>
<CardRow
text='Explore Places'
cards={recommended}
type='location' />
<CardRow
text='In the Spotlight'
cards={spotlight}
type='location' />
</ScrollView>
</LinearGradient>
)
}
const mapStateToProps = state => ({
authenticated: state.users.authenticated,
spotlight: state.locations.spotlight,
exploreSearch: state.locations.exploreSearch,
exploreTypeahead: state.locations.exploreTypeahead,
loading: state.locations.loading,
})
const mapDispatchToProps = dispatch => ({
setExploreSearch: s => dispatch(setExploreSearch(s)),
onExploreTypeaheadClick: val => dispatch(onExploreTypeaheadClick(val)),
})
export default connect(mapStateToProps, mapDispatchToProps)(Explore)
Typeahead.js
import React from 'react'
import { Text, View, TouchableOpacity } from 'react-native'
import { sizing, GradientInput } from '../style'
const styles = {
container: {
position: 'absolute',
zIndex: 100,
elevation: 100,
height: 400,
width: '100%',
},
input: {
width: '100%',
borderRadius: 0,
},
typeaheadContainer: {
position: 'absolute',
zIndex: 100,
elevation: 100,
top: 55,
width: '100%',
},
typeaheadRow: {
padding: 10,
paddingTop: 12,
paddingBottom: 12,
borderWidth: 1,
borderColor: '#eeeeee',
backgroundColor: '#ffffff',
marginBottom: -1,
},
typeaheadRowText: {
fontSize: 15,
fontFamily: 'open-sans',
lineHeight: 20,
backgroundColor: '#ffffff',
},
}
export const Typeahead = props => {
return (
<View style={[props.container, props.style]}>
<GradientInput style={styles.input}
placeholder={props.placeholder}
value={props.value}
onChange={props.onChange} />
<TypeaheadList vals={props.vals}
valKey={props.valKey}
onTypeaheadClick={props.onTypeaheadClick} />
</View>
)
}
export const TypeaheadList = props => {
if (!props.vals) return null;
return (
<View style={styles.typeaheadContainer}>
{props.vals.map(i => {
let text = i.text;
if (text.length > 31) text = text.substring(0,31) + '...';
return (
<TouchableOpacity activeOpacity={0.5} key={i[props.valKey]}
style={styles.typeaheadRow}
onPress={() => props.onTypeaheadClick(i[props.valKey])}>
<Text numberOfLines={1} style={styles.typeaheadRowText}>{text}</Text>
</TouchableOpacity>
)
})}
</View>
)
}
export default Typeahead
Try to move Typeahead component below all CardRow components and set position:absolute for Typeahead. Probably on android - the latest view shadow all views before (I am not sure, but I think you have to try it for next discovering issue).
You should also remove position: absolute from all but one component. Working code:
Explore.js
import React from 'react'
import { connect } from 'react-redux'
import { Text, View, ScrollView, TouchableOpacity } from 'react-native'
import { gradients, sizing } from '../../style'
import { LinearGradient } from 'expo-linear-gradient'
import { MountainHero } from '../Heros'
import { CardRow } from '../Card'
import Loading from '../Loading'
import { setExploreSearch, onExploreTypeaheadClick } from '../../actions/locations'
import { Typeahead } from '../Typeahead'
const styles = {
container: {
flex: 1,
flexDirection: 'column',
},
scrollView: {
paddingBottom: sizing.margin,
},
loadingContainer: {
position: 'absolute',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
zIndex: 1,
elevation: 1,
top: 53,
width: '100%',
},
topCardRow: {
paddingTop: sizing.margin + sizing.gradientInput.height,
},
typeahead: {
margin: sizing.margin,
marginBottom: 0,
width: sizing.screen.width - (2*sizing.margin),
zIndex: 1,
elevation: 1,
position: 'absolute',
top: sizing.mountainHero.height,
left: 0,
}
}
const Explore = props => {
const { authenticated: a, spotlight, loading } = props;
let r = (a.recommendedLocations || []);
if (!r || !spotlight) return null;
// remove spotlight locations from the recommended locations
const ids = spotlight.map(i => i.guid);
const recommended = r.filter(i => ids.indexOf(i.guid) == -1);
return (
<LinearGradient style={styles.container} colors={gradients.teal}>
<ScrollView contentContainerStyle={styles.scrollView}>
{loading && (
<View style={styles.loadingContainer}>
<Loading />
</View>
)}
<MountainHero text='Explore' />
<CardRow
style={styles.topCardRow}
text='Explore Places'
cards={recommended}
type='location' />
<CardRow
text='In the Spotlight'
cards={spotlight}
type='location' />
<Typeahead
style={styles.typeahead}
placeholder='Search Cities'
value={props.exploreSearch}
onChange={props.setExploreSearch}
vals={props.exploreTypeahead}
valKey={'place_id'}
onTypeaheadClick={props.onExploreTypeaheadClick}
/>
</ScrollView>
</LinearGradient>
)
}
const mapStateToProps = state => ({
authenticated: state.users.authenticated,
spotlight: state.locations.spotlight,
exploreSearch: state.locations.exploreSearch,
exploreTypeahead: state.locations.exploreTypeahead,
loading: state.locations.loading,
})
const mapDispatchToProps = dispatch => ({
setExploreSearch: s => dispatch(setExploreSearch(s)),
onExploreTypeaheadClick: val => dispatch(onExploreTypeaheadClick(val)),
})
export default connect(mapStateToProps, mapDispatchToProps)(Explore)
Typeahead.js
import React from 'react'
import { Text, View, TouchableOpacity } from 'react-native'
import { sizing, GradientInput } from '../style'
const styles = {
container: {
zIndex: 1,
elevation: 1,
height: 400,
width: '100%',
},
input: {
width: '100%',
borderRadius: 0,
},
typeaheadContainer: {
zIndex: 1,
elevation: 1,
top: 0,
width: '100%',
},
typeaheadRow: {
padding: 10,
paddingTop: 12,
paddingBottom: 12,
borderWidth: 1,
borderColor: '#eeeeee',
backgroundColor: '#ffffff',
marginBottom: -1,
zIndex: 1,
elevation: 1,
},
typeaheadRowText: {
fontSize: 15,
fontFamily: 'open-sans',
lineHeight: 20,
backgroundColor: '#ffffff',
},
}
export const Typeahead = props => {
return (
<View style={[props.container, props.style]}>
<GradientInput style={styles.input}
placeholder={props.placeholder}
value={props.value}
onChange={props.onChange} />
<TypeaheadList vals={props.vals}
valKey={props.valKey}
onTypeaheadClick={props.onTypeaheadClick} />
</View>
)
}
export const TypeaheadList = props => {
if (!props.vals) return null;
return (
<View style={styles.typeaheadContainer}>
{props.vals.map(i => {
let text = i.text;
if (text.length > 31) text = text.substring(0,31) + '...';
return (
<TouchableOpacity activeOpacity={0.5} key={i[props.valKey]}
style={styles.typeaheadRow}
onPress={() => props.onTypeaheadClick(i[props.valKey])}>
<Text numberOfLines={1} style={styles.typeaheadRowText}>{text}</Text>
</TouchableOpacity>
)
})}
</View>
)
}
export default Typeahead
Related
How to use react-native-vision-camera instead of expo-camera? earlier I was developing my project in expo but currently I am developing it using react native cli can anyone can tell me how can I use this package call react-native-vision-camera and make it work same as my expo code with any errors I tried using myself my it was giving me a black screen In my case android
Expo Code:
import { Camera } from 'expo-camera';
import { TouchableOpacity, Text, View, ActivityIndicator } from 'react-native';
import { useState, useEffect, useRef } from "react";
import * as FaceDetector from 'expo-face-detector';
export default function CameraPage({ navigation }) {
const [hasPermission, setHasPermission] = useState(null);
const cameraRef = useRef(null);
const [faceData, setFaceData] = useState([]);
useEffect(() => {
(async () => {
const { status } = await Camera.requestCameraPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
const handleTakePicture = async () => {
if (faceData.length === 0) {
alert('No Face')
}
else if
(cameraRef.current) {
const photo = await cameraRef.current.takePictureAsync();
console.log(photo.uri)
if (!photo.cancelled) {
navigation.navigate('addphoto', { Image: photo.uri });
}
}
}
const handleFacesDetected = ({ faces }) => {
setFaceData(faces);
}
return (
<View style={{ flex: 1, backgroundColor: 'black' }}>
<Camera
onFacesDetected={handleFacesDetected}
faceDetectorSettings={{
mode: FaceDetector.FaceDetectorMode.fast,
detectLandmarks: FaceDetector.FaceDetectorLandmarks.none,
runClassifications: FaceDetector.FaceDetectorClassifications.none,
minDetectionInterval: 100,
tracking: true,
}}
style={{
borderTopLeftRadius: 30,
borderTopRightRadius: 30,
borderBottomLeftRadius: 30,
borderBottomRightRadius: 30,
overflow: 'hidden',
width: '130%',
aspectRatio: 1,
}}
type={Camera.Constants.Type.front}
ref={cameraRef}
>
<View style={{ flex: 1, backgroundColor: 'transparent', flexDirection: 'row' }}>
</View>
</Camera>
<TouchableOpacity
style={{
alignSelf: 'center',
alignItems: 'center',
width: 90,
height: 90,
borderRadius: 500,
marginTop: '30%',
marginLeft: '5%',
borderColor: '#5A5A5A',
borderWidth: 6,
}}
onPress={handleTakePicture}
>
<View style={{ opacity: 0.5 }} />
</TouchableOpacity>
</View>
);
}
I am trying to create Height adjustable Views with React Native for an app I am building. I keep getting stuck on this one aspect. I am trying to create two stacked Views, with a line inbetween them so they are height adjustable when dragging the line up or down, adjusting content in it as well. Image below is a representation of what I am trying to make. "Home Option 2" is default state, "Home Option 1.3 is when the slider is dragged down, and "Home Option 1.2" is opposite - slider dragged up.
With an app bar at the bottom. (I dont have it made yet)
Any thoughts or help is appreciated!
Here is my code for App.tsx
import * as React from 'react';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, TouchableOpacity, View } from 'react-native';
import BottomSheet, { BottomSheetRefProps } from './components/BottomSheet';
import { useCallback, useRef } from 'react';
import MapView, { Marker, Geojson } from "react-native-maps";
import { PROVIDER_GOOGLE } from "react-native-maps";
export default function App() {
const ref = useRef<BottomSheetRefProps>(null);
const [topViewHeight, setTopViewHeight] = React.useState(0);
const onPress = useCallback(() => {
const isActive = ref?.current?.isActive();
if (isActive) {
ref?.current?.scrollTo(0);
} else {
ref?.current?.scrollTo(-200);
}
}, []);
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<View style={styles.mapViewContainer}>
<MapView
provider={PROVIDER_GOOGLE}
showsUserLocation={true}
style={styles.mapView}
initialRegion={{
latitude: 00.00 ,
longitude: -00.00 ,
latitudeDelta: 00.00 ,
longitudeDelta: 00.00 ,
}}
>
<Marker coordinate={{ latitude: 00.00, longitude: 00.00 }} />
</MapView>
</View>
<View style={styles.container}>
<StatusBar style="light" />
<TouchableOpacity style={styles.button} onPress={onPress} />
<BottomSheet ref={ref} {...{setTopViewHeight, topViewHeight}}>
<View style={{ flex: 1, backgroundColor: 'orange' }} />
</BottomSheet>
</View>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#111',
alignItems: 'center',
justifyContent: 'center',
},
button: {
height: 50,
borderRadius: 25,
aspectRatio: 1,
backgroundColor: 'white',
opacity: 0.6,
},
mapViewContainer: {
height: "50%",
width: "95%",
overflow: "hidden",
background: "transparent",
borderRadius: 13,
},
mapView: {
height: "100%",
width: "100%",
},
});
Code for BottomSheet.tsx (Which i was using as a reference for the ideal UX)
import { Dimensions, StyleSheet, Text, View } from 'react-native';
import React, { useCallback, useEffect, useImperativeHandle } from 'react';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
Extrapolate,
interpolate,
useAnimatedStyle,
useSharedValue,
withSpring,
withTiming,
} from 'react-native-reanimated';
const { height: SCREEN_HEIGHT } = Dimensions.get('window');
const TOP_VIEW_HEIGHT = 50;
const VIEW_RESIZE = 2.5;
const MAX_TRANSLATE_Y = -SCREEN_HEIGHT / VIEW_RESIZE;
type BottomSheetProps = {
children?: React.ReactNode;
setTopViewHeight: (height: number) => void;
topViewHeight: number;
};
export type BottomSheetRefProps = {
scrollTo: (destination: number) => void;
isActive: () => boolean;
};
const BottomSheet = React.forwardRef<BottomSheetRefProps, BottomSheetProps>(
({ children }, ref) => {
const translateY = useSharedValue(0);
const active = useSharedValue(false);
const scrollTo = useCallback((destination: number) => {
'worklet';
active.value = destination !== 0;
translateY.value = withSpring(destination, { damping: 50 });
}, []);
const isActive = useCallback(() => {
return active.value;
}, []);
useImperativeHandle(ref, () => ({ scrollTo, isActive }), [
scrollTo,
isActive,
]);
const context = useSharedValue({ y: 0 });
const gesture = Gesture.Pan()
.onStart(() => {
context.value = { y: translateY.value };
})
.onUpdate((event) => {
translateY.value = event.translationY + context.value.y;
translateY.value = Math.max(translateY.value, MAX_TRANSLATE_Y);
console.log(translateY.value);
})
.onEnd(() => {
if (translateY.value > -SCREEN_HEIGHT / 3) {
scrollTo(0);
} else if (translateY.value < -SCREEN_HEIGHT / 1.5) {
scrollTo(MAX_TRANSLATE_Y);
}
console.log('end: ' + translateY.value)
});
const rBottomSheetStyle = useAnimatedStyle(() => {
const borderRadius = interpolate(
translateY.value,
[MAX_TRANSLATE_Y + 50, MAX_TRANSLATE_Y],
[25, 5],
Extrapolate.CLAMP
);
return {
borderRadius,
transform: [{ translateY: translateY.value }],
maxHeight: 500,
};
});
return (
<GestureDetector gesture={gesture}>
<Animated.View style={[styles.bottomSheetContainer, rBottomSheetStyle] }>
<View style={styles.line} />
{children}
</Animated.View>
</GestureDetector>
);
}
);
const styles = StyleSheet.create({
bottomSheetContainer: {
minHeight: SCREEN_HEIGHT - 50,
width: '100%',
backgroundColor: 'white',
position: 'relative',
top: SCREEN_HEIGHT - 500,
borderRadius: 25,
},
line: {
width: 75,
height: 4,
backgroundColor: 'grey',
alignSelf: 'center',
marginVertical: 15,
borderRadius: 2,
},
});
export default BottomSheet;
The Bar component will have the GestureHandler tied to it. Interpolate yTranslation into a value between 0 and 1. The Bar component's SharedValue is passed as prop so that other components in its parent contain utilize it:
import {
StyleSheet,
ViewStyle,
Dimensions,
View,
useWindowDimensions,
} from 'react-native';
import Animated, {
SharedValue,
useAnimatedStyle,
interpolate,
withTiming,
} from 'react-native-reanimated';
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
type Props = {
anim: SharedValue<number>;
style?: ViewStyle;
};
const snapPoints = [0.2, 0.5, 0.8];
export default function Bar({ anim, style }: Props) {
const { height } = useWindowDimensions();
const gesture = Gesture.Pan()
.onUpdate((e) => {
// interpolate yTranslation to a value that snapPoints can work with
anim.value = interpolate(
e.translationY,
[-height * 0.5, height * 0.5],
[0, 1]
);
})
// snap to nearest point
.onEnd(() => {
const snapPoint = snapPoints.reduce((prev, curr) => {
const prevDist = Math.abs(prev - anim.value);
const currDist = Math.abs(curr - anim.value);
return prevDist < currDist ? prev : curr;
}, snapPoints[0]);
console.log('snapping to ', snapPoint);
// animate snapping to snapPoint
anim.value = withTiming(snapPoint);
});
return (
<GestureDetector gesture={gesture}>
<View style={styles.barContainer}>
<View style={styles.bar} />
</View>
</GestureDetector>
);
}
const styles = StyleSheet.create({
barContainer: {
backgroundColor: 'transparent',
width: '100%',
//padding to make bar easier to press
padding: 10,
justifyContent: 'center',
},
bar: {
backgroundColor: '#c4c4c4',
width: '80%',
height: 7,
alignSelf: 'center',
borderRadius: 25,
},
});
Now that translationY is a percentage it can be used to determine the amount of flex each view have:
import React from 'react';
import {
View,
StyleSheet,
} from 'react-native';
import Constants from 'expo-constants';
import Animated, {
useSharedValue,
useAnimatedStyle,
} from 'react-native-reanimated';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import SliderBar from './SliderBar';
import View1 from './View1';
import View2 from './View2';
import { footerHeight, ScreenWidth, ScreenHeight, MAX_FLEX } from './Constants';
export default function App() {
const barValue = useSharedValue(0.5);
const view1Style = useAnimatedStyle(() => {
return {
flex: barValue.value * MAX_FLEX,
};
});
const view2Style = useAnimatedStyle(() => {
return {
flex: Math.abs(barValue.value - 1) * MAX_FLEX,
};
});
return (
<GestureHandlerRootView
style={{ width: ScreenWidth, height: ScreenHeight }}>
<View style={styles.container}>
<Animated.View style={[styles.viewStyle, view1Style]}>
<View1 />
</Animated.View>
<SliderBar anim={barValue} />
<Animated.View style={[styles.viewStyle, view2Style]}>
<View2 />
</Animated.View>
<View style={styles.footer} />
</View>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
padding: 8,
margin: 5,
},
viewStyle: {
backgroundColor: '#c4c4c4',
flex: 1,
marginVertical: 10,
borderRadius: 10,
},
footer: {
backgroundColor: '#6f6f6f',
height: footerHeight,
borderRadius: 10,
},
});
Demo
I made a reusable component Button.js and I'm importing it on two different screens. The button looks the same, but on the first screen I need it to be position: 'absolute' and on the second one position: 'relative' (the default).
How do I add the position to be absolute on the first screen? I tried to add the styling on FirstPage.js but it does not work. How do I overwrite the style that is defined in Button.js?
Button.js:
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
const Button = ({ position, onPress, children }) => {
const { buttonStyle, textStyle } = styles;
return (
<TouchableOpacity onPress={onPress} style={buttonStyle, {'position': position}}>
<Text style={textStyle}>{children}</Text>
</TouchableOpacity>
);
};
Button.defaultProps = {
position: 'relative',
}
const styles = {
textStyle: {
alignSelf: 'center',
color: '#F44336',
fontSize: 16,
},
buttonStyle: {
zIndex: 2,
width: 100,
backgroundColor: '#FFF',
}
};
export { Button };
You can pass props, something like this (Button.js) (edited according to posted code):
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
const Button = ({ position, onPress, children }) => {
const { buttonStyle, textStyle } = styles;
const style = {...buttonStyle, position };
return (
<TouchableOpacity onPress={onPress} style={style}>
<Text style={textStyle}>{children}</Text>
</TouchableOpacity>
);
};
Button.defaultProps = {
position: 'relative',
}
const styles = {
textStyle: {
alignSelf: 'center',
color: '#F44336',
fontSize: 16,
},
buttonStyle: {
zIndex: 2,
width: 100,
backgroundColor: '#FFF',
}
};
export { Button };
Your button of course looks different, this is just an outline of what you could do (basically just using props).
This is REUSABLE Button as touchableOpacity.
export const NormalThemeButton = (props) => {
return (
<TouchableOpacity
style={[{ ...props.style, ...styles.normalThemeBtn }]}
style={[{ alignItems: props.align }, styles.anchor]}
onPress={props.onPress} >
<CustomText text={props.text} l bold style={{
textAlign: 'center',
color: theme['blackColor']
}} />
{props.children}
</TouchableOpacity >
);
};
This is where this RESUABLE Button is been used.
style={{ ...styles.openArrivedButton }}
text={Lng.instance.translate('useWaze')}
onPress={() => { Waze() }}/>
Hope You find it helpful.
I have two classes, ActivityList, and ActivityDetail. ActivityList queries for some data and then calls ActivityDetail with the information it receives. I console logged the information and checked to see if it is coming in the desired form. It is. However, for some reason ActivityDetal is not rendering into ActivityList.
ActivityList.js
import React, { Component } from 'react';
import { ScrollView } from 'react-native';
import firebase from 'firebase';
import ActivityDetail from './ActivityDetail';
import { getCurrentUser } from '../../models/User';
class ActivityList extends Component {
getActivityList() {
getCurrentUser()
.then(currentUser => currentUser.teacherList.map(batch => firebase.database().ref(`/batches/${batch}`)
.once('value')
.then(Class => firebase.database().ref(`/users/teachers/${Class.val().Teacher}`)
.once('value')
.then(teacher => this.renderActivityList(teacher.val())))));
}
renderActivityList(teacher) {
console.log(teacher);
return <ActivityDetail key={teacher.Name} person={teacher} />;
}
render() {
return (
<ScrollView>
{this.getActivityList()}
</ScrollView>
);
}
}
export { ActivityList };
ActivityDetail.js
import React from 'react';
import { Text, Image, View, Dimensions } from 'react-native';
import { Card, CardSection } from './';
const { width } = Dimensions.get('window');
const ActivityDetail = ({ person }) => {
console.log('working');
return (
<Card style={{ flex: 1, flexDirection: 'row' }}>
<CardSection>
<Image style={styles.headerStyle} source={{ uri: person.Header }} />
<View style={styles.containerStyle}>
<Image style={styles.profileStyle} source={{ uri: person.Profile }} />
</View>
<View style={{ width: 0.93 * width, height: 0.09 * width }} />
</CardSection>
<CardSection>
<View style={styles.textContainerStyle}>
<Text style={styles.nameStyle}>{person.Name}</Text>
<Text style={styles.classStyle}>{person.Subject}</Text>
</View>
</CardSection>
</Card>
);
};
const styles = {
profileStyle: {
height: 0.13 * width,
width: 0.13 * width,
alignSelf: 'center',
resizeMode: 'cover',
},
containerStyle: {
width: 0.18 * width,
height: 0.18 * width,
borderRadius: (0.18 * width) / 2,
backgroundColor: '#d5d5d5',
paddingLeft: 5,
paddingRight: 5,
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'center',
top: 65,
position: 'absolute',
},
nameStyle: {
fontSize: 20,
color: '#000',
paddingBottom: 5,
},
headerStyle: {
width: 0.93 * width,
height: 0.25 * width,
borderRadius: 4,
flex: 2,
},
textContainerStyle: {
justifyContent: 'center',
alignItems: 'center',
flex: 1,
},
classStyle: {
fontSize: 18,
color: '#aaa',
},
};
export default ActivityDetail;
The problem here is that in your ActivityList component's render function, you are rendering the result of this.getActivityList, which is always gonna be undefined.
You should instead trigger the data fetch in componentWillMount and use setState to set the data to the component state for rendering.
e.g.
class ActivityList extends Component {
constructor(props) {
super(props);
this.state = { teacher: {} };
}
componentWillMount() {
this.getActivityList();
}
getActivityList() {
... // get Data
this.setState({ teacher : data });
}
render() {
return (
<ScrollView>
<ActivityDetail key={this.state.teacher.Name} person={this.state.teacher} />
</ScrollView>
);
}
}
I am trying with react native , I have studied about it and now I am trying to learn it , This is my first day in this , I got some hints that how does it work form bellow mentioned tutorial.
https://www.tutorialspoint.com/react_native/react_native_text_input.htm
I am just trying with login page and handling the submit button click, just want to capture input data and want to app it to my Button.js file.
I know it may looks silly to you all there, but I really want to know it , I studied about props and was trying to use that in the same but when I replace component with props I start getting red screen , Please guide me little on this that if in case we are having two different components then how do we can pass our data between them.
Here I am posting both of the JS files :-
Form.js
import React, { Component, PropTypes } from 'react';
import Dimensions from 'Dimensions';
import {
StyleSheet,
KeyboardAvoidingView,
View,
ActivityIndicator,
TouchableOpacity,
Image,
} from 'react-native';
import UserInput from './UserInput';
import ButtonSubmit from './ButtonSubmit';
import SignupSection from './SignupSection';
import usernameImg from '../images/username.png';
import passwordImg from '../images/password.png';
import eyeImg from '../images/eye_black.png';
export default class Form extends Component {
constructor(props) {
super(props);
this.state = {
showPass: true,
press: false,
username: '',
password: ''
};
this.showPass = this.showPass.bind(this);
this.handleChange = this.handleChange.bind(this);
}
showPass() {
this.state.press === false ? this.setState({ showPass: false, press: true }) :this.setState({ showPass: true, press: false });
}
handleChange(event) {
// this.setState({usernamevalue: event.target.usernamevalue , passwordvalue : event.target.passwordvalue });
alert('A name was submitted: ' + this.state.password);
}
render() {
return (
<KeyboardAvoidingView behavior='padding'
style={styles.container}>
<UserInput source={usernameImg}
placeholder='Username'
autoCapitalize={'none'}
returnKeyType={'done'}
value={this.state.username}
onChangeText={(text) => this.setState({username:text})}
autoCorrect={false} />
<UserInput source={passwordImg}
secureTextEntry={this.state.showPass}
placeholder='Password'
returnKeyType={'done'}
value={this.state.password}
onChangeText={(text) => this.setState({password:text})}
autoCapitalize={'none'}
autoCorrect={false} />
<TouchableOpacity
activeOpacity={0.7}
style={styles.btnEye}
onPress={this.handleChange}
>
<Image source={eyeImg} style={styles.iconEye} />
</TouchableOpacity>
</KeyboardAvoidingView>
);
}
}
const DEVICE_WIDTH = Dimensions.get('window').width;
const DEVICE_HEIGHT = Dimensions.get('window').height;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
},
btnEye: {
position: 'absolute',
top: 55,
right: 28,
},
iconEye: {
width: 25,
height: 25,
tintColor: 'rgba(0,0,0,0.2)',
},
});
ButtonSubmit.JS
import React, { Component, PropTypes } from 'react';
import Dimensions from 'Dimensions';
import {
StyleSheet,
TouchableOpacity,
Text,
Animated,
Easing,
Image,
Alert,
View,
} from 'react-native';
import { Actions, ActionConst } from 'react-native-router-flux';
import spinner from '../images/loading.gif';
const DEVICE_WIDTH = Dimensions.get('window').width;
const DEVICE_HEIGHT = Dimensions.get('window').height;
const MARGIN = 40;
export default class ButtonSubmit extends Component {
constructor() {
super();
this.state = {
isLoading: false,
};
this.buttonAnimated = new Animated.Value(0);
this.growAnimated = new Animated.Value(0);
this._onPress = this._onPress.bind(this);
}
_onPress() {
if (this.state.isLoading) return;
this.setState({ isLoading: true });
Animated.timing(
this.buttonAnimated,
{
toValue: 1,
duration: 200,
easing: Easing.linear
}
).start();
setTimeout(() => {
this._onGrow();
}, 2000);
setTimeout(() => {
Actions.secondScreen();
this.setState({ isLoading: false });
this.buttonAnimated.setValue(0);
this.growAnimated.setValue(0);
}, 2300);
}
_onGrow() {
Animated.timing(
this.growAnimated,
{
toValue: 1,
duration: 200,
easing: Easing.linear
}
).start();
}
render() {
const changeWidth = this.buttonAnimated.interpolate({
inputRange: [0, 1],
outputRange: [DEVICE_WIDTH - MARGIN, MARGIN]
});
const changeScale = this.growAnimated.interpolate({
inputRange: [0, 1],
outputRange: [1, MARGIN]
});
return (
<View style={styles.container}>
<Animated.View style={{width: changeWidth}}>
<TouchableOpacity style={styles.button}
onPress={this._onPress}
activeOpacity={1} >
{this.state.isLoading ?
<Image source={spinner} style={styles.image} />
:
<Text style={styles.text}>LOGIN</Text>
}
</TouchableOpacity>
<Animated.View style={[ styles.circle, {transform: [{scale: changeScale}]} ]} />
</Animated.View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
top: -95,
alignItems: 'center',
justifyContent: 'flex-start',
},
button: {
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#F035E0',
height: MARGIN,
borderRadius: 20,
zIndex: 100,
},
circle: {
height: MARGIN,
width: MARGIN,
marginTop: -MARGIN,
borderWidth: 1,
borderColor: '#F035E0',
borderRadius: 100,
alignSelf: 'center',
zIndex: 99,
backgroundColor: '#F035E0',
},
text: {
color: 'white',
backgroundColor: 'transparent',
},
image: {
width: 24,
height: 24,
},
});
Should I use something like this.props.state.username to pass data to SubmitButton.js file.
The above code is from :- https://github.com/dwicao/react-native-login-screen
I am playing with this demo to understand the flow and concepts, Please provide me some suggestions here.
Your little help would be very much appreciated
Regards.