React Native Firebase Firestore data fetching with empty spaces - javascript

I have created 3 documents in Firestore database, each document has different data.
but When I add different data on different document ids then I am getting blank spaces, and those spaces are generated automatically for other document ids which I already created previously.
Document 1 should be shown on First screen and document 2 should show on screen two. I mean each document's data should show on its own screen. please check the image link below-
First Screen
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
} = doc.data()
users.push({
id: doc.id,
One, Two, Three
})
})
setUsers(users)
}
)
}, [])
return (
<View style={{ flex:1,}}>
<FlatList
style={{height: '100%'}}
data={users}
numColumns={1}
renderItem={({item}) => (
<Pressable >
<View style={styles.viewOne}>
<View>
<Text style={[styles.card, styles.title]}>{item.One}</Text>
<Text style={styles.text}>{item.Two}</Text>
<Text style={styles.text}>{item.Three}</Text>
</View>
</View>
</Pressable>
)} />
</View>
);}
export default Testing;
*Second Screen*
import React, { useState, useEffect } from 'react';
import {View, Button, Text, FlatList, StyleSheet, Pressable, TouchableOpacity} from 'react-native'
import {firebase} from '../config';
const TestingDocs = ({ navigation }) =>{
const [users, setUsers] = useState([]);
const todoRef = firebase.firestore().collection('testing');
useEffect(() => {
todoRef.onSnapshot(
querySnapshot => {
const users = []
querySnapshot.forEach((doc) => {
const { DocsOne, DocsTwo, DocsThree,
} = doc.data()
users.push({
id: doc.id,
DocsOne, DocsTwo, DocsThree,
})
})
setUsers(users)
}
)
}, [])
return (
<View style={{ flex:1,}}>
<FlatList
style={{height: '100%'}}
data={users}
numColumns={1}
renderItem={({item}) => (
<Pressable >
<View style={styles.viewOne}>
<View>
<Text style={[styles.card, styles.title]}>{item.DocsOne}</Text>
<Text style={styles.text}>{item.DocsTwo}</Text>
<Text style={styles.text}>{item.DocsThree}</Text>
</View>
</View>
</Pressable>
)} />
</View>
);}
export default TestingDocs;

You must have seen this Answer on your another Post :
It looks like you've incomplete or "optional" data in your backend. If
you don't want to render empty fields you can conditionally render
them.
For the users data that is missing both properties you can filter
the data prop.
Example:
<FlatList
data={users.filter(({ One, five }) => One || five)}
renderItem={({ item }) => (
<View style={{ .... }}>
{item.One && <Text>ID: {item.One}</Text>}
{item.five && <Text>Name: {item.five}</Text>}
</View>
)}
/>
You can also refer to this Answer:
Its as a result of performance issues with the FlatList component but
you can add the following props to your FlatList Component, it would
help solve the problem. They are:
i. removeClippedSubviews.
Set this to true, it comes with a default of false.
ii. windowSize. Set this to a number like say 30
iii. maxToRenderPerBatch. This controls the number of items rendered per batch, which is the next chunk of items rendered on every
scroll.
<FlatList
data={this.state.contacts}
removeClippedSubviews={true}
maxToRenderPerBatch={60}
windowSize={30}
ListHeaderComponent={headerComponent}
contentContainerStyle={{ paddingBottom: 10 }}
renderItem={({ item }) => (
<View>
{item}
</View>
)}
numColumns={1}
keyExtractor={(item, index) => String(index)}
/>
For more information, you can refer to the blog which explains how to use react hooks with firebase firestore.

Related

VirtualizedList: You have a large list that is slow to update How can i fix this problem

