How can I store the value of TextInput to local storage and get them when the app starts in react native? - javascript

I'm making a to-do list app. I need to store the input value locally or somewhere so that I can show them in the app even if the app is opened after closing once. Now when I'm closing the app and restarting all the previous values is being vanished. But I want to keep the previous data and If new data is given that will also be stored if I don't delete that manually. How can I solve this problem? I'm using expo.
Here is my code:
import React, { useState } from 'react';
import { View, Text, StyleSheet, Button, FlatList, TouchableOpacity, TextInput, ScrollView } from 'react-native';
import { MaterialIcons } from '#expo/vector-icons'
import Line from './Line';
function App() {
//storing data in array
const [initialElements, changeEl] = useState([
]);
const [value, setValue] = useState('')
const [idx, incr] = useState(1);
const [exampleState, setExampleState] = useState(initialElements);
//add button
const addElement = () => {
if (value != '') {
var newArray = [...initialElements, { id: idx, title: value }];
incr(idx + 1);
setExampleState(newArray);
changeEl(newArray);
}
}
//delete button
const delElement = (id) => {
let newArray = initialElements.filter(function (item) {
return item.id !== id
})
setExampleState(newArray);
changeEl(newArray);
}
//showing item
const Item = ({ title, id }) => (
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center' }}>
<View style={{ borderWidth: 2, margin: 5, flex: 1, padding: 10, borderRadius: 10 }}>
<TouchableOpacity onPress={() => { alert(title) }}>
<Text >{title}</Text>
</TouchableOpacity>
</View>
<TouchableOpacity onPress={() => delElement(id)}>
<MaterialIcons name='delete' size={24} />
</TouchableOpacity>
</View>
);
//calling item for render
const renderItem = ({ item }) => {
<Item title={item.title} id={item.id} />
}
return (
<View style={styles.container}>
<View style={{ alignItems: 'center', marginBottom: 10 }}>
<Text style={{ fontSize: 20, fontWeight: '500' }}> To <Text style={{ color: 'skyblue', fontWeight: '900' }}>Do</Text> List</Text>
<Line />
</View>
<View>
<TextInput onChangeText={text => setValue(text)} placeholder="Enter to Todo" style={{ borderWidth: 2, borderRadius: 10, backgroundColor: 'skyblue', height: 40, paddingLeft: 10, borderColor: 'black' }}></TextInput>
</View>
<View style={{ alignItems: 'center' }}>
<TouchableOpacity onPress={() => addElement()} style={{ marginTop: 10 }}>
<View style={{ backgroundColor: 'black', width: 70, height: 40, justifyContent: 'center', borderRadius: 10 }}>
<Text style={{ color: 'white', textAlign: 'center' }}>Add</Text>
</View>
</TouchableOpacity>
</View>
<FlatList
style={{ borderWidth: 2, padding: 10, flex: 1, backgroundColor: '#EEDDD0', marginTop: 10 }}
data={exampleState}
renderItem={renderItem}
keyExtractor={item => item.id.toString()
}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
margin: 5,
}
})
export default App;

React Native provides a local storage option called AsyncStorage. You might use asyncstorage to save the data locally on the device and get that data inside useEffect() hook on startup.
You can find more about AsyncStorage here.
Although this is deprecated and now community package " #react-native-async-storage/async-storage " is used. The implementation remains the same.

Related

Resulting in a blank screen when listing data with Firebase(Firestore) in React Native

