How can conditional rendering for array map - javascript

I have condition for profile page
I want to show user profile data
name . age etc..So I map array to get each user data
but problem is when there is no data in firestore , this profile page is empty
this is really disappointed
I want to conditional rendering the following rendering
But error is
jsx expression must have one parent element
so how can render this correctly
return (
<SafeAreaView style={styles.container}>
{values.map((value)=>(
<React.Fragment key={value.id}>
<View style={styles.header}></View>
<Image style={styles.avatar} source={{uri : value.photo}}/>
<View style={styles.body}>
<View style={styles.bodyContent}>
<Text style={styles.name}>{value.displayName}</Text>
<View style={styles.label}>
<Text >My Job is - </Text>
<Text>{value.job}</Text>
</View>
<View style={styles.label}>
<Text>My Age is - </Text>
<Text >{value.age}</Text>
</View>
<View style={styles.label}>
<Text>My Gender is - </Text>
<Text >{value.gender}</Text>
</View>
<View style={styles.label}>
<Text>I want to meet - </Text>
<Text >{value.interestIn}</Text>
</View>
</View>
</View>
</React.Fragment>
))}
<View style={tw("p-10 flex-row items-center justify-between px-5")}>
<Pressable
style={
tw('p-3 rounded-xl bg-red-400')} onPress={goedit}>
<Text style={styles.text}>Edit Profile</Text>
</Pressable>
<Pressable
style={
tw('p-3 rounded bg-red-400')} onPress={logout}>
<Text style={styles.text}>LOGOUT</Text>
</Pressable>
</View>
</SafeAreaView>
)
I want to show user profile page as template even no data in firestore
can someone help me

Just add a <> ontop of the map and then </> on the bottom to close it. If you need flex then you can switch out the <></> for

You could try something like this, but paste your logic inside if statement:
const renderValues = () => {
values.map((value) => {
if (value) {
return (
<View>
<Text>data</Text>
</View>
);
} else {
return <View>no data</View>;
}
});
};
return <>{renderValues()}</>;

const renderValues = () => {
values.map((value) => {
if (value) {
return (
<React.Fragment key={value.id}>
<View style={styles.header} />
<Image style={styles.avatar} source={{ uri: value.photo }} />
<View style={styles.body}>
<View style={styles.bodyContent}>
<Text style={styles.name}>{value.displayName}</Text>
<View style={styles.label}>
<Text>My Job is - </Text>
<Text>{value.job}</Text>
</View>
<View style={styles.label}>
<Text>My Age is - </Text>
<Text>{value.age}</Text>
</View>
<View style={styles.label}>
<Text>My Gender is - </Text>
<Text>{value.gender}</Text>
</View>
<View style={styles.label}>
<Text>I want to meet - </Text>
<Text>{value.interestIn}</Text>
</View>
</View>
</View>
</React.Fragment>
);
} else {
return <View>no data</View>;
}
});
};
return (
<SafeAreaView style={styles.container}>
<>
{renderValues()}
<View style={tw('p-10 flex-row items-center justify-between px-5')}>
<Pressable style={tw('p-3 rounded-xl bg-red-400')} onPress={goedit}>
<Text style={styles.text}>Edit Profile</Text>
</Pressable>
<Pressable style={tw('p-3 rounded bg-red-400')} onPress={logout}>
<Text style={styles.text}>LOGOUT</Text>
</Pressable>
</View>
</>
</SafeAreaView>
);

Related

React native : Flatlist inside scrollview