I am facing an error that says "VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc." Can anyone help me to fix this problem in my code? Also, sometimes posts load after 2-3 secs of time how can I load intensely?
MainPage:
export default function Home({ navigation }) {
const [userdata, setUserdata] = useState(null);
return (
<View style={styles.container}>
<StatusBar />
<ButtomNavbar navigation={navigation} page={'home'} />
<TopNavbar navigation={navigation} page={'home'} />
<Posts />
</View>
);
}
Post Comp:
import { StyleSheet, FlatList } from 'react-native'
import React, { useState, useEffect, useCallback } from 'react';
import PostCard from '../Cards/PostCard';
const Posts = () => {
const [userData, setUserData] = useState([]);
const fetchUserData = useCallback(async () => {
try {
const response = await fetch('http://10.0.2.2:3000/postdata');
const data = await response.json();
setUserData(data);
} catch (err) {
console.error(err);
}
}, []);
useEffect(() => {
fetchUserData();
}, [fetchUserData]);
return (
<FlatList
style={styles.container}
data={userData}
renderItem={({ item, index }) => (
<PostCard
key={index}
username={item.username}
profile_image={item.profile_image}
postImage={item.postImage}
/>
)}
/>
);
}
export default Posts
Post Card:
import React from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';
const PostCard = ({ username, profile_image, postImage }) => {
return (
<View style={styles.container}>
<View style={styles.c1}>
<Image source={{ uri: profile_image }} style={styles.profilepic} />
<Text style={styles.username}>{username}</Text>
</View>
<Image source={{ uri: postImage }} style={styles.image} />
</View>
);
};
Try adding the keyExtractor prop to your FlatList.
Here is an example:
return (
<FlatList
style={styles.container}
data={userData}
keyExtractor={(item) => `${item.id}`} <-----add this line
renderItem={({ item, index }) => (
<PostCard
key={index}
username={item.username}
profile_image={item.profile_image}
postImage={item.postImage}
/>
)}
/>
);
"VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc."
This warning basically appear when you try to render too much data or objects using FlatList because when you have good amount of data , you have to manage that it will not be render again and again as renderItem call again and again with data when user scroll which consume more memory
You can control this behaviour by creating renderItem class component and extend it with PureComponent instead of React.Component
You can also control this behaviour by shouldComponentUpdate method as shown in this example
https://www.geeksforgeeks.org/what-does-shouldcomponentupdate-do-and-why-is-it-important/
If you think it is going to take your time then as simplest solution you can use this lib
https://shopify.github.io/flash-list/

How to pass array of objects to another screen and display them react native

How do i pass the data of selected checkboxes to the previous screen in react native.
The following is what i have done so far:
This is my SelectProducts screen
import React, {useState, useEffect} from 'react'
import { StyleSheet, Text, View, Alert, Image, ScrollView, TouchableOpacity } from 'react-native';
import Checkbox from 'expo-checkbox';
const SelectProducts = ({route}) => {
const [checkedBox, setCheckedBox] = useState([]);
const [selectedBoxesItem, setSelectedBoxesItem] = useState([]);
const [itemList, setItemList] = useState([]);
const includeSelectedItem = (item, index) => {
const newCheckedBox = [...checkedBox];
newCheckedBox[index] = !newCheckedBox[index];
setCheckedBox(newCheckedBox);
setSelectedBoxesItem({
selectedUniqueKey: item.id,
selectedItemName: item.ad_headline,
selectedItemPrice: item.ad_price,
selectedItemPic: item.ad_picture
});
}
This is the function that I'm using to send the data to the RecordASale screen after clicking on the Done button that is below the list of checkboxes.
const handleSelectedSubmit = () => {
navigation.navigate({
name: 'RecordASale',
params: {
post: [selectedBoxesItem],
},
merge: true,
})
}
And this is the checkbox:
return (
{itemList.map((item, index) => (
<DataTable.Row>
<DataTable.Cell>
<View style={styles.checkboxContainer}>
<Checkbox
key={item.id}
value={checkedBox[index]}
onValueChange={() => includeSelectedItem(item, index)}
color={checkedBox ? '#800080' : undefined}
style={styles.checkbox}
/>
</View>
</DataTable.Cell>
<DataTable.Cell>
<Image source = {{uri: "https://cdn.beraty.com/beraty-ads/"+item.ad_picture}} style = {{ width: 30, height: 30 }} />
</DataTable.Cell>
<DataTable.Cell>{item.ad_headline}</DataTable.Cell>
<DataTable.Cell>{item.ad_price}</DataTable.Cell>
</DataTable.Row>
))}
<View style = {styles.submitButton}>
<Text style = {styles.submitButtonText} onPress={() => handleSelectedSubmit()}>Done</Text>
</View>
</DataTable>
);
}
What i want to achieve is to get the following details for every checkbox selected:
item.id,
item.ad_headline,
item.ad_price,
item.ad_picture
All the above data should be passed from this SelectProducts screen to RecordASale screen
To my own understanding, what I did was that I passed objects to the function of the state below:
const [selectedBoxesItem, setSelectedBoxesItem] = useState([]);
setSelectedBoxesItem({
selectedUniqueKey: item.id,
selectedItemName: item.ad_headline,
selectedItemPrice: item.ad_price,
selectedItemPic: item.ad_picture
});
So when i did this, I only get the last selected checkbox details passed to the RecordASale screen even though i selected more than one checkbox.
This is how i'm getting the details into the RecordASale screen:
const RecordASale = ({route}) => {
return (
{(route.params?.post) ? route.params?.post.map((item, index) => (
<View>
<View key={index}>
<Image source = {{uri: "https://cdn.beraty.com/beraty-ads/"+item.selectedItemPic}} style = {{ width: 30, height: 30 }} />
<Text>{item.selectedItemName}</Text>
<Text>{item.selectedItemPrice}</Text>
</View>
</View>
)) : <Text></Text>}
);
}
I want the same details as above for all selected checboxes to be passed to the other screen, and not just one.
I believe I'm quite close to that but some things are missing in my code. Please help. Thanks.
I only shared the parts I'm having problems with.
You can use useRoute hook
const SelectProducts = ({
route
}) => {
const routes = useRoute();
console.log(routes.params ? .post)
}

