How can I get current index through FlatList? - react-native

I'm passing data to details screen with Navigation params, everything works correct, the only thing that I want to pass clicked current level index to Object.entries[currentIndex], I'm not sure how can I get that.
<FlatList
data={data}
keyExtractor={(item) => item.index}
renderItem={({ item, index }) => (
<View>
{item.sTask?.map((levels, i) => (
<View>
<TouchableOpacity
onPress={() => navigation.navigate('Tasks', { item })}
style={{
backgroundColor: '#33333325',
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 10,
marginVertical: 20,
marginHorizontal: 20,
}}
>
<Text>{Level 1}</Text>
</TouchableOpacity>
<TouchableOpacity>
{Another level...}
</TouchableOpacity>
</View>
))}
</View>
)}
/>
Details screen:
const [currentTaskGroupIndex, setCurrentTaskGroupIndex] = useState(0); <---- We need to set current index somehow to current clicked index
//Test item params data
useEffect(() => {
const mapData = item?.sTask?.map((tasks) => {
return Object.entries(tasks)[currentTaskGroupIndex].map((level) => {
// console.log(level);
});
});
}, [item]);
No matter which level i clicked, i'm getting Level 1 details

You need to do like :
onPress={() => navigation.navigate('Tasks', { item:item, index:i })}
and then in details screen:
const indexParam = props?.route?.params?.index ?? 0
const [currentTaskGroupIndex, setCurrentTaskGroupIndex] = useState(indexParam); <---- We need to set current index somehow to current clicked index
//Test item params data
useEffect(() => {
const mapData = item?.sTask?.map((tasks) => {
return Object.entries(tasks)[currentTaskGroupIndex].map((level) => {
// console.log(level);
});
});
}, [item]);
Hope it helps. feel free for doubts

You can pass index of the task in navigation param
onPress={() => navigation.navigate('Tasks', { item, index: i })}
Now, you no need map() to find your task
const mapData = item?.sTask[index]
you can pass your whole object in the navigation params

Thank you guys for you answers, you were right, and plus i jusd added another Object.key().map and it worked
{item.sTask?.map((levels, i) => (
<View key={i}>
{Object.keys(levels).map((key, i) => (
<View key={i}>
{console.log(key)}
<TouchableOpacity
onPress={() =>
navigation.navigate('Tasks', { item: item, index: i })
}
style={{
backgroundColor: '#33333325',
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 10,
marginVertical: 20,
marginHorizontal: 20,
}}
>
<Text>{key}</Text>
</TouchableOpacity>
</View>
))}
```

Related

app crashes because of Flat-List API calling

I'm calling API and seeing the respective API values in the log, it shows me correct values, but when I try to set API in Flat list with the help of hooks my app crashes. I don't know the reason as I'm new in react native, so any help regarding this would be really appreciated.
NOTE( If I'm displaying the values directly without flat list it won't cause any error)
function Item({ item }) {
const navigation = useNavigation();
return (
<TouchableOpacity style={styles.listItemBox}
onPress={() => navigation.navigate('PatientDemographics')}
>
<View style={{flex:1}}>
<Text numberOfLines={1} style={{ textAlign: 'left', fontSize: 25, color:"#075430", textAlign: 'center',fontFamily:"Montserrat-Regular"}}>{item.firstName}</Text>
<TouchableOpacity style={[styles.smallRoundedBlueRoundedNoMargin,{marginTop:10,marginBottom:40}]}
onPress={() => navigation.navigate('PatientDemographics')} >
<Text style={[styles.cardText,{fontSize: 18},{color: 'white'}]}>SELECT </Text>
</TouchableOpacity>
</View>
</TouchableOpacity>
);
}
const SelectPatient = () => {
let numColumns = 4;
const formatData = (data, numColumns) => {
const numberOfFullRows = Math.floor(data.length / numColumns);
let numberOfElementsLastRow = 8 - (numberOfFullRows * numColumns);
while (numberOfElementsLastRow !== numColumns && numberOfElementsLastRow !== 0) {
data.push({ key: `blank-${numberOfElementsLastRow}`, empty: true });
numberOfElementsLastRow++;
}
return data;
};
// const navigation = useNavigation();
const [isLoading, setLoading] = useState(true);
const [patient, setPatient] = useState([]);
const mrnum=89
useEffect(() => {
axios({
method: 'get',
url: `https://emr-system.000webhostapp.com/emrappointment/emrappointment/patient/search?mrnum=89&cnic=&qrcode=`,
}).then((response) => {
//Balance / transaction-list
setPatient(response.data.result);
console.log(response.data.result);
console.log(patient[0].patientId);
}).then(() => setLoading(false));
}, []);
return (
<View style={styles.container}>
<Header name="Select Patient" class= ""/>
<UnitClerkHeader/>
<PatientHeader/>
<View style= {{flex:1 ,width: '100%', alignSelf: 'center'}}>
<SafeAreaView style={{flex:1}} >
<FlatList
style={{flex:1, marginTop: 30, marginRight:30,marginLeft:30}}
data={ formatData(patient,numColumns)}
renderItem={({ item }) => <Item item={item}/>}
keyExtractor={item => item.patientId}
numColumns = {numColumns}
/>
</SafeAreaView>
</View>
</View>
);
}
export default SelectPatient;
You can try with
<FlatList
style={{ flex: 1, marginTop: 30, marginRight: 30, marginLeft: 30 }}
data={() => formatData(patient, numColumns)}
renderItem={({ item }) => <Item item={item} />}
keyExtractor={item => item.patientId}
numColumns={numColumns}
/>
I can help you better when you show your error too.

