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

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);

Related

Why can't I navigate to my Screen using onPress={() => navigation.navigate('SaveProfileImage, { profileImage })}?

I'm developing an app where I have a screen called Add, other called Save, that I navigate between them using onPress={() => navigation.navigate('Save', { image })} inside a button, so I tried to do the same on another screen called profile that should navigate to the screen SaveProfileImage, but when I try this, I get the error TypeError: undefined is not an object (evaluating 'props.route.params') and I can't understand what can be so wrong in my codes:
Here is the Profile codes:
import React, { useState, useEffect } from 'react';
import { StyleSheet, View, Text, Image, FlatList, Button } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import firebase from 'firebase';
require('firebase/firestore');
import { connect } from 'react-redux';
function Profile({ navigation }, props) {
const [userPosts, setUserPosts] = useState([]);
const [user, setUser] = useState(null);
const [hasGalleryPermission, setHasGalleryPermission] = useState(null);
const [image, setImage] = useState(null);
useEffect(() => {
const { currentUser, posts } = props;
if (props.route.params.uid === firebase.auth().currentUser.uid) {
setUser(currentUser)
setUserPosts(posts)
}
else {
firebase.firestore()
.collection('users')
.doc(props.route.params.uid)
.get()
.then((snapshot) => {
if (snapshot.exists) {
setUser(snapshot.data());
}
else {
console.log('does not exist')
}
})
firebase.firestore()
.collection('posts')
.doc(props.route.params.uid)
.collection('userPosts')
.orderBy('creation', 'desc')
.get()
.then((snapshot) => {
let posts = snapshot.docs.map(doc => {
const data = doc.data();
const id = doc.id;
return { id, ...data }
})
setUserPosts(posts)
});
};
(async () => {
const galleryStatus = await ImagePicker.requestMediaLibraryPermissionsAsync();
setHasGalleryPermission(galleryStatus.status === 'granted');
})();
}, [props.route.params.uid, props.following]);
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 1,
});
if (!result.cancelled) {
setImage(result.uri);
};
};
if (hasGalleryPermission === false) {
return <View />;
};
if (hasGalleryPermission === false) {
return <Text>No access to gallery</Text>;
};
const onLogout = () => {
firebase.auth().signOut();
};
if (user === null) {
return <View />
}
return (
<View style={styles.container}>
{image && <Image source={{ uri: image }}
style={{ flex: 1 }} />}
<Image source={{ uri: props.route.params.profileImage }} />
<View style={styles.containerInfo}>
<Text>{user.name}</Text>
<Text>{user.state}</Text>
<Text>{user.city}</Text>
<Text>{user.email}</Text>
{props.route.params.uid !== firebase.auth().currentUser.uid}
<Button title='Pick Image From Gallery' onPress={() => pickImage()} />
<Button title='Save'
onPress={() => navigation.navigate('SaveProfileImage',
{ profileImage })} />
</View>
<View style={styles.container}>
<FlatList
numColumns={3}
horizontal={false}
data={userPosts}
renderItem={({ item }) => (
<View
style={styles.containerImage}>
<Image
style={styles.image}
source={{ uri: item.downloadURL }}
/>
</View>
)}
/>
</View>
<Button
title='Logout'
onPress={() => onLogout()}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
containerInfo: {
margin: 20
},
containerImage: {
flex: 1 / 3
},
image: {
flex: 1,
aspectRatio: 1 / 1
}
})
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts,
following: store.userState.following
})
export default connect(mapStateToProps, null)(Profile);
The app.js has a navigation container to control these screens:
<NavigationContainer >
<Stack.Navigator initialRouteName='Main'>
<Stack.Screen name='Main' component={MainScreen} />
<Stack.Screen name='Add' component={AddScreen}
navigation={this.props.navigation}/>
<Stack.Screen name='Save' component={SaveScreen}
navigation={this.props.navigation}/>
<Stack.Screen name='SaveProfileImage' component={SaveProfileImageScreen}
navigation={this.props.navigation}/>
<Stack.Screen name='Comment' component={CommentScreen}
navigation={this.props.navigation}/> </Stack.Navigator>
<NavigationContainer >
I can't understand why I can navigate from the AddScreen to the Save Screen, but I can't do the same between the profileScreen and the SaveProfileImage.
The save is this one:
import React, { useState } from 'react'
import { View, TextInput, Image, Button } from 'react-native'
import firebase from 'firebase'
require('firebase/firestore')
require('firebase/firebase-storage')
export default function Save(props) {
const [caption, setCaption] = useState('')
const uploadImage = async () => {
const uri = props.route.params.image;
const childPath =
`post/${firebase.auth().currentUser.uid}/${Math.random().toString(36)}`;
const response = await fetch(uri);
const blob = await response.blob();
const task = firebase
.storage()
.ref()
.child(childPath)
.put(blob);
const taskProgress = snapshot => {
console.log(`transferred: ${snapshot.bytesTransferred}`)
};
const taskCompleted = () => {
task.snapshot.ref.getDownloadURL().then((snapshot) => {
savePostData(snapshot);
})
};
const taskError = snapshot => {
console.log(snapshot)
};
task.on('state_changed', taskProgress, taskError, taskCompleted);
};
const savePostData = (downloadURL) => {
firebase.firestore()
.collection('posts')
.doc(firebase.auth().currentUser.uid)
.collection('userPosts')
.add({
downloadURL,
caption,
likesCount: 0,
estate: '',
city: '',
creation: firebase.firestore.FieldValue.serverTimestamp()
}).then((function () {
props.navigation.popToTop()
}))
};
return (
<View style={{ flex: 1 }}>
<Image source={{ uri: props.route.params.image }} />
<TextInput
placeholder='Write a Caption . . .'
onChangeText={(caption) => setCaption(caption)}
/>
<Button title='Save' onPress={() => uploadImage()} />
</View>
);
};
The SaveProfileScreen is this other one:
import React, { useState } from 'react'
import { View, TextInput, Image, Button } from 'react-native'
import firebase from 'firebase'
require('firebase/firestore')
require('firebase/firebase-storage')
export default function ProfileImage(props) {
const [caption, setCaption] = useState('')
const uploadImage = async () => {
const uri = props.route.params.image;
const childPath =
`profileImage/${firebase.auth().currentUser.uid}/
${Math.random().toString(36)}`;
console.log(childPath);
const response = await fetch(uri);
const blob = await response.blob();
const task = firebase
.storage()
.ref()
.child(childPath)
.put(blob);
const taskProgress = snapshot => {
console.log(`transferred: ${snapshot.bytesTransferred}`)
};
const taskError = snapshot => {
console.log(snapshot)
};
const taskCompleted = () => {
task.snapshot.ref.getDownloadURL().then((snapshot) => {
savePictureData(snapshot);
})
};
task.on('state_changed', taskProgress, taskError, taskCompleted);
};
return (
<View style={{ flex: 1 }}>
<Image source={{ uri: props.route.params.profileImage }} />
<Button title='Save' onPress={() => uploadImage()} />
</View>
);
};

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