My goal is for this entire block to be scrollable.
I tried all kinds of ways to achieve the goal but without success.
I tried with ListHeaderComponent and moved the entire top view to it and it didn't work.
And I also tried <FlatList nestedScrollEnabled />
And it didn't work either.
What is the correct way to reach the scroll?
I come from here :
const renderAccordians = () => {
const items: JSX.Element[] = [];
areaData.forEach(item => {
items.push(<Accordian item={item} key={item.title} />);
});
return items;
};
To here :
return (
<View>
<View style={styles.row}>
<TouchableOpacity onPress={() => onClickFather()}>
<MaterialIcons size={24} name={data.checked ? 'check-box' : 'check-box-outline-blank'} color={'black'} />
</TouchableOpacity>
<Text style={[styles.title]}>{data.title}</Text>
<TouchableOpacity style={styles.row} onPress={() => toggleExpand()}>
<MaterialIcons name={expanded ? 'arrow-drop-up' : 'arrow-drop-down'} size={30} color={'black'} />
</TouchableOpacity>
</View>
<View style={styles.parentHr} />
{expanded && (
<FlatList
data={data.data}
numColumns={1}
scrollEnabled={false}
renderItem={({ item, index }) => (
<View>
<TouchableOpacity style={[styles.childRow, styles.button]} onPress={() => onClick(index)}>
<MaterialIcons
size={24}
name={item.checked ? 'check-box' : 'check-box-outline-blank'}
color={'black'}
/>
<Text style={[styles.itemInActive]}>{item.key}</Text>
</TouchableOpacity>
<View style={styles.childHr} />
</View>
)}
/>
)}
</View>
);
Since your FlatList will be part of an Accordion component, you "can't" embed the ExpandButton inside the Flatlist > ListHeaderComponent ... cause It'll simply hide the whole FlatList along with it's Header when you collapse your accorddion...
keyExtractor is also missing in your FlatList .. I added index as a key here which is not recommended BTW, you better use a unique field in your listItem like id...
return (
<View style={{ flex: 1}}> // <<--- Look here
<View style={styles.row}>
<TouchableOpacity onPress={() => onClickFather()}>
<MaterialIcons
size={24}
name={data.checked ? 'check-box' : 'check-box-outline-blank'}
color={'black'}
/>
</TouchableOpacity>
<Text style={[styles.title]}>{data.title}</Text>
<TouchableOpacity style={styles.row} onPress={() => toggleExpand()}>
<MaterialIcons
name={expanded ? 'arrow-drop-up' : 'arrow-drop-down'}
size={30}
color={'black'}
/>
</TouchableOpacity>
</View>
<View style={styles.parentHr} />
{expanded && (
<FlatList
data={data.data}
numColumns={1}
scrollEnabled={true} // <<--- Look here
keyExtractor={(_, index) => index.toString()} // <<=== Look here
contentContainerStyle={{flexGrow: 1}} // <<--- Look here
renderItem={({ item, index }) => (
<View>
<TouchableOpacity
style={[styles.childRow, styles.button]}
onPress={() => onClick(index)}
>
<MaterialIcons
size={24}
name={item.checked ? 'check-box' : 'check-box-outline-blank'}
color={'black'}
/>
<Text style={[styles.itemInActive]}>{item.key}</Text>
</TouchableOpacity>
<View style={styles.childHr} />
</View>
)}
/>
)}
</View>
);
If it does not work, I think you should create a component and use map datalist to render all the items and putting them into the ScrollView tag.
<ScrollView
style={styles.messageContain}
ref={ref => {
this.scrollView = ref;
}}
{data.data.map((item, index) => {
return <YourComponent key={index} data={item} />;
})}
</ScrollView>

localStorage doesn't work in TouchableOpacity tag

I want to use localStorage and move to another page at the same time, but only moving work, I can't get the value
const pressHandlerMapTest = () => {
navigation.navigate("TestMapScreen");
};
return (
<ImageBackground style={styles.background}>
<View style={styles.tourWindow}>
<TouchableOpacity underlayColor="red"
onPress={pressHandlerMapTest}
onPressIn={() => {
localStorage.setItem('tour', 'others');
}}>
<Image source={require("../assets/royals.png")} ></Image>
</TouchableOpacity>
</View>
</ImageBackground>
);
const pressHandlerMapTest = () => {
localStorage.setItem('tour', 'others');
navigation.navigate("TestMapScreen");
};
return (
<ImageBackground style={styles.background}>
<View style={styles.tourWindow}>
<TouchableOpacity underlayColor="red"
onPress={pressHandlerMapTest}
>
<Image source={require("../assets/royals.png")} ></Image>
</TouchableOpacity>
</View>
</ImageBackground>
);

