React Native data not updating when firebase updated - reactjs

I am working on a project, I have done it 99%, but there's a problem.
Why the data doesn't change when I updated it?
In firebase the data has changed but my list still displays the previous data.
This is my code:
import React from 'react';
import { StyleSheet, Text, View, ListView,TouchableOpacity,Image,Alert,ActivityIndicator } from 'react-native';
import { Button, List, ListItem } from 'native-base'
import firebase from '../routes/set';
var data = []
export default class Menu extends React.Component {
constructor(props) {
super(props);
this.ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 })
this.state = {
listViewData: data,
loading:false
}
}
componentWillMount() {
var that = this
firebase.database().ref('/user').on('child_added', function (data) {
var newData = [...that.state.listViewData]
newData.push(data)
that.setState({ listViewData: newData })
})
}
async deleteRow(data,secId, rowId, rowMap) {
await firebase.database().ref('user/' + data.key).set(null)
rowMap[`${secId}${rowId}`].props.closeRow();
var newData = [...this.state.listViewData];
newData.splice(rowId, 1)
this.setState({ listViewData: newData });
}
render() {
return (
this.state.listViewData<1?<ActivityIndicator/>:
<View style={styles.container}>
<List
enableEmptySections
dataSource={this.ds.cloneWithRows(this.state.listViewData)}
renderRow={(data,secId,rowId,rowMap)=>
<ListItem>
<TouchableOpacity onLongPress={()=>
Alert.alert(null,'data',
[
{text:'delete',onPress:()=>this.deleteRow( data,secId, rowId, rowMap)},
{text:'edit',onPress:()=>this.props.navigation.navigate('Edit',{data:data,secId:secId,rowId:rowId,rowMap:rowMap})},
{text:'cancel',onPress:()=>null}
])
}>
<View style={{flexDirection:'row', marginLeft:20,marginTop:20}}>
<Image source={data.val().avatarSource} style={{width:50,height:50,borderRadius:30}}/>
<View style={{flexDirection:'column',marginLeft:20}}>
<Text> {data.val().name}</Text>
<Text> {data.val().age}</Text>
<Text> {data.val().gender}</Text>
<Text> {data.val().dob}</Text>
</View>
</View>
</TouchableOpacity>
</ListItem>
}
renderRightHiddenRow={( secId, rowId, rowMap,data) =>
<Button full danger onPress={() => this.deleteRow(secId, rowId, rowMap, data)}>
</Button>
}
/>
<TouchableOpacity onPress={()=>this.props.navigation.navigate('Daftar')}>
<Text>Insert</Text>
</TouchableOpacity>
</View>
);
}m
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});

Related

React native state is not working correctly