Data listing process with Firebase(Firestore) in React Native.
In the video I watched, he wrote and ran the code that lists the coins belonging to the user using firestore. It did not work even though I wrote the same codes. What is the reason? the code does not give an error, the screen appears, but it does not list.
The problem is that when I did the listing in console it worked. However, when I try to list with FlatList, the screen appears blank.
import { View, Text, SafeAreaView, TouchableOpacity, FlatList } from 'react-native'
import React, { useContext, useState, useEffect } from 'react'
import IconFA5 from 'react-native-vector-icons/FontAwesome5'
import IconFA from 'react-native-vector-icons/FontAwesome'
import { deviceWidth, deviceHeight } from '../../utils/dimensions'
import { AuthContext } from '../../navigation/AuthProvider'
import { Formik, validateYupSchema } from 'formik'
import * as yup from 'yup'
import { firebase } from '#react-native-firebase/auth'
import auth from '#react-native-firebase/auth'
import firestore from '#react-native-firebase/firestore'
const color = '#aaa';
const HomeScreen = ({ navigation }) => {
const renderItem = (item) => {
<TouchableOpacity
key={item.id}
style={{
flexDirection: 'row',
width: '95%',
height: 60,
borderWidth: 1,
margin: 10,
borderRadius: 20,
padding: 10,
justifyContent: 'space-between',
alignItems: 'center'
}}>
<View style={{ flex: 1 }}>
<Text style={{
textAlign: 'left',
fontSize: 18
}}>{item.coinID}</Text>
</View>
<View style={{ flex: 1 }}>
<Text style={{
textAlign: 'right',
fontSize: 18
}}>{item.value}</Text>
</View>
</TouchableOpacity>
}
const { signOut, user } = useContext(AuthContext);
const [currentUser, setCurrentUser] = useState({});
const [userCoinList, setUserCoinList] = useState([]);
const usersColl = firestore().collection('users');
const coinsColl = firestore().collection('coins');
const userCoinsColl = firestore().collection('userCoins');
useEffect(() => {
return usersColl
.doc(user.uid)
.get()
.then(result => {
setCurrentUser(result.data());
userCoinsColl.onSnapshot((querySnapshot) => {
let list = [];
querySnapshot.forEach(doc => {
const { userID, coinID, value } = doc.data();
if (userID == user.uid) {
list.push({
id: doc.id,
userID,
coinID,
value
});
setUserCoinList(list);
}
});
});
});
}, []);
return (
<SafeAreaView style={{ width: '100%', height: '100%' }}>
<View style={{
width: '100%',
height: '90%',
padding: 5,
flex: 1,
alignItems: 'center'
}}>
<View>
<FlatList
style={{ flex: 3, backgroundColor: '#f00' }}
data={userCoinList}
keyExtractor={item => item.id}
renderItem={renderItem}
/>
{console.log(userCoinList)}
</View>
</View>
</SafeAreaView>
)
}
export default HomeScreen
Your renderItem function does not return anything.
Here is a fix.
const renderItem = (item) => {
return <TouchableOpacity
key={item.id}
style={{
flexDirection: 'row',
width: '95%',
height: 60,
borderWidth: 1,
margin: 10,
borderRadius: 20,
padding: 10,
justifyContent: 'space-between',
alignItems: 'center'
}}>
<View style={{ flex: 1 }}>
<Text style={{
textAlign: 'left',
fontSize: 18
}}>{item.coinID}</Text>
</View>
<View style={{ flex: 1 }}>
<Text style={{
textAlign: 'right',
fontSize: 18
}}>{item.value}</Text>
</View>
</TouchableOpacity>
}

How to add information pop-up for TextInput in React Native?