can't submit a form when using FlatLists

Previously, I was rendering and mapping some data using another component called UserList. However, now I have changed it to a FlatList
When I was using {/* <UserList onSendRequest={onSendRequest} data={userData}></UserList> */}, every time I clicked on the button, the handleSubmit function was called successfully.
However, now I am using FlatList. Nothing happens when I click on the button. The handle submit does not run and I don't see anything from the flatList. What am I doing wrong?
return (
<SafeAreaView>
<View>
<View style={styles.searchTopContainer}>
<View style={styles.searchTopTextContainer}>
<Text>
Cancel
</Text>
</View>
<View>
<Formik
initialValues={initialValues}
onSubmit={handleSubmitForm}
validationSchema={validationSchema}>
{({ handleChange, handleBlur, handleSubmit, values }) => (
<View style={styles.searchFieldContainer}>
<View style={styles.form}>
<FieldInput
handleChange={handleChange}
handleBlur={handleBlur}
value={values.input}
fieldType="input"
icon="user"
placeholderText="Email or Phone Number or Name"
/>
<ErrorMessage
name="input"
render={(msg) => (
<Text style={styles.errorText}>{msg}</Text>
)}
/>
</View>
<View style={styles.buttonContainer}>
<Button
rounded
style={styles.searchButton}
onPress={handleSubmit}>
<Text style={styles.searchButtonText}>Search </Text>
</Button>
</View>
</View>
)}
</Formik>
</View>
{/* <UserList onSendRequest={onSendRequest} data={userData}></UserList> */}
{userData !== null &&
<FlatList
data={userData.users}
horizontal={false}
renderItem={({ item }) => (
<SingleUser
user={item}
onSendRequest={onSendRequest}
/>
)}
keyExtractor={(item) => item.id.toString()}
/>
}
</View>
</View>
</SafeAreaView>
);
};
UserList.tsx:
export const UserList: React.FunctionComponent<UserProps> = ({
data,
onSendRequest,
}) => {
if (!data) return null;
return (
<View style={styles.users}>
{data.users.nodes.map(
(item: { firstName: string; lastName: string; id: number }) => {
const userName = item.firstName.concat(' ').concat(item.lastName);
return (
<View style={styles.item} key={item.id}>
<Thumbnail
style={styles.thumbnail}
source={{
uri:
'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/afro_woman_female_person-512.png',
}}></Thumbnail>
<Text style={styles.userName}>{userName}</Text>
<View style={styles.addButtonContainer}>
<Button
rounded
style={styles.addButton}
onPress={() => onSendRequest(Number(item.id))}
>
<Icon name="plus" size={moderateScale(20)} color="black" />
</Button>
</View>
</View>
);
},
)}
</View>
);
};

Animate the dynamic height of a view on scroll