I have this component :
import React, { Component } from "react";
import { Text, View, Image, TouchableOpacity } from "react-native";
import { styles } from "./styles";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { getAccounts } from "../../redux/user/selectors";
const UserMenuAccount = ({ active, account, balance }) => (
<View style={styles.accountContainer}>
<Image
source={require("../../../assets/Usermenu/check.png")}
style={[styles.icon, { opacity: active ? 1 : 0 }]}
/>
<Text style={styles.text}>{account}</Text>
<Text style={[styles.text, { opacity: 0.5 }]}>{balance} ETH</Text>
</View>
);
class UserMenuAccounts extends Component {
state = {
userAccounts: [],
};
updateData = index => {
const {userAccounts} = this.state
const { GetAccounts } = this.props;
const data =[...GetAccounts]
data[index].isActive = data[index].isActive ? false : true
this.setState({userAccounts:data})
}
render() {
const { userAccounts } = this.state;
const { GetAccounts } = this.props;
return (
<View>
<Text style={{ opacity: 0.5, fontSize: 16, fontWeight: "bold" }}>
Click to switch
</Text>
{userAccounts.map((users, index) => {
return (
<TouchableOpacity onPress={()=>this.updateData(index)}>
<UserMenuAccount
account={`${users}`}
balance={0}
active={active.isActive || false}
key={index}
/>
</TouchableOpacity>
);
})}
</View>
);
}
}
const mapStateToProps = createStructuredSelector({
GetAccounts: getAccounts,
});
export default connect(mapStateToProps, null)(UserMenuAccounts)
;
The problem is I'm trying to setState active to only one UserMenuAccout but it sets active for all of them , could you please suggest me the way I can fix this ? thanks in advance , I'm grabbing accounts from redux store and mapping them as shown in the code
Try this might help
class UserMenuAccounts extends Component {
constructor(props){
this.state = {
userAccounts: props.GetAccounts,
};
}
updateData(index){
const data = […this.state.userAccounts];
data[index].isActive = data[index].isActive? false: true;
this.setState({userAccounts: data};
}
render() {
const { userAccounts } = this.state;
return (
<View>
<Text style={{ opacity: 0.5, fontSize: 16, fontWeight: "bold" }}>
Click to switch
</Text>
{userAccounts.map((users, index) => {
return (
<TouchableOpacity onPress={()=> this.updateData(index)}>
<UserMenuAccount
account={`${users}`}
balance={0}
active={users.isActive || false}
key={index}
/>
</TouchableOpacity>
);
})}
</View>
);
}
}

Filtering simple Flatlist

I want to filter this simple flatlist through a search bar. How do I code it so that whatever I write something on the input text it filters the flatlist? Could you help me completing it?
import React from 'react';
import { StyleSheet, Text, View, SafeAreaView, TextInput, TouchableOpacity, LayoutAnimation, Image, FlatList, ScrollView } from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import {ListItem, SearchBar} from 'react-native-elements';
export default class HomeScreen extends React.Component{
render() {
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>Home</Text>
</View>
<View style={styles.container1}>
<Icon name={"ios-search"} style={styles.icon}/>
<TextInput style={styles.inputBox}
underlineColorAndroid='rgba(0,0,0,0)'
placeholder="Procura aqui"
placeholderTextColor = "white"
selectionColor="black"
keyboardType="default"
/>
</View>
<View style={styles.flatlist}>
<FlatList
data = {[
{key:'Tiago'},
{key:'Ricardo'},
{key:'Beatriz'},
{key:'Miguel'},
{key:'Simão'},
{key:'David'}
]}
renderItem={({item}) => <Text style={styles.item}>{item.key}</Text>}
/>
</View>
</View>
);
}
}
You should have a state value for searchtext and filter the array based on that. the component should be as below.
export default class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
searchText: '',
};
}
render() {
//Data can be coming from props or any other source as well
const data = [
{ key: 'Tiago' },
{ key: 'Ricardo' },
{ key: 'Beatriz' },
{ key: 'Miguel' },
{ key: 'Simão' },
{ key: 'David' },
];
const filteredData = this.state.searchText
? data.filter(x =>
x.key.toLowerCase().includes(this.state.searchText.toLowerCase())
)
: data;
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>Home</Text>
</View>
<View style={styles.container1}>
<Icon name={'ios-search'} style={styles.icon} />
<TextInput
style={styles.inputBox}
underlineColorAndroid="rgba(0,0,0,0)"
placeholderTextColor="white"
selectionColor="black"
keyboardType="default"
onChangeText={text => this.setState({ searchText: text })}
value={this.state.searchText}
/>
</View>
<View style={styles.flatlist}>
<FlatList
data={filteredData}
renderItem={({ item }) => (
<Text style={styles.item}>{item.key}</Text>
)}
/>
</View>
</View>
);
}
}
Please provide flatlist data from the state so you can control it while searching. Assuming if you want to bring those results at the top that matches your search text, you can do something like the below code. Firstly add onChangeText prop to the textinput and handle the input like this.
filterItems = (search_text) => {
var items = [...this.state.data];
var filtered = [];
if (search_text.length > 0) {
filtered = items.sort(
(a, b) => b.includes(search_text) - a.includes(search_text),
);
this.setState({data: filtered});
} else {
filtered = items.sort((a, b) => b - a);
this.setState({data: filtered});
}
};

How can I make componentDidMount render again?