I want to achieve something like this in React Native:
I have a TextInput component and I want to put an icon to the right side. The user can click it, then I can display some text in a modal or in a another component.
Is this possible in react native?
return(
<View style={styles.container}>
<TextInput
placeholder="Állat neve"
value={AllatNev}
style={styles.textBox}
onChangeText={(text) => setAllatNev(text)}
/>
</View>
);
}
)
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: color_theme_light.bodyBackground
justifyContent: 'center',
alignItems:'center'
},
textBox:{
borderWidth:2,
borderColor: color_theme_light.textBoxBorder,
margin:15,
borderRadius:10,
padding: 10,
fontFamily:'Quicksand-Medium'
},
});
Yes -- you can position your info button over the TextInput using absolute positioning and a zIndex, for example:
import * as React from 'react';
import { Text, View, StyleSheet, TextInput, TouchableOpacity } from 'react-native';
export default function App() {
return (
<View style={styles.container}>
<View style={styles.textBoxParent}>
<TextInput style={styles.textBox} placeholder="Állat neve"/>
<TouchableOpacity style={styles.textBoxButton} onPress={() => {
//launch your modal
}}>
<Text>i</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
textBoxParent: {
justifyContent: 'center'
},
textBox:{
borderWidth:2,
borderColor: 'gray',
margin:15,
borderRadius:10,
padding: 10,
},
textBoxButton: {
position: 'absolute',
right: 20,
zIndex: 100,
width: 20,
height: 20,
borderWidth: 1,
borderRadius: 10,
justifyContent: 'center',
alignItems: 'center'
}
});
Working example: https://snack.expo.dev/OFMTc8GHE
Heres a full example of what you want (https://snack.expo.dev/bjzBFuE4W). And below I explain the code.
Fist I made a Modal from react native that takes in modalVisible, setModalVisible, and appears when modalVisible is true.
import * as React from 'react';
import { Text, View, StyleSheet,TextInput ,TouchableOpacity,Modal} from 'react-native';
import { AntDesign } from '#expo/vector-icons';
import { MaterialIcons } from '#expo/vector-icons';
const ModalInfo = ({modalVisible, setModalVisible})=>{
return (
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
setModalVisible(!modalVisible);
}}
>
<View style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}>
<View
style={{
width:200,height:200,backgroundColor:"gray",borderWidth:2,
justifyContent:"center",alignItems:"center"
}}
>
<TouchableOpacity onPress={()=>{setModalVisible(false)}}>
<MaterialIcons name="cancel" size={24} color="black" />
</TouchableOpacity>
</View>
</View>
</Modal>
)
}
Next I made a View to wrap around the textInput so I can also add an svg of the info icon. And then set the outside view to have flexDirection:"row", so everything would be ordered the way you wan't.
const TextInputWithModal = ()=>{
const [modalVisible, setModalVisible] = React.useState(false);
const [AllatNev,setAllatNev]= React.useState("");
return (
<View style={styles.textInputContainer}>
<TextInput
placeholder="Állat neve"
value={AllatNev}
style={styles.textBox}
onChangeText={(text) => setAllatNev(text)}
/>
<TouchableOpacity onPress={()=>{setModalVisible(true)}}>
<AntDesign name="infocirlceo" size={24} color="black" />
</TouchableOpacity>
<ModalInfo modalVisible={modalVisible} setModalVisible={setModalVisible}/>
</View>
)
}
export default function App() {
return (
<View style={styles.container}>
<TextInputWithModal/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems:"center",
},
textInputContainer:{
borderRadius:10,
padding: 10,
flexDirection:"row",
margin:15,
borderWidth:2,
},
textBox:{
fontFamily:'Quicksand-Medium',
marginRight:20,
},
});

How to save route.params with asyncstorage?

