render a scrollview after a condition in react native - if-statement

i need to display a scrollview in react native application, after a condition using if. In fact, i have a list of items in firebase that contain text and title but sometimes they may contain images also. I need to display that list of text and titles in the exact same order and also the images if there is any. here the code i used
_renderScrollViewContent() {
const data = this.state.items
if (data.image) {
return (
<View>
{data.map((item, i) =>
<View key={i}>
<Image source={require('path')}/>
<Text>{item.image}</Text>
<Text>{item.title}</Text>
<Text>{item.text} </Text>
</View>
)}
</View>
);
}
else
{
return (
<View>
{data.map((item, i) =>
<View key={i} style={styles.row}>
<Text>{item.image}</Text>
<Text>{item.title}</Text>
<Text>{item.text} </Text>
</View>
)}
</View>
);
}
}
render() {
const headerHeight = this.state.scrollY.interpolate({
inputRange: [0, HEADER_SCROLL_DISTANCE],
outputRange: [HEADER_MAX_HEIGHT, HEADER_MIN_HEIGHT],
extrapolate: 'clamp',
});
const imageOpacity = this.state.scrollY.interpolate({
inputRange: [0, HEADER_SCROLL_DISTANCE / 2, HEADER_SCROLL_DISTANCE],
outputRange: [1, 1, 0],
extrapolate: 'clamp',
});
const imageTranslate = this.state.scrollY.interpolate({
inputRange: [0, HEADER_SCROLL_DISTANCE],
outputRange: [0, -50],
extrapolate: 'clamp',
});
return (
<View style={styles.fill}>
<ScrollView
style={styles.fill}
scrollEventThrottle={16}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {y: this.state.scrollY}}}]
)}
>
{this._renderScrollViewContent()}
</ScrollView>
);
}
I Now it seems that it's not even checking for the condition (data.image) and it seems that it always goes directly to the else condition and displays only the text.
PS: i'm using this tutorial to make a scrollable header and it's working perfectly fine in displaying text but not images inside them https://medium.com/appandflow/react-native-scrollview-animated-header-10a18cb9469e.
Also, if delete the if condition, i always get the blank spaces between items .. any suggestion

so that's the code that finally worked for me ..i hope it will help someone else in the future
_renderScrollViewContent() {
var productList = [];
this.state.items.forEach(function (item) {
if (!item.image) {
productList.push(
<View key={item.key}>
<Text>{item.title}</Text>
<Text>{item.text}</Text>
<Text>{item.image}</Text>
</View>
);
}
else {
productList.push(
<View key={item.key}>
<Text>{item.title}</Text>
<Text>{item.text}</Text>
<Text>{item.image}</Text>
<Image source={require('path')}>
<View>
<Text>{item.legend}</Text>
</View>
</Image>
</View>
);
}
}.bind(this));
return (
<View>
{productList }
</View>
);
}

Related

Two column layout in react native using reactnavigation