Show user info in Profile bottom tab navigator after login successfully from Login screen?

I have a React Native project. My problem is that when login successfully from the Login screen, I navigate to the Home screen. Here I have a bottom navigator tab including Home and Profile. I want to show the user info in Profile but I don't know how to do it. Can anyone give me some ideas?
Here is my code
import React, {useState} from 'react';
import {
View,
StyleSheet,
Text,
Image,
ScrollView,
Dimensions,
TouchableOpacity,
TextInput,
Alert,
} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import Account from '../component/Account';
const {width: WIDTH} = Dimensions.get('window');
const Login = (props) => {
let [email, setEmail] = useState('');
let [password, setPassword] = useState('');
let [isFocused, setIsFocused] = useState(0);
const _onPress = (index) => {
setIsFocused(index);
};
const dangnhap = (params) => {
if (email.length == 0 || password.length == 0) {
Alert.alert('Thông Báo', 'Hãy nhập đầy đủ email và mật khẩu');
return;
}
try {
fetch('http://192.168.1.10:8080/api/auth/signin', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json ; charset=utf-8',
},
body: JSON.stringify(params),
})
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
if (responseJson.status == 200) {
props.navigation.push('Home', email);
props.navigation.navigate('Home');
} else {
Alert.alert('Thông báo', 'Đăng nhập thất bại');
}
});
} catch (error) {
console.log(error);
}
};
return (
<ScrollView style={{backgroundColor: 'white'}}>
<View style={styles.container}>
<Image
source={require('../assets/images/login.png')}
resizeMode="center"
style={styles.image}
/>
<Text style={styles.textTitle}>Chào mừng trở lại</Text>
<Text style={styles.textBody}>Đăng nhập vào tài khoản sẵn có</Text>
<View style={{marginTop: 10}} />
<View style={styles.inputContainer}>
<Icon
name="mail"
size={25}
color="#59B7C9"
style={styles.inputIcon}
/>
<TextInput
onChangeText={(value) => setEmail(value)}
placeholder="Email"
style={[
styles.input,
{borderColor: isFocused == 1 ? '#59B7C9' : 'black'},
]}
onFocus={() => _onPress(1)}
/>
</View>
<View style={styles.inputContainer}>
<Icon
name="lock-closed"
size={25}
color="#59B7C9"
style={styles.inputIcon}
/>
<TextInput
placeholder={'Mật khẩu'}
pass={true}
onChangeText={(value) => setPassword(value)}
style={[
styles.input,
{borderColor: isFocused == 2 ? '#59B7C9' : 'black'},
]}
secureTextEntry={true}
onFocus={() => _onPress(2)}
/>
</View>
<View style={{width: '90%'}}>
<TouchableOpacity
onPress={() => props.navigation.navigate('Forgot')}
style={([styles.textBody], {alignSelf: 'flex-end'})}>
<Text style={[styles.textBody, {color: 'blue'}]}>
Quên mật khẩu?
</Text>
</TouchableOpacity>
</View>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={styles.buttonLogin}
onPress={() => {
const newUser = {
Email: email,
Password: password,
};
dangnhap(newUser);
}}>
<Text style={styles.buttonText}>Đăng nhập</Text>
</TouchableOpacity>
</View>
<Text style={styles.textBody}>Hoặc kết nối với</Text>
<View style={{flexDirection: 'row'}}>
<Account color="#3b5c8f" icon="facebook" title="Facebook" />
<Account color="#ec482f" icon="google" title="Google" />
</View>
<View style={{flexDirection: 'row', marginVertical: 5}}>
<Text style={styles.textBody}>Chưa có tài khoản?</Text>
<Text
style={[styles.textBody, {color: 'blue'}]}
onPress={() => props.navigation.navigate('Signup')}>
Đăng ký
</Text>
</View>
</View>
</ScrollView>
);
};
This might help
import {AsyncStorage} from 'react-native';
if (responseJson.status == 200) {
AsyncStorage.setItem('email', 'email#email.com');
props.navigation.navigate('Home');
}
Profile.js
import { AsyncStorage } from 'react-native';
export default const Profile = props => {
const [email, setEmail] = useState('');
useEffect(() => {
retrieveData();
}, []);
const retrieveData = async () => {
const email = await AsyncStorage.getItem('email');
setEmail(email);
};
render(){
return {
<View>
<Text>{email}</Text>
</View>
}
}
}

