react native flatlist doesn't show updated value - javascript

I'm having a trouble in a flatlist that a button increments the value but doesn't show if I don't refresh or hit ctrl-s after incrementing value. Basically I cannot see the value change without pressing ctrl-s or going to another page and returning to the same page.
<FlatList
data={store}
renderItem={({ item }) => {
return (
<View style={styles.itemCountView}>
<TouchableOpacity style={styles.up}
onPress={() => item.itemCount++}>
<MaterialIcons name="arrow-drop-up" size={36} color="#ddd"/>
</TouchableOpacity>
<Text style={styles.itemCountText}>{item.itemCount}</Text>
</View>
)
}}
/>
I can increment the value, if I save or go to another page and come back the value changes and i can see it but there must be a way to see it change.
Any help is appreciated

As per the Documentation
extraData: https://reactnative.dev/docs/flatlist#extradata
A marker property for telling the list to re-render (since it implements PureComponent). If any of your renderItem, Header, Footer, etc. functions depend on anything outside of the data prop, stick it here and treat it immutably.

I think you'r not saving the changes you doing, basically you need to update the the store variable each time you increment an item, something like:
<FlatList
data={store}
renderItem={({ item }) => {
return (
<View style={styles.itemCountView}>
<TouchableOpacity style={styles.up}
onPress={() => {
const newStoreData = [...store]
const itemIndex = newStoreData.findIndex(item)
const newItem = {...item,itemCount:item.itemCount ++}
newStoreData[itemIndex] = newItem
setStoreData(newStoreData) // state or redux?
}
}>
<MaterialIcons name="arrow-drop-up" size={36} color="#ddd"/>
</TouchableOpacity>
<Text style={styles.itemCountText}>{item.itemCount}</Text>
</View>
)
}}
/>

You can try this approach here:
import {useState} from 'react';
import { Text, View, FlatList, TouchableOpacity } from 'react-native';
const store = [{
itemCount: 1
},
{
itemCount: 2
},
{
itemCount: 3
}]
export default function App() {
return (
<View style={{
flex: 1,
padding: 140,
}}>
<FlatList
data={store}
renderItem={({ item }) => ( <Item item={item}/> )}
/>
</View>
);
}
const Item = ({item})=> {
const[count, setCount] = useState(item.itemCount);
return (
<View>
<TouchableOpacity
style={{
backgroundColor: 'black',
padding: 10,
margin: 10
}}
onPress={() => setCount(count+1)}>
<Text style={{
color: 'white'
}}>{count}</Text>
</TouchableOpacity>
</View>
)
}
This will set the state for each item in the Flatlist.

Related

React Native how to add button inside Flatlist?

How can I add a button inside flatlist, whenever I tried to add a button then I am getting multiple buttons inside flatlist.
I want only one button which is scrollable with flatlist.
and if I add a button outside flatlist then it's not scrolling, it get fixed below the flatlist, only flatlist data scroll but the button not scroll with flatlist. How can I solve this issue? Really appreciate your support.
import React, { useState, useEffect } from 'react';
import {View, Button, Text, FlatList, StyleSheet, Pressable, TouchableOpacity} from 'react-native'
import {firebase} from '../config';
const Testing = ({ navigation }) =>{
const [users, setUsers] = useState([]);
const todoRef = firebase.firestore().collection('testing');
useEffect(() => {
todoRef.onSnapshot(
querySnapshot => {
const users = []
querySnapshot.forEach((doc) => {
const { one, two, three, four, five
} = doc.data()
users.push({
id: doc.id,
one, two, three, four, five
})
})
setUsers(users)
}
)
}, [])
return (
<View style={{ flex:1,}}>
<FlatList
style={{height: '100%'}}
data={users}
numColumns={1}
renderItem={({item}) => (
<Pressable>
<View>
<View>
<Text style={[styles.card, styles.surah]}>{item.one}</Text>
<Text style={styles.card}>{item.two}</Text>
<Text style={styles.text}>{item.three}</Text>
<Text style={styles.cardTwo}>{item.four}</Text>
<Text style={styles.text}>{item.five}</Text>
</View>
</View>
// I tried to add here button but it's not worked
</Pressable>
)}/>
// I also tried to add here button but it's not worked
</View>
);}
export default Testing;
You could implement this with the help of the index parameter of the renderItem function.
renderItem={({item, index}) => (
<View>
<View>
<Text style={[styles.card, styles.surah]}>{item.one}</Text>
<Text style={styles.card}>{item.two}</Text>
<Text style={styles.text}>{item.three}</Text>
<Text style={styles.cardTwo}>{item.four}</Text>
<Text style={styles.text}>{item.five}</Text>
</View>
{
index === users.length - 1 && <Pressable onPress={...}>...</Pressable>
}
</View>
)}
The above adds a component, in this case a Pressable at the end of the last item. If you want the last item to be pressable, then you can achieve this using the same pattern, but by wrapping the last component inside a pressable.
const InnerComponent = () => {
return <View>
<Text style={[styles.card, styles.surah]}>{item.one}</Text>
<Text style={styles.card}>{item.two}</Text>
<Text style={styles.text}>{item.three}</Text>
<Text style={styles.cardTwo}>{item.four}</Text>
<Text style={styles.text}>{item.five}</Text>
</View>
}
...
renderItem={({item, index}) => (
<View>
{
index === users.length - 1 ? <Pressable onPress={...}>
<InnerComponent />
</Pressable> : <InnerComponent />
}
</View>
)}

