Data not being displayed in one of two FlatLists in the same component - react-native

I have a component with two TextInputs. Under each TextInput, there is a FlatList which gets rendered when the user types in some characters (the actual rendering happens in ListItem, a module from react-native elements). For the first FlatList, everything works fine - it is rendered as expected. The problem is with the second FlatList - I cannot get it to render. See this gif to understand what I mean: https://gph.is/g/E1G1jnx
I've tried using the "extraData" prop as advised in One of two FlatLists not rendering items in same component , but it didn't fix the issue.
I know that the problem doesn not come from onChangeAddress(address) being an async function. I used a static dataset and still it wouldn't render.
import { StyleSheet, Text, TextInput, View, FlatList, ImageBackground, Image } from 'react-native';
import { ListItem, Button } from 'react-native-elements';
class Home extends Component {
constructor(props) {
super(props);
this.state = {
jobInputValue: '',
addressInputValue: '',
showJobsDropdown: false,
showAddressesDropdown: false,
jobsList: this.props.jobTitles.results,
addressPredictions: []
};
}
async onChangeAddress(address) {
//fetch the data from the API
//filteredAddresses stores the data from the API
if (filteredAddresses.length > 0) {
this.setState({
addressPredictions: filteredAddresses,
showAddressesDropdown: true
})
}
}
}
render() {
return (
<ImageBackground style={styles.bkgImage} source={require('../assets/homepage_background.jpg')}>
<TextInput
style={styles.jobTextInput}
value={this.state.jobInputValue}
placeholder='Ce job cauți?'
onChangeText={(jobInputValue) => this.setState({jobInputValue}, this.checkJobsDropdown(jobInputValue))}/>
<FlatList
data={this.state.jobsList}
style={this.state.showJobsDropdown ? styles.jobsDropdownStyle : styles.dropdownHiddenStyle}
renderItem={({ item }) => (
<ListItem
title={item}
containerStyle={{paddingBottom: -4}}
titleProps={{ style: styles.dropdownItemStyle}}
onPress={() => this.setState({jobInputValue: item}, this.hideJobsDropdown())}
/>
)}
keyExtractor={item => item}
/>
<TextInput
style={styles.addressTextInput}
value={this.state.addressInputValue}
placeholder='La ce adresă locuiești?'
onChangeText={addressInputValue => this.onChangeAddress(addressInputValue)}
/>
//the issue is with the FlatList below
<FlatList
data={this.state.addressPredictions}
extraData={this.state}
style={this.state.showAddressesDropdown ? styles.addressDropdownStyle : styles.dropdownHiddenStyle}
renderItem={({ addressItem, index }) => (
<ListItem
title={addressItem}
containerStyle={{paddingBottom: -4}}
titleProps={{style: styles.dropdownItemStyle}}
onPress={() => this.setState({addressInputValue: addressItem}, this.hideAddressesDropdown())}
/>
)}
keyExtractor={(addressItem, index) => index.toString()}
/>
<Button
title="CAUTĂ"
type="outline"
underlayColor={colors.red}
titleStyle={styles.buttonTitleStyle}
color={colors.red}
style={styles.buttonStyle}
onPress={this.printState}
/>
</ImageBackground>
);
}
Any help will be greatly appreciated!

The problem is in the destructuring of the data for rendering the items of your second flatlist:
renderItem={({ addressItem, index }) => (
<ListItem
title={addressItem}
containerStyle={{paddingBottom: -4}}
titleProps={{style: styles.dropdownItemStyle}}
onPress={() => this.setState({addressInputValue: addressItem}, this.hideAddressesDropdown())}
/>
)}
({ addressItem, index }) <- there's no addressItem available here. You have to replace that with item, as Flatlist offers an object with the { item: Object, index: Number, separators: object } structure as the param to your renderList callback.

Related

React Native - Variable link using a prop