I am trying to achieve two column layout if i click something inside tab (for example list of Chats) chat should show in right, now its appears inside left column
can you point me to correct way?
if someone needs this is how I make this
/* eslint-disable react-native/no-inline-styles */
import * as React from 'react';
import {Button, Text, View} from 'react-native';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
function ContactScreen({route}) {
const {contactId} = route.params;
return (
<View style={{flexDirection: 'row', flex: 1}}>
<Tab.Navigator
initialRouteName="Contacts"
style={{flex: 1}}>
<Tab.Screen name="Chats" component={ChatsTab} />
<Tab.Screen name="Contacts" component={ContactsTab} />
</Tab.Navigator>
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Contact {contactId}</Text>
</View>
</View>
);
}
function ChatScreen({route}) {
const {chatId} = route.params;
return (
<View style={{flexDirection: 'row', flex: 1}}>
<Tab.Navigator
initialRouteName="Chats"
style={{flex: 1}}>
<Tab.Screen name="Chats" component={ChatsTab} />
<Tab.Screen name="Contacts" component={ContactsTab} />
</Tab.Navigator>
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Chat {chatId}</Text>
</View>
</View>
);
}
function ChatsTab({navigation}) {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>chats tab</Text>
<Button
title="open chat 1"
onPress={() =>
navigation.navigate('Chat', {
screen: 'Chats',
chatId: 1,
})
}
/>
<Button
title="open chat 2"
onPress={() =>
navigation.navigate('Chat', {
screen: 'Chats',
chatId: 2,
})
}
/>
<Button
title="open chat 5"
onPress={() =>
navigation.navigate('Chat', {
screen: 'Chats',
chatId: 5,
})
}
/>
</View>
);
}
function ContactsTab({navigation}) {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>contacts tab</Text>
<Button
title="open contact 4"
onPress={() =>
navigation.navigate('Contact', {
screen: 'Contacts',
contactId: 4,
})
}
/>
<Button
title="open contact 3"
onPress={() =>
navigation.navigate('Contact', {
screen: 'Contacts',
contactId: 3,
})
}
/>
</View>
);
}
const HomeStack = createStackNavigator();
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<HomeStack.Navigator screenOptions={{headerShown: false}}>
<HomeStack.Screen
initialParams={{chatId: 0}}
name="Chat"
options={{animationEnabled: false}}
component={ChatScreen}
/>
<HomeStack.Screen
initialParams={{contactId: 0}}
options={{animationEnabled: false}}
name="Contact"
component={ContactScreen}
/>
</HomeStack.Navigator>
</NavigationContainer>
);
}

In Android Modal - wrapping content in GestureHandlerRootView not fixing react-native-gesture-handler

I am using expo ^40.0.0.
I am trying to get react-native-gesture-handler components to work in a <Modal> from react-native in Android. I followed docs here - https://docs.swmansion.com/react-native-gesture-handler/docs/#for-library-authors
It stays to wrap in <GestureHandlerRootView>, I did this but it doesn't fix the issue.
I created a snack demo of issue - https://snack.expo.io/#noitidart/frisky-carrot
Here you see a "Tapped here count: 0". Please try tapping it, you will see it doesn't increment the counter.
You will see a "RN BUTTON" this works but it is not from react-native-gesture-handler.
Here is my code from the snack:
import * as React from 'react';
import { Text, View, Modal, Button as RNButton } from 'react-native';
import {
BaseButton,
GestureHandlerRootView
} from 'react-native-gesture-handler';
export default function App() {
return (
<Modal animationType="slide" transparent={false}>
<Example />
</Modal>
);
}
const Example = function () {
const [cnt, setCnt] = React.useState(0);
return (
<View style={{ justifyContent: 'center', flex: 1 }}>
<RNButton title="rn button" onPress={() => alert('hi')} />
<GestureHandlerRootView>
<BaseButton
onPress={() => {
// alert('pressed');
setCnt(cnt + 1);
}}
>
<View style={{ backgroundColor: 'steelblue', height: 100 }}>
<Text>Tapped here count: {cnt}</Text>
</View>
</BaseButton>
</GestureHandlerRootView>
</View>
);
};

How to add Click Event on FlatList Item in React Native?