onPress not working in React Native Flatlist

My onPress handler is not working when someone clicks on Flatlist item.
Video of this issue
https://u.pcloud.link/publink/show?code=XZWGOUkZmDLPeKQOQJJzxnqFB8Q21X3acT7k
Here is the code:
import React, { useState, useEffect } from 'react';
import { View, Text, Image, FlatList, ActivityIndicator } from 'react-native';
import { TouchableNativeFeedback } from 'react-native-gesture-handler';
import axios from 'axios';
export default function _AjaxApp() {
const [postList, setPostList] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [isLoading, setIsLoading] = useState(false);
const loadData = (append = false) => {
let url = "https://edristi.in/wp-json/wp/v2/posts?per_page=20&page=" + currentPage;
setIsLoading(true);
setCurrentPage(currentPage + 1);
axios.get(url).then((r) => {
if (append) {
setPostList(postList.concat(r.data));
} else {
setPostList(r.data);
}
setIsLoading(false);
}).catch((e) => {
console.log(e);
});
}
useEffect(() => {
loadData();
}, [])
let Loader = <></>
if (isLoading) {
Loader = <ActivityIndicator></ActivityIndicator>
}
return (
<View>
<View style={{padding:20, backgroundColor:"#4342fe"}}>
<Text style={{color:"white"}}>Edristi App</Text>
</View>
<FlatList
data={postList}
renderItem={({ item, index, separators }) => <PostCard postList={postList} {...item} index={index} />}
keyExtractor={r => r.id + "-" + Math.random().toString()}
removeClippedSubviews={true}
maxToRenderPerBatch={2}
ListFooterComponent={Loader}
onEndReachedThreshold={0.5}
onEndReached={() => {
loadData(true);
}}
/>
</View>
);
}
class PostCard extends React.PureComponent {
onPressHandler() {
console.log("Clicked");
alert("Clicked");
}
render() {
let image = <></>
if (this.props.jetpack_featured_media_url.trim() !== "") {
image = <Image style={{ flex: 1 }} source={{
//uri: this.props.featuredimage,
uri: this.props.jetpack_featured_media_url,
}} />
}
// console.log(this.props.jetpack_featured_media_url);
return <TouchableNativeFeedback onPress={()=>{
this.onPressHandler();
}}>
<View style={{ margin: 10 }}>
<Text style={{ fontSize: 17, lineHeight: 23, fontWeight: "600" }}>{ this.props.title.rendered}</Text>
</View></TouchableNativeFeedback>
}
}
Try to import 'TouchableNativeFeedback' from 'react-native' instead of 'react-native-gesture-handler'.