I have a UI similar to the image below:
The Title section (the section in dark blue) is supposed to be fixed and the Body section is scrollable.
The height of the Title section would be dynamic as there can be 4 - 5 bullet points retrieved from the server.
My task is to start reducing the height of the Title section when the user starts scrolling upwards to read the content that's present beneath until the point where the user can only read the title in the header. Also, when the user starts scrolling downwards to the point where he/she can see the heading Body, I am supposed to start increasing the height so that he can again start seeing all the bullet points.
This feature is more of like hiding/showing the header on the scroll. But, the title will always be visible.
I have written the below-written code to achieve the same:
import React from 'react';
import {
View,
Text,
ScrollView,
TouchableOpacity,
Linking,
Image,
Animated,
} from 'react-native';
class App extends React.PureComponent<Props, States> {
constructor() {
super();
this.state = {
headerHeight: 0,
headerTitleHeight: 0,
};
this.scrollY = new Animated.Value(0);
}
render() {
const { navigation, pending, macroFeed } = this.props;
const { headerHeight, headerTitleHeight } = this.state;
const { themeDetails } = navigation.state.params;
const {
title, bullet1, bullet2, bullet3,
} = themeDetails;
let headerStyle = {};
if (headerHeight) {
headerStyle = {
height: this.scrollY.interpolate({
inputRange: [0, headerHeight - headerTitleHeight],
outputRange: [headerHeight, headerTitleHeight],
extrapolate: 'clamp',
}),
};
}
const sortedMacroFeed = _.sortBy(macroFeed, (o) => moment(o.date).format('YYYYMMDD')).reverse().slice(0, 5);
if (pending) {
return (
<View style={styles.maxFlex}>
<LoadingSpinner
size="large"
containerStyle={styles.loadingSpinner}
/>
</View>
);
}
return (
<View
style={styles.maxFlex}
>
<Animated.View
style={[styles.headerWrapper, headerStyle]}
onLayout={(event) => {
this.setState({
headerHeight: event.nativeEvent.layout.height,
});
}}
>
<View style={styles.macroBgWrapper}>
<Image source={themeDetails.imgUrl} style={styles.macroBg} />
<View style={styles.macroBgOverlay} />
</View>
<View
style={styles.header}
onLayout={(event) => {
this.setState({
headerTitleHeight: event.nativeEvent.layout.height,
});
}}
>
<TouchableOpacity onPress={() => navigation.goBack()}>
<View>
<Icon name="ios-arrow-back" size={32} style={styles.backIcon} />
</View>
</TouchableOpacity>
<View style={styles.titleWrap}>
<Text style={styles.headerTitle}>
{title}
</Text>
</View>
</View>
<View style={styles.bulletWrapper}>
{
!!bullet1 && (
<View style={styles.column}>
<View style={styles.row}>
<View style={styles.bullet}>
<Text style={styles.buttetListText}>
{'\u2022'}
{' '}
</Text>
</View>
<View style={styles.bulletText}>
<Text style={styles.buttetListText}>
{bullet1}
</Text>
</View>
</View>
</View>
)
}
{
!!bullet2 && (
<View style={styles.column}>
<View style={styles.row}>
<View style={styles.bullet}>
<Text style={styles.buttetListText}>
{'\u2022'}
{' '}
</Text>
</View>
<View style={styles.bulletText}>
<Text style={styles.buttetListText}>
{bullet2}
</Text>
</View>
</View>
</View>
)
}
{
!!bullet3 && (
<View style={styles.column}>
<View style={styles.row}>
<View style={styles.bullet}>
<Text style={styles.buttetListText}>
{'\u2022'}
{' '}
</Text>
</View>
<View style={styles.bulletText}>
<Text style={styles.buttetListText}>
{bullet3}
</Text>
</View>
</View>
</View>
)
}
{
!bullet1 && !bullet2 && !bullet3 && (
<View style={styles.noBulletWrapper}>
<Text style={styles.noBulletPoints}>
No description found.
</Text>
</View>
)
}
</View>
</Animated.View>
<ScrollView
style={styles.maxFlex}
showsVerticalScrollIndicator={false}
onScroll={Animated.event([
{ nativeEvent: { contentOffset: { y: this.scrollY } } },
])}
scrollEventThrottle={16}
>
<View style={[styles.section, styles.wrapGutter]}>
<Text style={styles.sectionTitle}>
Recent Headlines
</Text>
{
sortedMacroFeed.map((feed) => (
<View key={feed.id} style={styles.newsSection}>
<Text style={styles.newsHours}>
{moment(feed.date).fromNow()} | {feed.author}
</Text>
<Text style={styles.newsTitle}>
{feed.title}
<Text onPress={() => this.openWebView(feed.url)}>
<EvilIcons name="external-link" size={16} style={styles.externalLinkIcon} />
</Text>
</Text>
</View>
))
}
</View>
<View style={styles.section}>
<View style={[styles.wrapGutter, styles.sectionTitleWrap]}>
<Text style={styles.sectionTitle}>
Exposure
</Text>
<View style={styles.totalNavWrap}>
<Text style={styles.totalNav}>
$467M
</Text>
<Text style={styles.totalNavLabel}>
Total NaV (all portfolios)
</Text>
</View>
</View>
<Tabs
tabsData={TABS_DATA}
renderTabContent={this.renderTabContent}
tabName="title"
hideIfOneTab
/>
</View>
</ScrollView>
</View>
);
}
}
If you have observed I am trying to retrieve the dynamic height of the title section using onLayout.
This code only works one way i.e when I scroll up. The height of the title section reduces to the point where only the title can be seen and the bullet points get hidden. But after that, I cannot scroll down. The height gets permanently reduced.
Now, if I change the below-given code:
let headerStyle = {};
if (headerHeight) {
headerStyle = {
height: this.scrollY.interpolate({
inputRange: [0, headerHeight - headerTitleHeight],
outputRange: [headerHeight, headerTitleHeight],
extrapolate: 'clamp',
}),
};
}
to
const headerStyle = {
height: this.scrollY.interpolate({
inputRange: [0, 240],
outputRange: [300, 60],
extrapolate: 'clamp',
}),
};
everything seems to work fine. Basically, if I stop retrieving the value of the height dynamically and provide a static value like 240 or something, everything seems to work fine.
But, animation on scroll stops if I accept dynamic height. Any help to solve this would be much appreciated. Thanks in anticipation.
I found the issue. So, if you observe the line:
<Animated.View
style={[styles.headerWrapper, headerStyle]}
onLayout={(event) => {
this.setState({
headerHeight: event.nativeEvent.layout.height,
});
}}
>
onLayout used to setState as soon as the headerHeight value changed on the scroll. Due to which the height of the header used to reduce and scrolling the content down was not possible.
I changed the code to:
<Animated.View
style={[styles.headerWrapper, headerStyle]}
onLayout={(event) => {
if (!this.state.headerHeight) {
this.setState({
headerHeight: event.nativeEvent.layout.height,
});
}
}}
>
Now, everything works fine.