How can I change background color of a FlatList row?

I'm learning a about lists in React-Native but I can't seem to make my code work. I want to change the background color of a certain row in a FlatList when the button inside that row is pressed. I found some answers online but most of them were complex for me to understand and didn't work(I'm very new to React-Native)
This is my code so far:
export default function Listt () {
const [click, setClick] = useState(null);
const [productsData, setproductsData] = useState([]);
const ItemView = ({item, index}) => {
return (
// FlatList Item
<TouchableOpacity style={styles.row} onPress={() => {setClick(index);}}>
<View>
<Text style={styles.rowtext}>
{item[0]+ ' ' + item[1]}
</Text>
</View>
</TouchableOpacity>
);
};
async function loadInState() {
const keys =await AsyncStorage.getAllKeys();
const result = await AsyncStorage.multiGet(keys);
setproductsData([...productsData, ...result]);
}
useFocusEffect(
React.useCallback(() => {
loadInState()
}, [])
);
return (
<View style={styles.container}>
<FlatList
data={productsData}
renderItem={ItemView}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
row: {
width:widtht,
height:45,
borderWidth: 2,
borderColor: 'black',
textAlign: 'center',
justifyContent: 'center',
alignItems:'center',
textAlign:'center'
//backgroundColor:'blue',
},
rowtext:{
fontSize:17,
},
});
You can simply do it with index .which uniquely identify the each item in the list
here is the code you can use.
const ItemView = ({item, index}) => {
return (
// FlatList Item
<TouchableOpacity style={[styles.row,{backgroundColor: click===index ? 'tomato':'transparent'}]} onPress={() => {setClick(index);}}>
<View>
<Text style={styles.rowtext}>
{item[0]+ ' ' + item[1]}
</Text>
</View>
</TouchableOpacity>
);
};
Here I used my fav color tomato when you press the button but you can use as per your need and where I used transparent you can use rest of the items colors like white or whatever you want.

How to Update state in return()?