I'm fetching api(makeup API) in Explore component and using it also in Explorebutton.
Im taking brands as a button in ExploreButtons. When i click button in FlatList element in ExploreButtons I want to see images from api in second FlatList in ExploreButtons. Is there a way componentDidMount can rerender when i click button?
import React, { Component } from 'react'
import { View } from 'react-native'
import ExploreButtons from './ExploreButtons'
export default class Explore extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
makeupApi: 'http://makeup-api.herokuapp.com/api/v1/products.json',
}
}
callbackFunction = (item) => {
this.setState({
makeupApi: 'http://makeup-api.herokuapp.com/api/v1/products.json?brand=' + item,
})
}
async componentDidMount() {
try {
const response = await fetch(this.state.makeupApi);
const responseJson = await response.json();
this.setState({
isLoading: false,
dataSource: responseJson,
}, function () {
});
const reformattedArray = this.state.dataSource.map(obj => {
var rObj = {};
rObj = obj.brand;
return rObj;
});
this.setState({
duplicatesRemoved: reformattedArray.filter((item, index) => reformattedArray.indexOf(item) === index)
})
}
catch (error) {
console.error(error);
}
};
render() {
console.log(this.state.makeupApi)
return (
<View style={{ flex: 1 }}>
<ExploreButtons
api={this.state.dataSource}
removedDuplicatesFromAPI={this.state.duplicatesRemoved}
parentCallback={this.callbackFunction}
makeupApi= {this.state.makeupApi} />
</View>
)
}
}
export default class ExploreButtons extends Component {
getBrandImages = (item) => {
this.props.parentCallback(item)
}
render() {
return (
<View style={{ flex: 1 }}>
<View>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={this.props.removedDuplicatesFromAPI}
renderItem={({ item }) =>
<TouchableOpacity
style={styles.exploreButtons}
onPress={() => {
this.getBrandImages(item)
}}
>
<Text>{item}</Text>
</TouchableOpacity>
}
keyExtractor={item => item}
/>
</View>
<View>
<FlatList
data={this.props.api}
renderItem={({ item }) =>
<View>
<Image source={{ uri: item.image_link }}
style={{
alignSelf: "center",
width: '100%',
height: 300,
}} />
</View>
}
keyExtractor={item => item.id.toString()} />
</View>
</View>
)
}
}
You could just put all the logic inside componentDidMount on another function and call it when you call the callback. As a first very rough approach this would work:
Notes: you don't really need the API URL in the state, put the item on the state and construct the URL based on it.
import React, { Component } from 'react';
import { View } from 'react-native';
import ExploreButtons from './ExploreButtons';
export default class Explore extends Component {
API_URL = 'http://makeup-api.herokuapp.com/api/v1/products.json';
constructor(props) {
super(props);
this.state = {
isLoading: true,
item: null,
dataSource: null,
duplicatesRemoved: [],
};
}
getAPIURL(item) {
if(!item){
return API_URL
}
return `${API_URL}?brand=${item}`;
}
async fetchData(item) {
try {
const url = getAPIURL(item);
const response = await fetch(url);
const responseJson = await response.json();
this.setState({
isLoading: false,
dataSource: responseJson,
item,
});
const reformattedArray = responseJSON.map(({ brand }) => brand);
this.setState({
duplicatesRemoved: reformattedArray.filter(
(item, index) => reformattedArray.indexOf(item) === index,
),
});
} catch (error) {
console.error(error);
}
}
async componentDidMount() {
fetchData();
}
render() {
const { dataSource, duplicatesRemoved, item } = this.state;
return (
<View style={{ flex: 1 }}>
<ExploreButtons
api={dataSource}
removedDuplicatesFromAPI={duplicatesRemoved}
parentCallback={this.fetchData}
makeupApi={getURL(item)}
/>
</View>
);
}
}
export default class ExploreButtons extends Component {
getBrandImages = item => {
this.props.parentCallback(item);
};
render() {
const { removedDuplicatesFromAPI, api } = this.props;
return (
<View style={{ flex: 1 }}>
<View>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={removedDuplicatesFromAPI}
renderItem={({ item }) => (
<TouchableOpacity
style={styles.exploreButtons}
onPress={() => {
this.getBrandImages(item);
}}
>
<Text>{item}</Text>
</TouchableOpacity>
)}
keyExtractor={item => item}
/>
</View>
<View>
<FlatList
data={api}
renderItem={({ item }) => (
<View>
<Image
source={{ uri: item.image_link }}
style={{
alignSelf: 'center',
width: '100%',
height: 300,
}}
/>
</View>
)}
keyExtractor={item => item.id.toString()}
/>
</View>
</View>
);
}
}
How can I make componentDidMount render again?
Not sure what you mean, but I think what you are asking is How can I make componentDidMount *run* again?, and to do that, you would need to have the same code in callbackFunction to run that again. componentDidMount will only run after the first time the component render.
Also notice that if you want to rerender the FlatList you need to pass extraData so it know that it needs to rerender.

React native Tried to get frame for out of range index NaN (MYSQL database)