I'm making an app with some products that I got from my Wordpress database. On the homescreen, I have an overview of all the products, each in a tile. I want to be able to put a button in each tile, which links to the specific product page. But, since it works with a component, I need to be able to do this with a prop. And, if possible, based on the title of the API.
This is my code for the screen with all the products:
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View, FlatList, Image, Button } from 'react-native';
import SuitcaseItem from '../components/SuitcaseItem';
const AllSuitcasesScreen = ({ navigation }) => {
const [suitcases, setSuitcases] = useState([]);
const getSuitcases = async () => {
try {
const response = await fetch("https://evivermeeren.com/wp-json/wp/v2/posts?categories=59", {
}
)
const json = await response.json();
console.log(json);
setSuitcases(json);
} catch (error) {
console.error(error);
}
}
useEffect(() => {
getSuitcases();
}, []);
return (
<View style={styles.screen}>
<View style={styles.flexbox2}>
<Text style={styles.products}>Onze koffers</Text>
<View style={styles.shoppingcart}>
<Image
style={styles.icon}
source={{uri: 'https://cdn-icons-png.flaticon.com/512/1413/1413908.png'}}
/>
<Text style={styles.number}>0</Text>
</View>
</View>
<View style={styles.list}>
<FlatList
data={suitcases}
renderItem={({ item }) => (
<SuitcaseItem
title={item.title.rendered}
imageUri={{uri: 'https://www.samsonite.be/on/demandware.static/-/Sites/default/dw851ab6f0/images/misc/sams_share-image.jpg'}}
desc={item.slug}
buttonText={item.title.rendered}
/>
)}
/>
</View>
</View>
);
}
export default AllSuitcasesScreen;
And this is the result:
Now, when I click the black button, I go to the page 'Evo L', which I also made. This is the button that I use:
<Pressable style={styles.seeProduct} onPress={() => navigation.navigate("Evo L")}>
<Text style={styles.text}>Bekijk product: {props.buttonText}</Text>
</Pressable>
This is in another file, the 'SuitcaseItem'.
So, I should be able to put something like navigation.navigate("props.buttonNav") with buttonNav = {item.title.rendered} so it goes to the page Evo L if I click on that one and then Evo M when I click on that tile and so one. Does anyone have an idea?
You can pass props to a screen. See this excellent official documentation for React Navigation on passing props.
-> Make a generic item detail screen like ItemDetail (instead of Evo L).
-> Modify the navigation.navigate("props.buttonNav") to:
navigation.navigate("ItemDetail", {itemTitle: props.buttonText})
You can access these props in the ItemDetail screen as:
function ItemDetail({ navigation, route }) {
return(
<Text>route.params.itemTitle</Text>
)
}

Is there a way to get the Title of the selected item from another component in React Native

I have two different components "HomePage" & "ListItemCA"
In HomePage, I have a FlatList and a modal popup
<FlatList
data={ listData}
keyExtractor={list => list.Title}
renderItem={({ item }) => <ListItemCA data={item} onLongPress={openModal} />}
/>
and each list item is called from another component ListItemCA
function ListItemCA({data, onLongPress}) {
return (
<TouchableOpacity onLongPress={onLongPress} >
<View style={styles.container}>
<Text style={styles.title}>{data.Title}</Text>
<View style={styles.metaContainer}>
<Text style={styles.meta}>{( data.totalMonths != null ? data.totalMonths : '0' )} Months</Text>
<Text style={styles.meta}>{( data.members != null ? data.members.length : '0' )} Members</Text>
</View>
</View>
</TouchableOpacity>
);
}
What I want to acheive?
I want to get the selected list item title on my HomePage component. When a user longpress on a list item that title should be displayed on a modal popup. How do I pass the selected list item title to the HomePage component using longpress?
If your goal is to display data from the long pressed item in the modal, you could add the data as a parameter of your openModal function:
function openModal(data) {
// your function
return (
<Text>{data.Title}</Text>
)
}
Then, in your FlatList, modify the props of ListItemCA to call openModal for the selected item:
renderItem={({ item }) => <ListItemCA data={item} onLongPress={openModal(item)} />}
If you also want to save the data from the long pressed item in your HomePage component for other uses, you could save it in the state. In your HomePage component:
import React, { useState } from 'react'
function HomePage() {
const [itemData, setItemData] = useState()
// your code
}
Then, in your flatlist:
<FlatList
data={listData}
keyExtractor={list => list.Title}
renderItem={({ item }) =>
<ListItemCA
data={item}
onLongPress={ () => {
setItemData(item)
openModal(item)
}}
/>
}
/>
You can achieve this by passing(return) parameter from your component like this -
function ListItemCA({data, onLongPress}) {
return (
<TouchableOpacity onLongPress={() => {
onLongPress(data.Title);
//return data.Title when onLongPressed clicked
}}>
<View style={styles.container}>
...
</View>
</TouchableOpacity>
);
}
then get it in props -
<FlatList
data={listData}
keyExtractor={list => list.Title}
renderItem={({ item }) =>
<ListItemCA
data={item}
onLongPress={(title) => {//this **title** return from **onLongPress(data.Title)**
openModal();
setTitle(title);// or directly can pass that title in openModal func.
}}
/>
}
/>