I want to change the state when I get id of 5/6 or 7, after TouchableOpacity. Because I want to show the "Other Things" button one time whether id is 5/6 or 7.
export function HomePage({ navigation }) {
const [parentServiceCheck, setParentServiceCheck] = useState({ check: '0' });
return (
<View>
<FlatList numColumns={2} contentContainerStyle={{ margin: 7, flex: 1 }} keyExtractor={(item, index) => item.id} data={allService} renderItem={itemData => (
<View style={{ flexDirection: 'column', flex: 1 }}>
{
(itemData.item.id == 5 || itemData.item.id == 6 || itemData.item.id == 7) && parentServiceCheck.check == '0' ?
<TouchableOpacity onPress={() => pressHandler(itemData.item.id)} style={{ margin: 7 }}>
<Card titlebtn="Other Things" src={itemData.item.service_image} sty={{ height: 180, }} />
</TouchableOpacity>
// Now here I want to setParentServiceChildCheck({check: '1'}) when itemData.item.id has 5/6/7
:
<TouchableOpacity onPress={() => pressHandler(itemData.item.id)} style={{ margin: 7 }}>
<Card titlebtn={itemData.item.service_name} src={itemData.item.service_image} sty={{ height: 180, }} />
</TouchableOpacity>
}
</View>
)}
/>
</View>
)
}
You can't really update or execute any JS code like that. I would suggest to go through few training to learn about how things work in React JSX.
For now, if you want to update the state then you should be good to use it in useEffect as shown below
export default function HomePage({ navigation }) {
const [parentServiceCheck, setParentServiceCheck] = useState({ check: '0' });
useEffect(() => {
const hasValidId = allService.find(_hasValidId);
// This will get called only once after first render
// This will execute only when id's are 5, 6 or 7 which is your requirement
if (hasValidId) {
setParentServiceChildCheck({ check: '1' });
}
}, []);
const _hasValidId = itemData => {
const itemId = itemData?.item?.id;
const validItemIds = [5, 6, 7];
return validItemIds.includes(itemId);
};
const _renderItem = itemData => {
const itemId = itemData?.item?.id;
let content = null;
if (_hasValidId(itemData)) {
if (parentServiceCheck.check === '0') {
content = (
<TouchableOpacity onPress={() => pressHandler(itemId)} style={{ margin: 7 }}>
<Card titlebtn="Other Things" src={itemData.item.service_image} sty={{ height: 180 }} />
</TouchableOpacity>
);
} else {
content = (
<TouchableOpacity onPress={() => pressHandler(itemId)} style={{ margin: 7 }}>
<Card
titlebtn={itemData.item.service_name}
src={itemData.item.service_image}
sty={{ height: 180 }}
/>
</TouchableOpacity>
);
}
}
return <View style={{ flexDirection: 'column', flex: 1 }}>{content}</View>;
};
return (
<View>
<FlatList
numColumns={2}
contentContainerStyle={{ margin: 7, flex: 1 }}
keyExtractor={(item, index) => item.id}
data={allService}
renderItem={_renderItem}
/>
</View>
);
}
FYI, you might need to change few lines as the whole component code is not shared and I don't know about your business logic.
You should never update state in return / render function. It will make infinite rendering loop.

How to update a single item in FlatList - React Native