I am working in demo Application for learning React native And I need to render clickable FlatList item.
Here is Render and itemClick function:
render() {
return (
<View style={styles.container}>
<View>
<FlatListData list={this.state.itemList} />
</View>
</View>
);
}
itemClick(item){
console.log('click on item', item);
}
I have already declare FlatListData functional component for that :
const FlatListData = ({list}) => {
return (
<FlatList
data={list}
keyExtractor={(item, index) => index.toString()}
renderItem={itemList}
/>
);
};
In FlatList component, RenderItem attribute have again custom component for single item i.e :
const itemList = ({item, index}) => {
return (
<TouchableOpacity onPress={this.itemClick(item)} style={styles.catalogContainer}>
<Image
source={{uri: item.img}}
style={styles.imageStyle}
/>
</TouchableOpacity>
);
};
But this code is not working. It will give me error like : Undefined is not an object (eveluting _this.itemClick)
Here in which way and where Should I write click function for item ?
Any one have a answer that can solve my problem ?
NOTE :
All code is written in one js file. My Render function is inside my class component. and other two declarative component are out of that class component.
I need that item inside that click function. But I don't know how to pass parameter inside function.
There are a couple of things you can do to fix your problem.
You need to move your itemClick function inside itemList as an arrow function.
const itemList = ({ item, index }) => {
itemClick = item => {
console.log("click on item", item);
};
return (
<TouchableOpacity
// onPress={this.itemClick(item)}
onPress={() => this.itemClick(item)}
style={styles.catalogContainer}
>
<Image source={{ uri: item.img }} style={styles.imageStyle} />
</TouchableOpacity>
);
};
Pass your itemClick function as props to child component
render() {
return (
<View style={styles.container}>
<FlatListData list={this.state.itemList} onItemClick={this.itemClick} />
</View>
);
}
itemClick = (item) => {
console.log('click on item', item);
}
Now you can call onItemClick prop inside itemList
const FlatListData = ({ list, onItemClick }) => {
itemList = ({ item }) => (
<TouchableOpacity
onPress={() => onItemClick(item)}
style={styles.catalogContainer}
>
<Image source={{ uri: item.img }} style={styles.imageStyle} />
</TouchableOpacity>
);
return (
<FlatList
data={list}
keyExtractor={(item, index) => index.toString()}
renderItem={this.itemList}
/>
);
};
Hope this helps you. Feel free for doubts.

Dynamic table data rendering react native

I want to achieve the data rendering in a form of table something like this:
I have a data (stored in variable 'myData') of form:
{
"id":0,
"name": ["wheat", "Rice", "Sugar"],
"Quality":["Good", "good","good"],
"Quantity":["200","200","200"]
}
I tried mapping the and together to implement this, but it renders the data horizontally in a single row.
This is what I tried to achieve so far with minimum test code.
return (
<View style={{flexDirection:'column'}}>{myData.map( (item) => {
return (
<View key={item.id}>
<View>
<Text>
{item.name}
</Text>
</View>
<View>
<Text >
{item.Quality}
</Text>
</View>
</View>
)
})}
</View>
)
Please help to achieve this
map each array into it's own View and use flexDirection: "row"
const list = myData.map(item => {
return (
<View style={{ flexDirection: 'row'}}>
<View style={{ flex: 1}}>
{item.name.map((name, i) => (
<Text>{i + 1}</Text>
))}
</View>
<View style={{ flex: 1}}>
{item.name.map((name, i) => (
<Text>{name}</Text>
))}
</View>
<View style={{ flex: 1}}>
{item.Quality.map((quality, i) => (
<Text>{quality}</Text>
))}
</View>
<View style={{ flex: 1}}>
{item.Quantity.map((quantity, i) => (
<Text>{`${quantity} Bags`}</Text>
))}
</View>
</View>
);
});
Try this,
return (
<View style={{flexDirection:'column'}}>{myData.name.map( (item, index) => {
return (
<View key={item.id}>
<View>
<Text>
{item}
</Text>
</View>
<View>
<Text >
{myData.Quality[index]}
</Text>
</View>
<View>
<Text >
{myData.Quantity[index]}
</Text>
</View>
</View>
)
})}
</View>
)

Fetching data from AWS SQS and wait to render ReactJs