React Navigation only sets param after navigating a second time

I want to pass the item (asset) from a Flastlist and present in a child screen.
But when I press the item, the parameter is null. I have to go back and press it again for the parameter to be set. And if I press a different item, the old item still lingers until I press the new item a second time.
I don't know if useEffect is the best way to do it. I am just trying different approaches but have not had any luck with useEffect, useFocusEffect, or none.
Parent with the Flastlist
export default function SitesScreen(props) {
const [sites, setSites] = useState(["Power Plant", "Paper Mill", "Plastic Injection"])
const [selectedItem, setSelectedItem] = useState(null)
const Item = ({ item, onPress }) => (
<TouchableOpacity onPress={onPress} style={[styles.item]} >
<Text style={styles.text}>{item}</Text>
</TouchableOpacity>
)
const renderItem = ({ item }) => {
return (
<View style={styles.itemContainer} >
<Item
item={item}
onPress={() => onItemSelected(item)}
/>
</View >
)
}
const onItemSelected = (item) => {
setSelectedItem(item)
props.navigation.navigate("Asset", { asset: selectedItem })
}
return (
<View style={styles.container}>
<CustomHeader title="Sites" navigation={props.navigation} isHome={true} ></CustomHeader>
<View style={styles.contentContainer}>
<View style={{ width: '90%', height: '50%', alignItems: 'center', bottom: -150 }} >
<FlatList
data={sites}
renderItem={renderItem}
keyExtractor={(item) => JSON.stringify(item)}
/>
</View>
</View>
</View>
)}
Child screen to present item
export default function SitesScreen(props) {
const [asset, setAsset] = useState('')
useEffect(() => {
setAsset(props.route.params.asset)
console.log(asset)
}, [])
return (
<View style={styles.container}>
<CustomHeader title="Asset" navigation={props.navigation} isHome={false} ></CustomHeader>
<View style={styles.contentContainer}>
<Text style={styles.text} >{asset}</Text>
<View style={{ width: '90%', height: '50%', alignItems: 'center', bottom: -150 }} >
</View>
</View>
</View>
)}
When you pass the selectedItem value as a parameter to the next screen, the new state set from setSelectedItem has not been applied to the component yet. Because the new state hasn't been applied, you are still passing the initial null value that was set for selectedItem. This happens because state values are used by functions based on their current closures.
See this StackOverflow post for a more detailed explanation about this problem.
Problem solved.
The parent screen needs to have the navigate action in the useEffect hook, and not in the onItemSelected function. That way it waits until the state has been changed before it navigates.
useEffect(() => {
if (selectedItem) {
props.navigation.navigate("Asset", { asset: selectedItem })
}
}, [selectedItem])

How to make a dropdown for every FlatList item?