Async Storage not working (React native expo), no error given in console, but everytime I reload nothing is stored

I'm trying to use Async Storage for a note app, I created a component called task.js as a template for todos, an navigation.js component for nav, and home.js for the main screen all display with <navigation /> inapp.js, I added a funcction to store object value using async storage, but is not working, everytime I hard reload the app everything will be gone but it is not giving me any errors, I don't know where to start
here is my Home.js
import React, {useState} from 'react';
import { Keyboard, KeyboardAvoidingView, Platform, StyleSheet, Text,
TextInput, TouchableOpacity, View, SafeAreaView, ScrollView, Image } from 'react-native';
import Task from '../components/Task';
import AsyncStorage from '#react-native-async-storage/async-storage';
export default function Home({ navigation }) {
const [task, setTask] = useState();
const [taskItems, setTaskItems] = useState([]);
React.useEffect ( () => {
save(taskItems);
}, [taskItems])
React.useEffect (() => {
getsave();
}, [])
const handleAddTask = () => {
Keyboard.dismiss();
setTaskItems([...taskItems, task])
setTask(null);
}
const completeTask = (index) => {
let itemsCopy = [...taskItems];
itemsCopy.splice (index, 1);
setTaskItems(itemsCopy)
}
const save = async taskItems =>{
try {
const savetask = JSON.stringify(taskItems)
await AsyncStorage.setItem('tasksave', savetask)
} catch (e) {
console.log(e);
}
};
const getsave = async () => {
try {
const taskItems = await AsyncStorage.getItem('tasksave');
if (taskItems != null){
setTaskItems(JSON.parse(taskItems));
}
} catch (error) {
console.log(e);
}
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.tasksWrapper}>
<Text style={styles.sectionTitle}>Your stuff:</Text>
<TouchableOpacity onPress={() => navigation.navigate('About')}>
<Text style={styles.about}>About</Text>
</TouchableOpacity>
<ScrollView style={styles.items}>{
taskItems.map((item, index) => {
return (
<View key={index}>
<TouchableOpacity onPress={ () => navigation.navigate("Gas", {item})}>
<Task text={item} navigation={navigation} />
</TouchableOpacity>
<TouchableOpacity onPress={() => completeTask(index)} style={styles.deleteW}>
<Image style={styles.delete} source={require('../components/remove.png')}></Image>
</TouchableOpacity>
</View>
)
})
}
</ScrollView>
</View>
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : "height"}
style={styles.textwrapper}>
<TextInput style={styles.input} placeholder={'message'} value={task} onChangeText={text => setTask(text)}></TextInput>
<TouchableOpacity onPress={() => handleAddTask()}>
<View style={styles.addWrap}>
<Text style={styles.add}>+</Text>
</View>
</TouchableOpacity>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
Here's my Task.js:
import React from "react";
import { View, Text, StyleSheet, Image, TouchableOpacity } from "react-native";
const Task = (props, {navigation}) => {
return (
<View style={styles.item}>
<View style={styles.itemleft}>
<Image style={styles.lightball} source={require('./arabic.png')}></Image>
<Text style={styles.itemtext}>{props.text}</Text>
</View>
<Image style={styles.arrow} source={require('./rightarrow.png')}></Image>
</View>
)
}
const styles = StyleSheet.create({
export default Task;
I hope is a quick read, I took out all the style stuff but this is still kinda long sorry, if you think it has something to do with my app.js or nav.js I can give you those too, I usually slove these bugs on my own but I just have no idea where to begin cause I'm not getting any error messages, thank you

How to push data in the setState of array-type state?

I am having a state data. I wanted to push a new entry of the form
{ task:'ANy task',key:Math.random().toString() }
in the data array while using setData.
I had tried many ways mentioned here, but don't klnow why its not working.
Here's my code.
import React, { useState } from "react";
import { StyleSheet, Text, View, TextInput, Button } from "react-native";
import { StatusBar } from "expo-status-bar";
const Addtask = ({navigation}) => {
const [data,setData] = useState([]);
console.log("from add = ",data)
const [task, setTask] = useState("");
const handleSubmit = () => {
console.log("submit pressed for task = ", task)
const updatedData = [...data,{
task:task,
key: Math.random().toString(),
}]
//here i am setting the data
setData(prevState => [...prevState,updatedData]);
console.log("data after adding task",data)
navigation.navigate("Tasklist",{data:data})
}
return (
<View style={styles.container}>
<StatusBar style="light" backgroundColor="midnightblue" />
<View>
<Text style={styles.text}>Add Task Here</Text>
</View>
<View>
<TextInput
style={styles.input}
onChangeText={setTask}
value={task}
onChange={setTask}
placeholder="Type your task"
keyboardType="ascii-capable"
/>
</View>
<View style={styles.buttoms}>
<View style={{margin:4}}>
<Button color={'red'} onPress={()=>{navigation.goBack()}} title="Cancel"></Button>
</View>
<View style={{margin:4}}>
<Button color={'lightblue'} onPress={()=>setTask('')} title="Clear"></Button>
</View>
<View style={{margin:4}}>
<Button color={'green'} onPress={handleSubmit} title="Save"></Button>
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
.
.
.
});
export default Addtask;
To debug, I had used console stmts which reveals that the task value is coming in the handleSubmit() correctly but it is not getting pushed.
Log
submit pressed for task = Morning bike ride.
data after adding task Array []
Because You are already have
const updatedData = [...data,{
task:task,
key: Math.random().toString(),
}]
You don't need setData(prevState => [...prevState,updatedData]) you can just assign the updatedData to setData like setData(updatedData)
You can use
setData(current => [...current, {
task:task,
key: Math.random().toString(),
}])
Then you don't need updatedData

Remove an item in AsyncStorage using FlatList

Sorry for the inexperience, but how do I remove an Item in Async Storage renderized in Flat List, for example:
This is my component that creates a flatlist
export default function Saved() {
const [colors, setColors] = useState([]);
useEffect(() => {
async function getStorage() {
const nomeStorage = await AsyncStorage.getAllKeys();
if (nomeStorage != null) {
setColors(nomeStorage);
}
}
getStorage();
}, [colors])
return (
<View style={styles.body}>
<FlatList
data={colors}
keyExtractor={(item) => item}
renderItem={({ item }) => <Saveds data={item} />}
/>
</View>
);
}
and this is my FlatList renderized component
export default function Saveds(props) {
return (
<View>
<View style={styles.Boxes}>
<Box color={props.data}>
<Badge
badgeStyle={styles.badge}
value={<Text style={styles.color}>{props.data}</Text>}
/>
</Box>
<TouchableOpacity style={styles.btn}>
<Icon name={'trash-outline'} color={'#FFF'} size={30} />
</TouchableOpacity>
</View>
</View>
);
}
I need one way to when I click in my TouchableOpacity, I delete the selected data in my AsyncStorage.
The name in my AsyncStorage is the same as the value, so I can delete the AsyncStorage getting the value of my props.data.
Anyone can help me?
Deleting from your async storage should be as easy as just calling AsyncStorage.removeItem(key)
I had some similar functionality in an app that I made a while ago, I attached the delete function call to the onLongPress prop of touchableOpacity:
<TouchableOpacity
onPress={() => navigation.navigate('UserScreen', data)}
onLongPress={handleLongPress}>
<View style={styles.container}>
// ...
</View>
</TouchableOpacity>
And earlier in the component, I defined a function that handles the deleting:
const handleLongPress = async () => {
// In your instance, you should be OK to replace data.key with props.data
await AsyncStorage.removeItem(data.key);
/* I then had another function that was passed in as a prop
to update another component when the deletion had taken place */
await onChange();
};

Categories