Srry if the title makes no sense. Don't know a better title.
How can I save route.params items that I pass to my second screen using AsyncStorage?
In my first screen i have a bunch of data in a FlatList that can be opened with a Modal. Inside that Modal I have a TouchableOpacity that can send the data thats inside the Modal to my second screen. The data that has been passed to the second screen is passed to a FlatList. The data in the FlatList should be saved to AsyncStorage. Tried alot of things getting this to work, but only getting warning message
undefined. Code below is the most recent progress.
Using React Navigation V5.
FIRST SCREEN
const [masterDataSource, setMasterDataSource] = useState(DataBase);
const [details, setDetails] = useState('');
<TouchableOpacity
onPress={() => {
const updated = [...masterDataSource];
updated.find(
(item) => item.id === details.id,
).selected = true;
setMasterDataSource(updated);
navigation.navigate('cart', {
screen: 'cart',
params: {
items: updated.filter((item) => item.selected),
},
});
setModalVisible(false);
}}>
<Text>Add to cart</Text>
</TouchableOpacity>
SECOND SCREEN
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, TouchableOpacity } from 'react-native';
import { useTheme } from '../Data/ThemeContext';
import AsyncStorage from '#react-native-async-storage/async-storage';
import Ionicons from 'react-native-vector-icons/Ionicons';
export default function ShoppingList({ route, navigation }) {
const [shoppingList, setShoppingList] = useState([]);
const { colors } = useTheme();
const todo = () => {
alert('Todo');
};
useEffect(() => {
restoreShoppingListAsync();
}, []);
const shoppingListAsync = () => {
const shoppingList = route.params && route.params.items;
setShoppingList(list);
storeShoppingList(list);
};
const asyncStorageKey = '#ShoppingList';
const storeShoppingListAsync = (list) => {
const stringifiedList = JSON.stringify(list);
AsyncStorage.setItem(asyncStorageKey, stringifiedList).catch((err) => {
console.warn(err);
});
};
const restoreShoppingListAsync = () => {
AsyncStorage.getItem(asyncStorageKey)
.then((stringifiedList) => {
console.log(stringifiedList);
const parsedShoppingList = JSON.parse(stringifiedList);
if (!parsedShoppingList || typeof parsedShoppingList !== 'object')
return;
setShoppingList(parsedShoppingList);
})
.then((err) => {
console.warn(err);
});
};
const RenderItem = ({ item }) => {
return (
<View>
<TouchableOpacity
style={{
marginLeft: 20,
marginRight: 20,
elevation: 3,
backgroundColor: colors.card,
borderRadius: 10,
}}>
<View style={{ margin: 10 }}>
<Text style={{ color: colors.text, fontWeight: '700' }}>
{item.name}
</Text>
<Text style={{ color: colors.text }}>{item.gluten}</Text>
<Text style={{ color: colors.text }}>{item.id}</Text>
</View>
</TouchableOpacity>
</View>
);
};
const emptyComponent = () => {
return (
<View style={{ alignItems: 'center' }}>
<Text style={{ color: colors.text }}>Listan är tom</Text>
</View>
);
};
const itemSeparatorComponent = () => {
return (
<View
style={{
margin: 3,
}}></View>
);
};
return (
<View
style={{
flex: 1,
}}>
<View
style={{
padding: 30,
backgroundColor: colors.Textinput,
elevation: 12,
}}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<TouchableOpacity onPress={() => navigation.goBack()}>
<Ionicons name="arrow-back-outline" size={25} color="#fff" />
</TouchableOpacity>
<Text style={{ color: '#fff', fontSize: 20 }}>Inköpslista</Text>
<TouchableOpacity>
<Ionicons
name="trash-outline"
size={25}
color="#fff"
onPress={() => todo()}
/>
</TouchableOpacity>
</View>
</View>
<View style={{ flex: 1, marginTop: 30 }}>
<FlatList
data={shoppingList}
renderItem={RenderItem}
ListEmptyComponent={emptyComponent}
ItemSeparatorComponent={itemSeparatorComponent}
initialNumToRender={4}
maxToRenderPerBatch={5}
windowSize={10}
removeClippedSubviews={true}
updateCellsBatchingPeriod={100}
showsVerticalScrollIndicator={true}
contentContainerStyle={{ paddingBottom: 20 }}
/>
</View>
</View>
);
}
As you are using async storage to maintain the cart.
I would suggest an approach as below
Update the asyn storage when new items are added to or removed from the cart
Retrieve the items from the cart screen and show the items there
Before you navigate store the items like below
AsyncStorage.setItem(
'Items',
JSON.stringify(updated.filter((item) => item.selected))
).then(() => {
navigation.navigate('Cart', {
items: updated.filter((item) => item.selected),
});
});
The cart screen would be something like below
function Cart({ navigation, route }) {
const [data,setData]=useState([]);
React.useEffect(() => {
async function fetchMyAPI() {
const value = await AsyncStorage.getItem('Items');
setData(JSON.parse(value));
}
fetchMyAPI();
}, []);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Go back" onPress={() => navigation.goBack()} />
<FlatList
data={data}
renderItem={RenderItem}
/>
</View>
);
}
Working Example
https://snack.expo.io/#guruparan/cartexample