react-native (js) : generalise a function

I have two functions which I would like to condense into one (with another argument). They are called like so:
<ListView
automaticallyAdjustContentInsets={false}
dataSource={this.state.dataSource_proj}
renderRow={this.renderProjRow.bind(this)}/>
<ListView
automaticallyAdjustContentInsets={false}
dataSource={this.state.dataSource_map}
renderRow={this.renderMapRow.bind(this)}/>
And the functions are:
renderProjRow(rowData, sectionID, rowID) {
return (
<TouchableHighlight onPress={() => this.rowProjPressed(rowData.project_name)}
underlayColor='#dddddd'>
<View>
<View style={styles.rowContainer}>
<Image source={{uri: 'Letter-' + rowData.project_name.substr(0,1).toUpperCase()}}
style={styles.thumb}/>
<View style={styles.textContainer}>
<Text style={styles.title}
numberOfLines={1}>{rowData.project_name}</Text>
</View>
</View>
<View Style={styles.separator}/>
</View>
</TouchableHighlight>
);
}
renderMapRow(rowData, sectionID, rowID) {
return (
<TouchableHighlight onPress={() => this.rowMapPressed(rowData)}
underlayColor='#dddddd'>
<View>
<View style={styles.rowContainer}>
<Image source={{uri: 'Letter-' + rowData.map_name.substr(0,1).toUpperCase()}}
style={styles.thumb}/>
<View style={styles.textContainer}>
<Text style={styles.title}
numberOfLines={1}>{rowData.map_name}</Text>
</View>
</View>
<View Style={styles.separator}/>
</View>
</TouchableHighlight>
);
}
I would like to just have one function: renderRow, which takes rowData.project_name if the data-source is dataSource_proj and rowData.map_name if the data-source is dataSource_map.
However, aside from using eval ... I'm not sure how to implement this in a proper way.

Categories

Resources