So we are having an issues where we send a request to SQS and then on the receive message it returns no data.
To try and fix this we threw in a while loop to poll for an extended period of time; however that really doesn't work because we cannot sleep between polls and it seems as if they are all happening asynchronously.
I say that because if the value does get returned (happens about 50% of the time) it will re render the next screen as many time as cap is left and polling never gets set to false. Could someone please help us out!
render() {
return (
<Navigator
renderScene={this.renderScene.bind(this)}
navigator={this.props.navigator}
navigationBar={
<Navigator.NavigationBar style={{backgroundColor: 'transparent'}}
routeMapper={NavigationBarRouteMapper} />
} />
);
}
renderScene() {
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" hidden={true}/>
<View style={styles.topContainer}>
<View style={styles.bannerContainer}>
<View style={{flexDirection: 'column', flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Image style={styles.mark} source={require('./yb.png')} />
</View>
</View>
<View style={styles.credentialContainer}>
<View style={styles.inputContainer}>
<Icon style={styles.inputPassword} name="person" size={28} color="#FFCD00" />
<View style={{flexDirection: 'row', flex: 1, marginLeft: 2, marginRight: 2, borderBottomColor: '#e0e0e0', borderBottomWidth: 2}}>
<TextInput
style={styles.input}
placeholder="Username"
autoCorrect={false}
placeholderTextColor="#e0e0e0"
onChangeText={(text) => this.setState({username: text})}
value={this.state.username}>
</TextInput>
</View>
</View>
<View style={styles.inputContainer}>
<Icon style={styles.inputPassword} name="lock" size={28} color="#FFCD00" />
<View style={{flexDirection: 'row', flex: 1, marginLeft: 2, marginRight: 2, borderBottomColor: '#e0e0e0', borderBottomWidth: 2}}>
<TextInput
style={styles.input}
placeholder="Password"
autoCorrect={false}
secureTextEntry={true}
placeholderTextColor="#e0e0e0"
onChangeText={(text) => this.setState({password: text})}
value={this.state.password}>
</TextInput>
</View>
</View>
<TouchableOpacity style={styles.forgotContainer}>
<Text style={{color: '#e0e0e0'}}>Forgot Password</Text>
</TouchableOpacity>
</View>
</View>
<TouchableHighlight
underlayColor="#D6AB00"
onPress={this.login}
style={styles.signInButtonContainer}>
<Text style={styles.signInText}>Sign In</Text>
</TouchableHighlight>
</View>
);
}
login() {
var polling = true;
if(this.state.username.length == 0){
Alert.alert("Error","No email entered");
}
else if(this.state.password.length == 0){
Alert.alert("Error","No password entered");
}
else
{
info.username = this.state.username;
info.password = this.state.password;
AWS.sendMessage('****',JSON.stringify(info), (error,result) =>{
if(error != null) console.log("Error Occured Attempting to Send A Message")
else {
var cap=0;
while(cap <= 30 && polling){
cap = cap+1;
AWS.receiveMessages('****'+info.username, (error, result) =>{
if(error != null) console.log("Error");
else{
if(result){
try{
polling = false;
if(result[0] == "[]")
{
console.log(result[0]);
console.log(typeof result[0]);
Alert.alert("Error","Username/Password Incorrect");
}
else
{
console.log(result[0]);
console.log(typeof result[0]);
var temp = JSON.parse(result[0]);
this.props.navigator.replace({
id: 'MapIOS',
data: {
username: info.username,
ofAge: temp[0].ofAge,
uid: temp[0].u_ID
},
});
}
}
catch(e){
console.log(e);
}
}
AWS.deleteMessage('***'+info.username, result[1], (error) => {
if(error != null) console.log("Error");
});
}
});
}
return
}
});
}
}
};
The problem is that you are calling AWS.ReceiveMessage in a loop.
When you call AWS.ReceiveMessage, the call is asynchronous. Meaning, it will return immediately. It does not wait for your callback to be called. So your loop can be exhausted before any of the callbacks get called.
Trying to set your polling variable from your callback function is not a safe way to "protect" your callback.
You need to restructure your function so that the second AWS.ReceiveMessage does not happen until after your callback has been called from the first AWS.ReceiveMessage.
Generally speaking, don't loop.
Some important facts about SQS that you need to keep in mind when using it:
SQS is not "first in, first out". Messages may not be received in the same order as they were put into the queue. So if you add messages "1" then "2", you may receive them as "2" then "1".
Even if messages are in the queue, you may not get a message when you call ReceiveMessage.
You may receive the same message more than once. For example, if you added message "1" to the queue, you may get back "1" then "1" again later. So you need to ensure that message processing is idempotent and repeat-safe.
All of the above is due to the fact that SQS is distributed and redundant. It guarantees that you will get your messages, but it does not guarantee the order, or the number of times that messages will be received.
Update:
This was my original answer, but I don't think it's the primary issue with the code. I'm leaving it here since I think it's still valuable information.