How to Pass Props to Modal Component in React Native

I need to pass Flatlist data to modal in react native. Like, on click the item from the flatlist, it shows the modal with data for selected item. Here is my code below
Home.js
import React, { Component } from 'react';
import { StyleSheet, Text, View, FlatList, Image, TouchableOpacity, Alert, Button } from 'react-native';
import { Popup } from './Modal';
import * as productdata from '../data/productdata.js';
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: [],
};
}
componentDidMount() {
this.setState({
isLoading: false,
dataSource: productdata.product,
})
}
popupRef = React.createRef()
onShowPopup = item => {
popupRef.show();
}
onClosePopup = () => {
popupRef.close()
}
ProductList = ({ item }) => (
<View style={styles.listItem}>
<TouchableOpacity onPress={() => this.onShowPopup(item)} style={{ height: 100, width: 100, justifyContent: "center", alignItems: "center" }}>
<Image source={item.photo} style={{ width: 100, height: 100, borderRadius: 15 }} />
</TouchableOpacity>
<View style={{ alignItems: "center", flex: 1, marginTop: 20 }}>
<Text style={{ fontWeight: "bold", fontSize: 22 }}>{item.name}</Text>
<Text style={{ fontSize: 18, fontWeight: "bold" }}>{item.price}</Text>
</View>
<Popup name="Product Details" ref={(target) => popupRef = target} onTouchOutside={this.onClosePopup} />
</View>
);
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.dataSource}
renderItem={this.ProductList}
keyExtractor={item => item.name}
/>
</View>
);
}
}
Modal.js
import React, { Component } from 'react';
import { Modal, Dimensions, TouchableWithoutFeedback, StyleSheet, View, Text, Button, Image, TouchableOpacity } from 'react-native';
const deviceHeight = Dimensions.get("window").height
export class Popup extends Component {
constructor(props) {
super(props)
this.state = {
show: false,
}
}
show = () => {
this.setState({ show: true })
}
close = () => {
this.setState({ show: false })
}
renderOutsideTouchable(onTouch) {
const view = <View style={{ flex: 1, width: '100%' }} />
if (!onTouch) return view
return (
<TouchableWithoutFeedback onPress={onTouch} style={{ flex: 1, width: '100%' }}>
{view}
</TouchableWithoutFeedback>
)
}
renderTitle = () => {
const { name } = this.props
return (
<View style={{ alignItems: 'center' }}>
<Text style={{
color: 'black', fontSize: 20,
fontWeight: 'bold', margin: 15
}}>
{name}
</Text>
</View>
)
}
renderContent = (item) => {
return (
<View style={{ alignItems: 'center', marginBottom: 10 }}>
<View style={styles.card}>
<Text style={{ fontSize: 20, fontWeight: '500', fontWeight: 'bold', alignSelf: 'center', margin: 5 }}/>
<TouchableOpacity style={styles.buttonContainer}>
<Text style={styles.button}>Add to Cart</Text>
</TouchableOpacity>
</View>
</View>
)
}
render() {
let { show } = this.state
const { onTouchOutside, title } = this.props
return (
<Modal animationType={'slide'} transparent={true} visible={show} onRequestClose={this.close}>
<View style={{ flex: 1, backgroundColor: '#000000AA', justifyContent: 'flex-end' }}>
{this.renderOutsideTouchable(onTouchOutside)}
<View style={{
backgroundColor: '#FFFFFF', width: '100%', height: '70%', borderTopRightRadius:20, borderTopLeftRadius: 20, paddingHorizontal: 20, maxHeight: deviceHeight * 5}}>
{this.renderTitle()}
{this.renderContent()}
</View>
</View>
</Modal>
)
}
}
My Problem: I am not able to pass the flatlist item data to a Modal component and have no better idea solving it in this code.
Please help me and if any including, changes or complete solution for perfect understanding for the requirement would be really great. Many Thanks in Advance!
You don't need to duplicate open state in modal/popup. Simply set the style and open. Assume open close state is controlled by parent component, so if rendering modal/popup it is open by definition.
class Popup extends React.Component {
renderOutsideTouchable(onTouch) {
const view = <View style={{ flex: 1, width: '100%' }} />;
if (!onTouch) return view;
return (
<TouchableWithoutFeedback
onPress={onTouch}
style={{ flex: 1, width: '100%' }}>
{view}
</TouchableWithoutFeedback>
);
}
renderTitle = () => {
const { name } = this.props;
return (
<View style={{ alignItems: 'center' }}>
<Text
style={{
color: 'black',
fontSize: 20,
fontWeight: 'bold',
margin: 15,
}}>
{name}
</Text>
</View>
);
};
renderContent = () => {
const { item } = this.props;
return (
<View style={{ alignItems: 'center', marginBottom: 10 }}>
<View style={styles.card}>
<Text
style={{
fontSize: 20,
fontWeight: '500',
// fontWeight: "bold",
alignSelf: 'center',
margin: 5,
}}
/>
<TouchableOpacity style={styles.buttonContainer}>
<Text>Name: {item.name}</Text>
<Text>Price: {item.price}</Text>
<Text>Description: {item.desc}</Text>
<Text>Rating: {item.rating}</Text>
<Text style={styles.button}>Add to Cart</Text>
</TouchableOpacity>
</View>
</View>
);
};
render() {
const { onTouchOutside, title } = this.props;
return (
<Modal
animationType={'slide'}
transparent
visible // <-- visible prop is truthy
onRequestClose={this.close}>
<View
style={{
flex: 1,
backgroundColor: '#000000AA',
justifyContent: 'flex-end',
zIndex: 1000,
}}>
{this.renderOutsideTouchable(onTouchOutside)}
<View
style={{
backgroundColor: '#FFFFFF',
width: '100%',
height: '70%',
borderTopRightRadius: 20,
borderTopLeftRadius: 20,
paddingHorizontal: 20,
maxHeight: deviceHeight * 5,
}}>
{this.renderTitle()}
{this.renderContent()}
</View>
</View>
</Modal>
);
}
}
A react ref isn't necessary for opening a modal/popup to display a specific item. Change the onShowPopup and onClosePopup to set/nullify a clicked on item. Conditionally render the Popup outside the Flatlist.
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: productdata.product,
isLoading: false,
popupItem: null,
};
}
onShowPopup = (popupItem) => {
this.setState({ popupItem });
};
onClosePopup = () => {
this.setState({ popupItem: null });
};
ProductList = ({ item }) => (
<View style={styles.listItem}>
<TouchableOpacity
onPress={() => this.onShowPopup(item)}
style={{
height: 100,
width: 100,
justifyContent: 'center',
alignItems: 'center',
}}>
<Image
source={item.photo}
style={{ width: 100, height: 100, borderRadius: 15 }}
/>
</TouchableOpacity>
<View style={{ alignItems: 'center', flex: 1, marginTop: 20 }}>
<Text style={{ fontWeight: 'bold', fontSize: 22 }}>{item.name}</Text>
<Text style={{ fontSize: 18, fontWeight: 'bold' }}>{item.price}</Text>
</View>
</View>
);
render() {
return (
<View style={styles.container}>
{this.state.popupItem && (
<Popup
name="Product Details"
item={this.state.popupItem} // <-- pass pop item
onTouchOutside={this.onClosePopup}
/>
)}
<FlatList
data={this.state.dataSource}
renderItem={this.ProductList}
keyExtractor={(item) => item.name}
/>
</View>
);
}
}
Expo Snack