So I started learning react-native from videos and they have used ListView but as the ListView will be deprecated soon and will be removed. I get to know that FlatList will be the proper replacement but being a beginner I am not able to migrate to Flatlist.
Error message
ListView has been removed from React Native.See link for more information or use 'deprecated-react-native-listview'
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
FlatList,
ActivityIndicator,
} from 'react-native';
import { createStackNavigator, createAppContainer } from 'react-navigation';
export default class Login extends Component {
static navigationOptions= ({navigation}) =>({
header: null
});
state = {
username : [],
data : []
}
constructor(props) {
super(props);
this.state = {
isLoading: true, // check if json data (online) is fetching
dataSource: [], // store an object of json data
};
}
componentDidMount () {
return fetch("http://172.16.2.109:8090/assessment/getdata2.php?username=test2312")
.then((response) => response.json())
.then((responseJson) => {
// set state value
this.setState({
isLoading: false, // already loading
dataSource: responseJson
});
})
.catch((error) => {
ToastAndroid.show(error.toString(), ToastAndroid.SHORT);
});
}
render() {
const { navigate } = this.props.navigation;
if(this.state.isLoading) {
return(
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
return(
<View style={{flex: 1, paddingTop:20}}>
<FlatList
data={this.state.dataSource}
renderItem={({item}) => {
return (
<View>
<Text style={styles.info}>{item.ascendant} is </Text>
</View>
)
}}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
const styles = StyleSheet.create({
info: {
fontSize: 20,
}
});
Hope this helps!
import { FlatList } from 'react-native';
<FlatList
data={this.state.dataSource}
showsVerticalScrollIndicator={false}
renderItem={(rowData, index) => (
<Text style={styles.rowViewContainer} onPress={this.GetListViewItem.bind(this, rowData.fruit_name)}>{rowData.fruit_name}</Text>
<View
style={{
height: .5,
width: "100%",
backgroundColor: "#000",
}}
/>
)}
keyExtractor={(item, index) => index.toString()}
style={{marginTop: 10}}
/>
try this code
import { AppRegistry, StyleSheet, FlatList, Text, View, Alert, ActivityIndicator, Platform} from 'react-native';
class Project extends Component {
constructor(props)
{
super(props);
this.state = {
isLoading: true
}
}
componentDidMount() {
return fetch('https://reactnativecode.000webhostapp.com/FruitsList.php')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson
}, function() {
// In this block you can do something with new state.
});
})
.catch((error) => {
console.error(error);
});
}
FlatListItemSeparator = () => {
return (
<View
style={{
height: 1,
width: "100%",
backgroundColor: "#607D8B",
}}
/>
);
}
GetFlatListItem (fruit_name) {
Alert.alert(fruit_name);
}
render() {
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
return (
<View style={styles.MainContainer}>
<FlatList
data={ this.state.dataSource }
ItemSeparatorComponent = {this.FlatListItemSeparator}
renderItem={({item}) => <Text style={styles.FlatListItemStyle} onPress={this.GetFlatListItem.bind(this, item.fruit_name)} > {item.fruit_name} </Text>}
keyExtractor={(item, index) => index}
/>
</View>
);
}
}
const styles = StyleSheet.create({
MainContainer :{
justifyContent: 'center',
flex:1,
margin: 10,
paddingTop: (Platform.OS === 'ios') ? 20 : 0,
},
FlatListItemStyle: {
padding: 10,
fontSize: 18,
height: 44,
},
});
AppRegistry.registerComponent('Project', () => Project);

Rendering a map with an API in react native

I am currently trying to build a currency convertor with react native but I cannot seem to render this API data back as a map and then into a rate. If anyone can guide me I would really appreciate. I would like to use the API response as a number and mathematically calculate input and provide out which is the users input converted.
The error Im getting is that undefined is not a function(near '...this.state.rates.map...')
The commented out part is to be ignored.
The following is my code:
import React from 'react';
import {AppRegistry, Text, View, Alert} from 'react-native';
const rate = 'https://free.currencyconverterapi.com/api/v6/convert?q=GBP_PKR&compact=ultra&apiKey=59825436b2556cde7205';
class Rates extends React.Component {
constructor(props) {
super(props);
this.state = {
rates: 1,
}
}
componentDidMount() {
fetch('https://free.currencyconverterapi.com/api/v6/convert?q=GBP_PKR&compact=ultra&apiKey=59825436b2556cde7205')
.then(res => res.json())
.then(data => this.setState({rates: data.rate}));
console.log(rates.data);
}
render() {
const rateNumber = this.state.rates.map(function(listItem, index){
<View key={index}>
<Text> {listItem.rate}</Text>
</View>
});
return (
<View>
<Text>Rate for today </Text>
<Text>{rateNumber}</Text>
</View>
)
}
}
export default Rates;
App
import React from 'react';
import {ListView, StyleSheet,RefreshControl, Text,TextInput, TouchableOpacity, View } from 'react-native';
import Rates from './data/Rates';
class App extends React.Component {
constructor(props){
super(props);
this.state = {
gbp: '',
pkr: '',
rate: [],
}
}
/*componentDidMount() {
fetch('https://free.currencyconverterapi.com/api/v6/convert?q=GBP_PKR&compact=ultra&apiKey=59825436b2556cde7205')
.then(response => response.json())
.then(data=> this.setState({ rate: [data] }));
}
*/
convert() {
const gbp = this.state.gbp;
const pkr = this.state.pkr;
const total = gbp*pkr;
console.log(total);
}
render() {
const rate = this.state;
const amount = JSON.stringify(rate.rate);
return (
<View style={styles.container}>
<View>
<Text>{amount}</Text>
<View><Rates/></View>
</View>
<TextInput
placeholder="GBP"
value={this.state.gbp}
onChangeText={(gbp) => this.setState({ gbp })}
keyboardType='numeric'
/>
<TextInput
placeholder="PKR"
value={this.state.pkr}
onChangeText={(pkr) => this.setState({ pkr })}
keyboardType='numeric'
/>
<TouchableOpacity onPress={() =>this.refreshDataFromServer()}>
<Text value={this.state.rate}> Convert</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
export default App;

Resources