React Native - trigger scrolling of FlatList outside the FlatList

I have a vertical FlatList component and two buttons as TouchableOpacity, how do I perform scrolling of the FlatList with the buttons,
i.e. 'scrolling the FlatList towards bottom` and 'scroll the FlatList towards top'?
Minimal Example:
<View>
<FlatList/>
<TouchableOpacity>
<Text>Scroll towards Top</>Text
</TouchableOpacity>
<TouchableOpacity>
<Text>Scroll towards Bottom</>Text
</TouchableOpacity>
</View>
This is not difficult to accomplish, The <Flatlist/> component already have methods to do that.
scrollToEnd(): Scrolls to the end of the content.
scrollToIndex(): Scrolls to the item at the specified index such 0 which is the top.
I have created a simple demo for you: https://snack.expo.io/#abranhe/flatlist-scroll
I have created a custom <Button/> and <Card/> components. I am creating an array with some random data with this format
const data = [
{ message: 'Random Message' }, { message: 'Random Message' }
]
I am adding a reference to the <Flatlist/> by adding
ref={ref => (this.flatlist = ref)}
Then I call the methods and that's it.
<Button title="▼" onPress={() => this.flatlist.scrollToEnd()} />
The whole source code:
import React from 'react';
import { Text, View, FlatList, StyleSheet } from 'react-native';
import { random } from 'merry-christmas';
import Card from './components/Card';
import Button from './components/Button';
const data = [...Array(10)].map(i => ({ message: random() }));
export default () => (
<View style={styles.container}>
<FlatList
ref={ref => (this.flatlist = ref)}
data={data}
renderItem={({ item }) => <Card gretting={item.message} />}
/>
<View style={styles.bottomContainer}>
<Button
title="▲"
onPress={() => this.flatlist.scrollToIndex({ index: 0 })}
/>
<Button title="▼" onPress={() => this.flatlist.scrollToEnd()} />
</View>
</View>
);
You can use a scrollView component

ReactNative - FlatList not updated until scroll

I have a problem with FlatList component which does not update until scrolled.
I tried add log to renderItem and keyExtractor both methods called with correct data but list didn't update.
Here is a render method:
render() {
const messages = this.props.messages
const message = this.props.message
return (
<View style={[styles.container]}>
<FlatList
ref={"flatList"}
contentContainerStyle={styles.list}
data={messages}
renderItem={(listItem) => {
return <MessageBuble message={listItem.item}/>
}}
keyExtractor={(item: Message) => {
return item.id
}}
/>
<View style={[styles.textInputContainer]}>
<TextInput
style={styles.textInput}
value={message}
multiline={true}
onChangeText={this.props.messageChanged}
/>
<Button title={"Odeslat"} onPress={() => {
if (this.props.sendMessage) {
this.props.sendMessage(this.props.message)
}
}}/>
</View>
</View>
)
}
Add extraData in FlatList and retry
<FlatList
extraData={this.props}
....
Tried the extraData, but that does not work.
There was an issue on Android where content was not visible when I returned back from another page to home screen (where the flatlist was present). The content was visible when I scrolled it a bit.
I assigned the main list to the extraData attribute, and could see that it changed in size via console logs. But the content remained invisible. Finally, used
onContentSizeChange={() => {
if (list.length > 0) {
ref.current.scrollToOffset({ animated: true, x: 0 });
}
}}
and it worked.