In my case I am writing a Facebook clone like application, but much simpler. I put each item in FlatList and render them.
To "like" a post, I press the "like" button on the post and the "like" button turns yellow and the likes count increase by 1 ( Also I call addLike API Function after clicking ), I press it again, it turns gray and likes count decrease by one ( Also I Call removeLike API Funtion ).
This is what I have so far: I store all the loaded posts with redux - this.props, each post has a property called "liked", which is boolean, indicating whether this user has liked this post or not, when user presses "like", Now I doing when user like the post I call the addLike() action and fetch all the posts to feed again. I want to do this without fetching data again and again
FlatList Component
<FlatList
style={styles.postList}
data={this.props.postData}
extraData={this.props}
maxToRenderPerBatch={10}
keyExtractor={item => {
return item.id;
}}
ItemSeparatorComponent={() => {
return <View style={styles.separator} />;
}}
renderItem={post => {
const item = post.item;
this.state.userisLiked = item.likedUsers.find(
user => user.id == this.state.userDetails.id,
);
// console.log('Returning Is Liked ', isLiked);
return (
<View style={styles.card}>
<View>
{item.postImage ? (
<TouchableOpacity
onPress={() =>
this.showSelectedImageFullView(item.postImage)
}>
<ImageBackground
style={styles.cardImage}
source={{
uri: Strings.AWSS3_POST_IMAGE + item.postImage,
}}>
<View style={styles.overlay} />
</ImageBackground>
</TouchableOpacity>
) : (
<View></View>
)}
</View>
<View style={{flexDirection: 'row'}}>
<Image
source={
item.user.profilePicture
? {
uri:
Strings.AWSS3_USER_PROFILE_AVATAR +
item.user.profilePicture,
}
: Images.IMAGE_PLACEHOLDER
}
style={styles.postUserImage}
/>
<View style={{flexDirection: 'column'}}>
<Text style={styles.postUserName}>
{item.user.firstName} {item.user.lastName}
</Text>
<TimeAgo
style={styles.postedTime}
time={item.createdAt}
interval={20000}
/>
</View>
<TouchableOpacity
style={styles.postMoreInfoIcon}
onPress={() => this.toggleModal(item)}>
<Image
source={Images.POST_MORE_INFO}
style={styles.postMoreInfoIcon}
/>
</TouchableOpacity>
</View>
<TouchableOpacity onPress={() => this.homeMoreInfoScreen(item)}>
<View style={{flexDirection: 'column'}}>
<Text style={styles.postTitle}>{item.title}</Text>
<Text style={styles.postBody} numberOfLines={2}>
{item.description}
</Text>
</View>
</TouchableOpacity>
<View style={styles.cardFooter}>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
onPress={() => {
this.handleUserLikes(item);
}}>
{this.state.userisLiked ? (
<Image
source={Images.POST_LIKE_CHECKED}
style={{
width: 20,
height: 20,
resizeMode: 'contain',
}}
/>
) : (
<Image
source={Images.POST_LIKE_UNCHECKED}
style={{
width: 20,
height: 20,
resizeMode: 'contain',
}}
/>
)}
</TouchableOpacity>
<Text
selectable={true}
onPress={() => this.toggleLikeModal(item)}
style={{
fontFamily: AppStyles.primaryFont,
fontSize: 15,
color: AppStyles.colorWhite,
marginLeft: 5,
}}>
{item.likesCount} Likes
</Text>
</View>
<View style={{flexDirection: 'row', marginLeft: 20}}>
<TouchableOpacity
onPress={() => this.homeMoreInfoScreen(item)}>
<Image
source={Images.POST_COMMENT}
style={{width: 20, height: 20, resizeMode: 'contain'}}
/>
</TouchableOpacity>
<Text
selectable={true}
onPress={() => this.homeMoreInfoScreen(item)}
style={{
fontFamily: AppStyles.primaryFont,
fontSize: 15,
color: AppStyles.colorWhite,
marginLeft: 5,
}}>
{item.commentsCount} Comments
</Text>
</View>
<View
style={{
flexDirection: 'row',
marginLeft: 10,
position: 'absolute',
right: 100,
top: 20,
}}>
</View>
<View
style={{
flexDirection: 'row',
marginLeft: 10,
position: 'absolute',
right: 10,
top: 20,
}}>
<TouchableOpacity
onPress={() => this.homeMoreInfoScreen(item)}>
<Text
style={{
fontFamily: AppStyles.primaryFont,
fontSize: 15,
color: AppStyles.colorWhite,
}}>
Comment
</Text>
</TouchableOpacity>
</View>
</View>
</View>
);
}}
/>
Handle User Like Action
//Add Likes to selected post click listner
handleUserLikes = item => {
//Check user already liked the post
const isLiked = item.likedUsers.find(
user => user.id == this.state.userDetails.id,
);
if (isLiked) {
this.props.removeLikeFromPost(item.id, this.state.user_token);
this.props.fetchPostData(this.state.user_token);
} else {
this.props.addLikeToPost(
item.id,
this.state.user_token,
this.state.userDetails.id,
);
this.props.fetchPostData(this.state.user_token);
}
};
Assuming that the items are available as props. You can handle the data alteration in the reducer instead of your React component. first what you have to do is
handleUserLikes = item => {
this.props.addLikeToPost({
item_id:item.id,
user_token:this.state.user_token,
user_id:this.state.userDetails.id,
});
};
inside your redux code fire a function that handles the logic.
const userLike =(state,payload)=>{
let Newitem = null;
let item= state.item.find(
user => item.id == payload.item_id
);
let itemIndex = state.item.findIndex(
user => user.id == payload.item_id
);
let isLiked = item.likedUsers.find(user=>user.id===payload.user_id);
if(isLiked){
Newitem = {...item,likedUsers:item.likedUsers.filter(user=>user.id!==payload.user_id)}
} else{
Newitem = {...item,likedUsers:[...item.likedUsers,payload.user_id]}
}
let Newitems = [
state.items.slice(0, itemIndex),
Newitem,
state.items.slice(++itemIndex)
];
return {...state,items:Newitems}
}
This method userLike should be called inside the reducer switch statement which corresponds to your particular action. like this
function appReducer(state = initialState, action) {
switch (action.type) {
.........
case 'YOU_ACTION' :
return userLike(state,action);
}
}
In this manner, you don't have to fetch the items data again and again. But make sure you send the data to the back end saying the post is liked or unliked.
const addLikeToPost = (data) => dispatch => {
// you can send a request to your back end here without Awaiting.
// data is passed from the addLikeToPost method called from you react component.
dispatch({action:'YOU_ACTION',payload:data});
}
First of all thanks to #TRomesh for his answer.
Based on that I found a solution for this issue easily
My Button Action Call
handleUserLikes = item => {
this.props.addLikeToPost({
item_id:item.id,
user_token:this.state.user_token,
user_id:this.state.userDetails.id,
});
};
Redux Reducer Function -> This is the part I edited from above answer
const userLike = (state, payload) => {
console.log('TCL: userLike -> payload', payload.payload);
console.log('TCL: userLike -> state.postData', state.postData);
let item = state.postData.find(user => user.id == payload.payload.item_id);
let local_data = state.postData;
let isLiked = item.likedUsers.find(
user => user.id === payload.payload.user_id,
);
if (isLiked) {
local_data = local_data.map(data => {
if (data.id == payload.payload.item_id) {
data.likesCount = Number(data.likesCount - 1);
}
return data;
});
} else if (isLiked == undefined || isLiked == null) {
local_data = local_data.map(data => {
if (data.id == payload.payload.item_id) {
data.likesCount = Number(data.likesCount + 1);
}
return data;
});
}
return {...state, postData: local_data};
};
Cheers !