Is there a way to "fade in" items in flatlist?

I'm setting up a component that render a flatlist and I'm trying to make the flatlist items "fade in" animation For a more impressive display
This is a component for render search suggestions that show items
import React, { Component } from 'react'
import { View, Text, TextInput, FlatList, Image, TouchableOpacity } from 'react-native'
import { inject, observer } from 'mobx-react/native'
import { Button } from '../../components'
import Style from './style'
import I18n from '../../i18n'
import Icon from 'react-native-vector-icons/MaterialIcons'
import Api from '../../utils/Api'
import _ from 'lodash'
let mounted = false
#inject('UserStore', 'NavigationStore')
#observer
class SearchProducts extends Component {
constructor(props) {
super(props)
this.state = {
searchAutoComplete: [],
showAutoComplete: false,
currentSearch: '',
searchTimeoutId: null,
}
this.autoCompleteTimeout = null
this.storedResults = []
}
componentWillMount() {
mounted = true
}
componentWillUnmount() {
mounted = false
clearTimeout(this.autoCompleteTimeout)
}
_renderCategory = ({ item }) => {
return (
<View style={Style.featuredView}>
<Image source={item.image} style={Style.featuredImage} />
<Text style={{ textAlign: 'center', color: '#9B999A' }}>{item.title}</Text>
</View>
)
}
_renderSuggestion = ({ item, index }) => {
const splittedName = item.split(' ')
let splittedSearch = this.state.currentSearch.toUpperCase().split(' ')
splittedSearch = splittedSearch.map(x => x.trim()).filter(x => x.length > 1)
let suggestion = []
if (splittedSearch.length == 0) {
suggestion = splittedName.map((word, index) => <Text key={index}>{word} </Text>)
} else {
let highlightedWords = []
splittedName.forEach((word, index) =>
splittedSearch.forEach(wordFromSearch => {
const currentWord = word.toUpperCase()
const isAlreadyHighlighted = highlightedWords.includes(currentWord)
if ((currentWord.includes(wordFromSearch.toUpperCase()) && this.state.currentSearch.length > 0) || isAlreadyHighlighted) {
let v = (
<Text key={index} style={{ color: '#2eb872' }}>
{word}{' '}
</Text>
)
if (!isAlreadyHighlighted) {
highlightedWords.push(currentWord)
}
suggestion[index] = v
} else {
let v = <Text key={index}>{word} </Text>
suggestion[index] = v
}
})
)
}
return (
<TouchableOpacity
style={Style.suggestionView}
onPress={() => {
this.props.UserStore.addRecentSearch(item)
this.props.NavigationStore.navigate({ routeName: 'SearchResult', params: { search: item } })
this.autoCompleteTimeout = setTimeout(() => {
if (mounted) this.setState({ showAutoComplete: false })
}, 400)
}}
>
<Icon name='search' size={20} style={{}} />
<Text style={{ marginLeft: 20, textAlign: 'left', color: '#9B999A' }}>{suggestion}</Text>
</TouchableOpacity>
)
}
getSuggestions = async currentSearch => {
try {
const response = await Api.serachOutoCompleate(currentSearch)
let searchAutoComplete = response.suggestions.products.map(product => product.product_title)
response.suggestions.categories.forEach(categories => searchAutoComplete.push(categories))
response.suggestions.warehouses.forEach(warehouse => searchAutoComplete.push(warehouse.warehouse_name))
response.suggestions.upcs.forEach(upcs => searchAutoComplete.push(upcs.product_title))
response.suggestions.tags.forEach(tags => searchAutoComplete.push(tags.product_title))
this.storedResults[currentSearch] = searchAutoComplete
if (mounted && currentSearch && searchAutoComplete) this.setState({ currentSearch: currentSearch, searchAutoComplete: searchAutoComplete })
else this.setState({ currentSearch: currentSearch })
} catch (error) {
console.log(error)
}
}
_onSearchChange = _.debounce(currentSearch => {
if (currentSearch === '') {
this.setState({ filter: [], currentSearch })
} else {
if (this.storedResults[currentSearch]) {
this.setState({ currentSearch })
let searchAutoComplete = this.storedResults[currentSearch]
if (mounted && currentSearch && searchAutoComplete) this.setState({ searchAutoComplete })
} else {
this.getSuggestions(currentSearch)
}
}
}, 250)
render() {
I18n.locale = this.props.UserStore.user.lang
const recent = this.props.UserStore.RecentSearches
return (
<View style={Style.container}>
<View style={Style.search_container}>
<TextInput
style={Style.search_input}
underlineColorAndroid='transparent'
placeholder={I18n.t('search_products')}
returnKeyType='search'
autoCorrect={false}
onChangeText={this._onSearchChange}
onFocus={() => this.setState({ showAutoComplete: true })}
onSubmitEditing={event => {
if (event.nativeEvent.text.length) this.props.UserStore.addRecentSearch(event.nativeEvent.text)
this.props.NavigationStore.navigate({ routeName: 'SearchResult', params: { search: event.nativeEvent.text } })
}}
onKeyPress={() => this.suggestionTimeout && clearTimeout(this.suggestionTimeout)}
blurOnSubmit
/>
</View>
{this.state.currentSearch.length > 0 && this.state.showAutoComplete && this.state.searchAutoComplete.length > 0 ? (
<View style={{ paddingVertical: 10 }}>
<FlatList initialNumToRender={20} data={this.state.searchAutoComplete} keyExtractor={(item, index) => item.toString()} renderItem={this._renderSuggestion} keyboardShouldPersistTaps='always' />
</View>
) : (
<View>
{recent.length > 0 ? (
<View>
<View style={Style.whiteBorder} />
<View style={Style.searchHistory}>
<Text style={Style.searchHistory_header}>{I18n.t('recent_searches')}</Text>
{recent.map((title, index) => (
<Button
key={index}
style={Style.recentSearch}
onPress={() => {
this.props.UserStore.addRecentSearch(title)
this.props.NavigationStore.navigate({ routeName: 'SearchResult', params: { search: title } })
}}
>
<Icon name='schedule' style={Style.recentSearchIcon} />
<Text style={Style.recentSearchText}>{title}</Text>
</Button>
))}
</View>
</View>
) : null}
</View>
)}
</View>
)
}
}
export default SearchProducts
I expect the output will show fade in animation in the flatlist
and i dont know how to implement it
This can be accomplished by using the Animated api.
First import Animated from react-native, then add fade: new Animated.Value(0) to your state inside the constructor.
Now change the View that is surrounding the FlatList from this
<View style={{ paddingVertical: 10 }}>
to this
<Animated.View style={{ paddingVertical: 10, opacity: this.state.fade }}>
At last add this block to start the animation when the list mounts:
componentDidMount() {
Animated.timing(this.state.fade, {
duration: 500,
toValue: 1,
useNativeDrivers: true
}).start();
}
If you want to animate the FlatList in every time the user makes a search, you would have to move this block to another part of your program logic and remember to set the fade value back to 0 before the animation.