Click listener in flatlist

How can I add click listener in Flatlist?
My code:
renderItem({item, index}){
return <View style = {{
flex:1,
margin: 5,
minWidth: 170,
maxWidth: 223,
height: 304,
maxHeight: 304,
backgroundColor: '#ccc',
}}/>
}
render(){
return(<FlatList
contentContainerStyle={styles.list}
data={[{key: 'a'}, {key: 'b'},{key:'c'}]}
renderItem={this.renderItem}
/>);
}
}
Update 1: I used button but it is not working in Flatlist. However using only button instead of Flatlist, it works. Why is it not working in Flatlist renderItem?
_listener = () => {
alert("clicked");
}
renderItem({item, index}){
return<View>
<Button
title = "Button"
color = "#ccc"
onPress={this._listener}
/>
</View>
}
I used TouchableWithoutFeedback. For that, you need to add all the renderItem elements (i.e your row) into the TouchableWithoutFeedback. Then add the onPress event and pass the FaltList item to the onPress event.
import {View, FlatList, Text, TouchableWithoutFeedback} from 'react-native';
render() {
return (
<FlatList style={styles.list}
data={this.state.data}
renderItem={({item}) => (
<TouchableWithoutFeedback onPress={ () => this.actionOnRow(item)}>
<View>
<Text>ID: {item.id}</Text>
<Text>Title: {item.title}</Text>
</View>
</TouchableWithoutFeedback>
)}
/>
);
}
actionOnRow(item) {
console.log('Selected Item :',item);
}
You need to wrap your row element (inside your renderItem method) inside <TouchableWithoutFeedback> tag. TouchableWithoutFeedback takes onPress as it's prop where you can provide onPress event.
For TouchableWithoutFeedback refer this link
I used TouchableOpacity. and it's working great.This will give you click feedback. which will not be provided by TouchableWithoutFeedback. I did the following:
import { View, Text, TouchableOpacity } from "react-native";
.
.
.
_onPress = () => {
// your code on item press
};
render() {
<TouchableOpacity onPress={this._onPress}>
<View>
<Text>List item text</Text>
</View>
</TouchableOpacity>
}
If you are facing flatlist row first click issue
please add below property to flatlist.
disableScrollViewPanResponder = {true}
The Pressable component is now preferred over TouchableWithoutFeedback (and TouchableOpacity). According to the React Native docs for TouchableWithoutFeedback:
If you're looking for a more extensive and future-proof way to handle touch-based input, check out the Pressable API.
Example implementation:
import { Pressable } from "react-native";
render() {
return(
<FlatList
contentContainerStyle={styles.list}
data={[{key: 'a'}, {key: 'b'}, {key:'c'}]}
renderItem={({item}) => (
<Pressable onPress={this._listener}>
// BUILD VIEW HERE, e.g. this.renderItem(item)
</Pressable>
)}
/>
);
}
References
TouchableWithoutFeedback (React Native): https://reactnative.dev/docs/touchablewithoutfeedback
Pressable (React Native): https://reactnative.dev/docs/pressable
you dont need to add Touchable related component into your Flatlist renderItem. Just pass onTouchStart prop to your Flatlist.
in example:
<FlatList
style={themedStyles.flatListContainer}
data={translations}
renderItem={renderItem}
keyExtractor={(item, index) => `${item.originalText}____${index}`}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
ListEmptyComponent={renderEmptyListComponent}
onTouchStart={onBackgroundPressed}
/>