Why does object prop doesn't show in Flat List?

this.state={
task : "",
the_array : []
};
}
handleTextChange=(some_task) =>{
this.setState({
task : some_task
});
}
pushToList = () => {
let taskitem = {
name: this.state.task,
};
this.setState({
the_array: [...this.state.the_array, taskitem],
});
}
render() {
return(
<View style={{backgroundColor:'powderblue', height:'100%'}}>
<FlatList data = {this.state.the_array}
renderItem={(item) => <Text>{item.name}</Text>} keyExtractor={(item) => item.name} >
</FlatList>
<TextInput style={{ backgroundColor: 'lightsteelblue', height:60, borderRadius : 25, marginBottom:20}}
onChangeText={this.handleTextChange}>
</TextInput>
<TouchableOpacity style={{
backgroundColor: 'mediumpurple', height: 60, width: 80, alignSelf: 'center', borderRadius: 20, justifyContent: 'center',
alignItems: 'center', marginBottom:20
}} onPress={this.pushToList}>
This is my code.I'm trying to add Textinput content to Flatlist. For that purpose, I defined an object inside my button onPress method('pushToList'), named 'taskitem', and set a prop for it named 'name'.
'pushTolistMethod' is supposed to put the 'name' into the Flatlist on screen. but strangely it doesn't work and nothing happens when I press the button. I was wondering if anybody could help me with that.
Can you replace your flatlist code like this and try?
<FlatList data = {this.state.the_array}
renderItem={({ item }) => <Text>{item.name}</Text>} keyExtractor={(item) => item.name} >
</FlatList>
The data is on the item key so we use destructuring to access that from within the function.