Kind of surprised at the lack of information I can find about this question, feel like its something that you be done pretty often? I know with a standard dropdown it's pretty easy, you set up a hook that controls state with a boolean, when that boolean is true, you show the dropdown, when it's false, you show the closed version.
The Issue that I discovered when trying to do this with each render item is that the hook state control needs to be in the global context, because of this whenever you click on Flatlist item, all of the items open because they are all using the same state control. How would you make it so that each rendered item has its own dropdown state when you can't set a state hook inside each render item?
Here's my code to give you a better idea of what I'm talking about, be sure to read the comments:
<FlatList
contentContainerStyle={{ alignItems: 'center', marginVertical: 10, minHeight: 200 }}
data={notes}
keyExtractor={(item, index) => item.key}
ListFooterComponent={() => <AddNoteFooter onPress={addSpecificNote} />}
renderItem={({ item }) => {
//would need something similar to a hook right here,
// to manage the state of each item individually
//change "isOpen" state when button is pressed
return (
<View>
{!isOpen &&
<TouchableOpacity onPress={null} style={styles.flastliststyle}>
<Text style={styles.flastlistItemText}>{item.note}</Text>
</TouchableOpacity>
}
{isOpen &&
<TouchableOpacity>
/* extended dropdown code */
</TouchableOpacity>
}
</View>)
}
Looking to do something similar to this video but where each item is a flatlist item (also using hooks):
https://www.youtube.com/watch?v=awEP-pM0nYw&t=134s
Thank you!
SOLUTION:
flatlist:
<FlatList
contentContainerStyle={{ alignItems: 'center', marginVertical: 10, minHeight: 200 }}
data={notes}
keyExtractor={(item, index) => item.key}
ListFooterComponent={() => <AddNoteFooter onPress={addSpecificNote} />}
renderItem={({ item }) => {
return (
<NoteItem noteitem={item} />
)
}}
/>
then the component rendered for each item:
const NoteItem = (props) => {
const [isOpen, updateDrop] = useState(false)
return (
<View>
{!isOpen &&
<TouchableOpacity onPress={() => updateDrop(prev => !prev)} style={styles.flastliststyle}>
<Text style={styles.flastlistItemText}>{props.noteitem.note}</Text>
</TouchableOpacity>
}
{isOpen &&
<TouchableOpacity onPress={() => updateDrop(prev => !prev)} style={styles.flastliststyle}>
<Text style={styles.flastlistItemText}>pressed</Text>
</TouchableOpacity>
}
</View>
)
}

How to pass and execute functions as props in class Component in React Native?

I'm a beginner in React Native and struggling in passing and executing functions as props from parent to child component. Here's the code:
MainMap
import React from 'react';
import {
TouchableWithoutFeedback,
StyleSheet,
View,
Button,
FlatList,
Dimensions
} from 'react-native';
import PlaceInput from '../components/PlaceInput';
const INCREMENT = 1;
const HEIGHT = Dimensions.get('window').height
const WIDTH = Dimensions.get('window').width
class MainMap extends React.Component{
constructor(props){
super(props);
this.state={
numOfInput:[],
counter: 0,
}
this.onAddSearch = this.onAddSearch.bind(this)
this.onDeleteSearch = this.onDeleteSearch.bind(this)
}
onAddSearch(){
this.setState((state) => ({
counter: state.counter + INCREMENT,
numOfInput: [...state.numOfInput, state.counter]
}))
}
onDeleteSearch(inputId){
const items = this.state.numOfInput.filter(item => item.id !== inputId)
this.setState({
numOfInput: items
})
}
render(){
return(
<TouchableWithoutFeedback onPress={this.hideKeyboard} >
<View style={styles.container} >
<Button title='Add a location' onPress={this.onAddSearch} />
<View style={{height: HEIGHT/2 }}>
<FlatList
data={this.state.numOfInput}
keyExtractor={(item, index) => item.id}
renderItem={itemData => {
return(
<PlaceInput
key={itemData.item.id}
// id={itemData.item.id}
onDelete={this.onDeleteSearch}
showDirectionOnMap={this.showDirectionOnMap}
userLatitude={userLatitude}
userLongitude={userLongitude}
/>
)
}}
/>
</View>
</View>
</TouchableWithoutFeedback>
)
}
}
export default MainMap;
const styles = StyleSheet.create({
container:{
flex: 1
},
})
Here's the PlaceInput component
class PlaceInput extends React.Component{
constructor(props){
super(props);
... // These lines have no relation to what I'm asking so don't mind them
}
...
render(){
return(
<View style={styles.buttonContainer} >
<View style={{flex: 1, alignItems: 'center'}}>
<Text style={{fontSize: 8}}>{'\u25A0'}</Text>
</View>
<View style={{flex: 4}}>
<TextInput
autoCorrect={false}
autoCapitalize='none'
style={styles.inputStyle}
placeholder='Search your places'
onChangeText={(input) => {
this.setState({destinationInput: input});
this.getPlacesDebounced(input);
}}
value={this.state.destinationInput}
/>
{/* {predictions} */}
</View>
<View style={styles.rightCol}>
<TouchableOpacity onPress={this.props.onDelete.bind(this, this.props.id)}>
<Ionicons name='md-car' size={25} style={{alignSelf: 'center'}} />
</TouchableOpacity>
</View>
</View>
)
}
}
What I'm trying to do:
Define a function to execute in MainMap.js (in FlatList --> PlaceInput for specific) , which is to delete an search bar( the whole PlaceInput in the FlatList) every time I click the right symbol of that search bar. The function is onDeleteSearch
The right symbol is styled in a TouachableOpacity as you can see in the PlaceInput.js component. I put it in the last View pair
However, When I click, the screen deletes all the search bars, not the one I click. Is it the problem of the id of the component PlaceInput ? Or with the way I call the props?...
Please help me !
<TouchableOpacity onPress={this.props.onDelete.bind(this, this.props.id)}>
<Ionicons name='md-car' size={25} style={{alignSelf: 'center'}} />
</TouchableOpacity>
Don't bind, just call this.props.onDelete(this.props.id);
In MainMap, try this:
<PlaceInput
key={itemData.item.id}
// id={itemData.item.id}
onDelete={() => this.onDeleteSearch(itemData.item.id)} // here
showDirectionOnMap={this.showDirectionOnMap}
userLatitude={userLatitude}
userLongitude={userLongitude}
/>
Assuming the function:
onPressed(optionalArgument = false) {
// do something
}
You can pass a function to onPress if it does not require any arguments, i.e
onPress={onPressed} // - would work if no arguments required.
onPress={onPressed(argument)} // - will get fired on component render
onPress={()=> onPressed(argument)} // - will work as expected on button press
onPress={()=> { // - will work as expected on button press
// Multiple lines of code
onPressed(argument);
anotherFunction();
}
};
In your MainMap you are doing everything correctly, just uncomment the
// id={itemdata.item.id}
In PlaceInput, just one small change:
<TouchableOpacity onPress={() => this.props.onDelete(this.props.id)}>
<Ionicons name='md-car' size={25} style={{alignSelf: 'center'}} />
</TouchableOpacity>
If you don't add ()=> to your onPress, the function gets called immediately, that's why you see such behaviour.
Your numOfInput is just a list of numbers, so instead of using item.id-s use item directly.
Here:
const items = this.state.numOfInput.filter(item => item !== inputId)
And here
<PlaceInput
key={itemData.item}
// id={itemData.item}
...
/>

border bottom changing on press flatlist item in react native

i'm new in react native and i need your help
i want to give border-bottom to pressed item of horizontal flatlist and border-bottom of Previous item disappear,
now i can give border to new pressed item but i cant remove previous item border
how can i achive this?
enter image description here
these are my Category_style code
state = {
isModalVisible: false,
Index : 0
}
_toggleModal = (index) => {
this.setState({isModalVisible: !this.state.isModalVisible});
this.setState({Index : index});
}
renderProduct(item) {
return <Sub_Categories_FlatList_style name={item.title} icon={item.icon}/>
}
renderSeparator = () => (
<View
style={{
backgroundColor: '#d2d2d2',
height: 0.5,
}}
/>
)
render() {
const {title, index} = this.props;
return (
<View style={pStyles.container}>
<TouchableHighlight onPress={() => this._toggleModal(index)}
style={(index === this.state.Index) ? pStyles.border_bottom : pStyles.no_border_bottom}>
<Text style={pStyles.title}>{title}</Text>
</TouchableHighlight>
<Modal isVisible={this.state.isModalVisible} animationType={'slide'}>
<TouchableOpacity onPress={() => this._toggleModal(index)} style={pStyles.T_opacity}
activeOpacity={.7}>
<Image source={require('./../pictures/x_icon.png')}
style={pStyles.close_image}/>
</TouchableOpacity>
<View style={pStyles.in_modal_view}>
<Text style={pStyles.modal_header_text}>{title}</Text>
<FlatList
data={this.props.subCategory}
renderItem={({item}) => this.renderProduct(item)}
ItemSeparatorComponent={this.renderSeparator}
keyExtractor={(item, index) => index}/>
</View>
</Modal>
</View>
and these are my Category code
static navigationOptions = {
headerStyle: {
backgroundColor: '#4caf50'
},
headerTitle: <Text style={Category_in_style.headerTitleStyle}>Category</Text>
}
renderCategory(item, index) {
return <Category_style title={item.title} index={index} subCategory={item.sub_category}/>
}
renderProduct(item) {
return <Product_style image={item.imageUrl} title={item.title} price={item.price}/>
}
render() {
return (
<View style={{backgroundColor: 'white'}}>
<FlatList style={styles.first_flat}
horizontal
data={this.state.main_categories}
renderItem={({item, index})=> this.renderCategory(item, index)}
keyExtractor={(item, index) => index}/>
<PTRView style={{backgroundColor: '#f1f1f1'}}>
<FlatList style={[{marginTop: 10}, {marginBottom: 50}]}
data={this.state.articles}
renderItem={({item}) => this.renderProduct(item)}
keyExtractor={(item, index) => index}
numColumns={2}/>
</PTRView>
</View>
There may be multiple ways of doing this but I prefer having the parent decide which component is clicked.
So what I suggest is in your renderProduct the item object also has an index which you can pass to your Sub_Categories_FlatList_style and also pass a function to it which basically updates a variable whenever the item is clicked.
Then when you render the list item simply check if it's index matches the currently selected index and then style it accordingly.
Sorry if this seems a bit vague but I'm eyeballing this since I'm at work but I'll be more than happy to answer any follow ups.

Categories

Resources