How to force Flatlist to re-render after getting a single data?

I've faced an issue with flatlist when I get single data from the server and set these into state and passes into data props, I can't see any update in the render "I'm adding some Loading if I'd not received any data I show an let's say Indicator" so the indicator disappears and I see blank screen!!
FYI, When I enable Hot Reloading and just press Save in my IDE I can see the single Data in my Screen!
So how can I force it to appear the data!
Code
import React, { Component } from "react";
import firebase from "react-native-firebase";
import Icon from "react-native-vector-icons/Ionicons";
import _ from "lodash";
import {
View,
Text,
StyleSheet,
FlatList,
TouchableOpacity,
Image,
Dimensions
} from "react-native";
const { width } = Dimensions.get("screen");
class ListChats extends Component {
constructor(props) {
super(props);
this.state = {
users: [],
noChat: true
};
}
_getUsers = () => {
let currentUser = firebase.auth().currentUser.uid;
let ref = firebase.database().ref(`Messages/${currentUser}`);
let usersKeys = [];
ref.once("value").then(snapshot => {
snapshot.forEach(childsnap => {
usersKeys.push(childsnap.key);
});
let usernames = [];
usersKeys.forEach(key => {
firebase
.database()
.ref("users")
.child(key)
.once("value")
.then(usersShot => {
let username = usersShot.val().username;
usernames.push({ username: username, key: key });
});
});
this.setState({ users: usernames,noChat: false });
});
};
componentDidMount() {
this._getUsers();
}
render() {
if (this.state.noChat) {
console.log("IF", this.state.users);
return (
<View style={styles.container}>
<Image
style={{
width,
height: width * 0.7,
resizeMode: "contain"
}}
source={require("../../assets/empty.gif")}
/>
<Text style={{ alignSelf: "center" }}>No Chats Found</Text>
</View>
);
} else {
console.log("Else", this.state.users);
return (
<View style={styles.container}>
<FlatList
key={Math.random() * 1000}
extraData={this.state} // I'm already added
data={this.state.users}
contentContainerStyle={{ flexGrow: 1 }}
renderItem={({ item }) => {
return (
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate("ChatDetails", {
Key: item.key,
userName: item.username
})
}
>
<View style={styles.parent}>
<Icon name="ios-contact" size={50} color="#4d8dd6" />
<View
style={{
flex: 1,
justifyContent: "flex-start",
alignItems: "flex-start",
marginHorizontal: 25
}}
>
<Text
style={{
color: "#000",
fontSize: 17
// marginHorizontal: 25
// alignSelf: "stretch"
}}
>
{item.username}
</Text>
{/* <Text
style={{
color: "#a1a1a1",
marginHorizontal: 35,
marginVertical: 5,
alignSelf: "stretch"
}}
numberOfLines={1}
lineBreakMode="tail"
>
{item.lastMssg.text}
</Text> */}
</View>
<Icon name="ios-chatboxes" size={25} color="#d6d6d6" />
</View>
</TouchableOpacity>
);
}}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center"
},
parent: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingVertical: 25,
marginHorizontal: 15,
borderBottomWidth: 1,
borderBottomColor: "#eee"
}
});
export default ListChats;
You have used extra data props but the issue is flatlist does a shallow data comparison so when the length of users changes it won't affect the flatlist so replace it with this.state.users
extraData={this.state.users}
You can see in the documentation it says flatlist is implementation of PureCompoment and PureComponent does a shallow comparison thats the reason it is not re-rendering.
https://reactnative.dev/docs/flatlist#extradata
to force the flatlist re-render. just add a boolean lets say "forceUpdate" to your state and whenever an item is added to flatlist data toggle that boolean and pass that to the extra data of your FlatList instead of the state.

Categories

Resources