React Native is Not fetching the latest data from API call - reactjs

I sicerely Apologies if this has been asked before. I am a bit new to react native and react in general.
my react nativee code is not fetcing the latest data
see the code for the list component below
I would deeply appreciate it if you can tell me why this is happening and how to make it pick the latest data
import React, { Component } from "react";
import {
FlatList,
Text,
View,
StyleSheet,
ScrollView,
ActivityIndicator
} from "react-native";
import Constants from "expo-constants";
import { createStackNavigator } from "#react-navigation/stack";
import { toCommaAmount } from "./utilities";
const Stack = createStackNavigator();
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={ExpenseList} />
<Stack.Screen name="NewTransaction" component={ExpenseForm} />
</Stack.Navigator>
);
}
function Item({ title }) {
return (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
}
class ExpenseList extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: null
};
}
componentDidMount() {
return fetch("https://example.com/expense/api/get_all.php")
.then(response => response.json())
.then(responseJson => {
this.setState({
isLoading: false,
dataSource: responseJson.items
});
})
.catch(error => console.log(error));
}
render() {
if (this.state.isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator />
</View>
);
} else {
let myExpenses = this.state.dataSource.map((val, key) => {
return (
<View key={key} style={styles.item}>
<Text>
{val.title} {toCommaAmount(val.amount)}
</Text>
<Text>{val.date_time}</Text>
</View>
);
});
return <View style={styles.container}>{myExpenses}</View>;
}
}
}
export default ExpenseList;

ComponentDidMount is a void function. I assume you have this problem because you try to return the result of the fetch execution.

Can you remove your api call out of componentDidMount(), because it gets invoked only once. Rename it to getRealData() and attach it to a button click. So every click on button will being up latest data from backend.

Related

React native get information from json data

I'm a beginner in react native, so, what I wanted to do is to get an item.CPU in an array to be able to put it in sampleData so I can render the information in a graphic using PureChart.
import React, { Component } from 'react';
import { StyleSheet, ActivityIndicator, SafeAreaView, FlatList, Text, View } from
'react-native';
import PureChart from 'react-native-pure-chart';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {data: []};
}
componentDidMount() {
fetch('http://192.168.1.34:3000/users').then((response) =>
response.json()).then((json)=> this.setState({data : json.param})).catch((error) =>
console.error(error));
}
render() {
const { data } = this.state;
let sampleData = [30, 200, 170, 250, 10];
return (
<View style = {styles.container}>
<PureChart data={sampleData} type='line' />
<SafeAreaView >
<FlatList data ={data} keyExtractor={({ID}, index) => ID} renderItem={({item})=>(<Text>{item.CPU}</Text>)}/>
</SafeAreaView>
</View>
// <SafeAreaView style={styles.container}>
// <FlatList
// data={data}
// keyExtractor={({ ID }, index) => ID}
// renderItem={({ item }) => (<Text>{item.CPU}, {item.RPM}</Text>)}
// />
//
);
}
};
const styles = StyleSheet.create({
container: {
top: "50%",
},
});
It might be easier to pass in the json data inside a prop. You should make this request before you load the view. That way you remove potential bugs that arise from things like server errors, errors from improperly handling loading etc.

Expo - React Native: Firebase Phone Number Authentication is failing

I need help. I am trying to send a verification code with phone number authentication in EXPO and It works well in development. But I don't know why the same code is not working in production. I am getting this error,
verifyphonenumber failed: second argument “applicationVerifier” must be an implementation of firebase.auth.ApplicationVerifier
The FirebaseRecaptchaVerifierModal is not showing up when I click to send the verification code. I think, it is failing in the second argument of verifyPhoneNumber() but I can’t see how I can correct this.recaptchaVerifier.current using the Doc. Here is my code
import React, {Component} from 'react';
import { View, TextInput, TouchableOpacity } from 'react-native';
import firebase from 'firebase';
import config from '../../firebase.config';
import { FirebaseRecaptchaVerifierModal } from 'expo-firebase-recaptcha';
import {ToastError, ToastSuccess} from '../../utils/Toast';
import styles from './styles';
class PhoneAuthScreen extends Component {
constructor(props) {
super(props);
this.state = {
phoneNumber: '',
},
this.recaptchaVerifier = React.createRef();
}
onChangeText(key, value) {
this.setState({
[key]: value
})
}
signInWithPhoneNumber = async () => {
const { phoneNumber } = this.state;
const phoneProvider = new firebase.auth.PhoneAuthProvider();
try {
const verificationId = await phoneProvider.verifyPhoneNumber(
phoneNumber, this.recaptchaVerifier.current);
this.setState({ verificationId: verificationId });
ToastSuccess('Verification code has been sent to your phone.');
} catch (err) {
ToastError(err.message);
}
}
render() {
return (
<View style={styles.verifyPage}>
<View>
<View style={styles.telSection}>
<TextInput
style={styles.telInput}
placeholder='+1 201-555-0123'
placeholderTextColor='#080040'
keyboardType={'phone-pad'}
returnKeyType='done'
autoCapitalize='none'
autoCorrect={false}
secureTextEntry={false}
ref='PhoneInput'
autoCompleteType="tel"
value={this.state.phoneNumber}
onChangeText={(val) => {
this.onChangeText('phoneNumber', val)
}
}
/>
</View>
</View>
<FirebaseRecaptchaVerifierModal
ref={this.recaptchaVerifier}
firebaseConfig={config}
/>
<TouchableOpacity style={styles.confirmBtn} onPress={this.signInWithPhoneNumber}>
<Text style={styles.confirmText}>Send Verification Code</Text>
</TouchableOpacity>
</View>
)
};
}
export default PhoneAuthScreen;

React-Redux | Productlist | Add/Remove w/ BindActionCreator

I'm trying to create an product list in React where I can add and remove products.
I started to do some research on how I could do this using the redux framework/platform and react native
I already have an function productList container, product component and cartList, cartProduct component.
My problems are:
Products: I can only add products and not remove
Cart: Vice versa + the cart does not get updated on the status of the cart items.
I've added bindActionCreator but don't know how to apply it to my productList yet.
What do I expect to happen?
I'm trying to add and remove products from the react store in the same container/component.
How can I do this? Is my approach correct or am I completely wrong ?
A thank you in advance.
ProductActionCreators
export const ADD_TO_CART = 'ADD_TO_CART'
export const REMOVE_FROM_CART = 'REMOVE_FROM_CART'
export function addItemToCart(row) {
return {
type:'ADD_TO_CART',
payload: row, qty
}
}
export function removeTodo(row) {
return {
type:'REMOVE_FROM_CART' ,
payload: row, qty
}
}
ProductList(simplified)
import React from 'react';
import { Component } from 'react';
import {
View,
StyleSheet,
Text
} from 'react-native';
import Products from '../components/Products';
import { bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import * as ProductActionCreators from '../actions/ProductActionCreators'
export class ProductList extends React.Component {
static navigationOptions = {
header: null,
};
constructor(props) {
super(props);
const { rows } = this.props.navigation.state.params;
const arrays = Object.values( {rows});
this.state = {
arrays,
filteredProducts: arrays,
};
const { dispatch } = props
this.boundActionCreators = bindActionCreators(ProductActionCreators, dispatch)
console.log(this.boundActionCreators)
}
render() {
return (
<View style={styles.container} >
<Text style={styles.title} >
{this.state.arrays[0].name}
</Text>
<Products products={this.state.arrays[0].data} onPress=
//Trying to change this to multiple actions
{this.props.addItemToCart}/>
</View>
)
}
}
const qty = 0;
const mapDispatchToProps = (dispatch) =>{
//need to add BindActionCreator
return{
addItemToCart:(row) => dispatch({
type:'ADD_TO_CART', payload: row, qty
}),
removeItem:(product) => dispatch ({
type:'REMOVE_FROM_CART' , payload: product, qty
})
}
}
export default connect(null, mapDispatchToProps) (ProductList);
Product(simplified)
import React, { Component } from "react";
import {
View,
Text,
TouchableOpacity,
TextInput,
FlatList,
} from "react-native";
import Icon from "react-native-vector-icons/Ionicons";
class Products extends Component {
constructor(props) {
super(props);
const { products } = this.props;
this.state = {
products,
filteredProducts: products,
};
}
renderProducts = (products) => {
return (
<View key={products.index}>
<View>
<Icon name={products.item.icon} color="#DD016B" size={25} />
</View>
<View>
<Text style={styles.name}>
{products.item.name}
</Text>
<Text>
€ {products.item.price}
</Text>
</View>
<View style={styles.buttonContainer}>
<TouchableOpacity onPress={() => this.props.onPress(products.item)} >
<Icon name="ios-add" color="white" size={25} />
</TouchableOpacity>
<TouchableOpacity onPress={() => this.props.onPress(products.item)} >
<Icon name="ios-remove" color="white" size={25} />
</TouchableOpacity>
</View>
</View>
)
}
render() {
return (
<View>
<FlatList
style={styles.listContainer}
data={this.state.filteredProducts}
renderItem={this.renderProducts}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
export default Products;
reducers/cartItems
const cartItems = (state = [], action) => {
switch (action.type)
{
case 'ADD_TO_CART':
if (state.some(cartItem => cartItem.id === action.payload.id)) {
// increase qty if item already exists in cart
return state.map(cartItem => (
cartItem.id === action.payload.id ? { ...cartItem, qty: cartItem.qty + 1 } : cartItem
));
}
return [...state, { ...action.payload, qty: 1 }];
// else add the new item to cart
case 'REMOVE_FROM_CART':
return state
.map(cartItem => (cartItem.id === action.payload.id ? { ...cartItem, qty: cartItem.qty - 1 } : cartItem))
.filter(cartItem => cartItem.qty > 0);
}
return state
}
export default cartItems
store/Index
import {createStore} from 'redux';
import cartItems from '../reducers/carItems';
export default store = createStore(cartItems)
App structure (simplified)
Main folder
↳
Containers(folder)
↳
ProductsList.js
CartList.js
Components(folder)
↳
Product.js
cartProduct.js
Reducers(folder)
↳
carItems.js
Actions(folder)
↳
ProductActionCreators.js
Navigation(folder)
↳
AppNavigator,js
MainTabNavigator.js
Assets(folder for images etc.)
Store(folder)
↳
index.js
App.JS
Data.JS (using static JSON file for this development phase)
You have two different actions, addItemToCart, removeItem which you define in mapDispatchToProps. Now that you specify a mapDispatchToProps argument to connect, the dispatch method is not available as a prop to the connected component, instead the method returns by mapDispatchToProps are only available
Second, you don't need to use bindActionCreators and definitely not in the component. MapDispatchToProps can simply be an object and connect will use dispatch internally.
Third, you need to pass the add and remove actions both to the child component.
Fourth you can pass multiple actions on to Product component simply as prop
Your code would look like
ProductActionCreators.js
export const ADD_TO_CART = 'ADD_TO_CART'
export const REMOVE_FROM_CART = 'REMOVE_FROM_CART'
export function addItemToCart(row) {
return {
type:'ADD_TO_CART',
payload: row
}
}
export function removeItem(item) {
return {
type:'REMOVE_FROM_CART' ,
payload: item
}
}
ProductList
import React from 'react';
import { Component } from 'react';
import {
View,
StyleSheet,
Text
} from 'react-native';
import Products from '../components/Products';
import { connect } from 'react-redux';
import { addItemToCart, removeItem } from '../actions/ProductActionCreators';
export class ProductList extends React.Component {
static navigationOptions = {
header: null,
};
constructor(props) {
super(props);
const { rows } = this.props.navigation.state.params;
const arrays = Object.values( {rows});
this.state = {
arrays,
filteredProducts: arrays,
};
}
render() {
return (
<View style={styles.container} >
<Text style={styles.title} >
{this.state.arrays[0].name}
</Text>
<Products products={this.state.arrays[0].data} addItemToCart={this.props.addItemToCart} removeItem={this.props.removeItem}/>
</View>
)
}
}
const mapDispatchToProps = {
addItemToCart,
removeItem
}
export default connect(null, mapDispatchToProps) (ProductList);
Product
import React, { Component } from "react";
import {
View,
Text,
TouchableOpacity,
TextInput,
FlatList,
} from "react-native";
import Icon from "react-native-vector-icons/Ionicons";
class Products extends Component {
constructor(props) {
super(props);
const { products } = this.props;
this.state = {
products,
filteredProducts: products,
};
}
renderProducts = (products) => {
return (
<View key={products.index}>
<View>
<Icon name={products.item.icon} color="#DD016B" size={25} />
</View>
<View>
<Text style={styles.name}>
{products.item.name}
</Text>
<Text>
€ {products.item.price}
</Text>
</View>
<View style={styles.buttonContainer}>
<TouchableOpacity onPress={() => this.props.addItemToCart(products.item)} >
<Icon name="ios-add" color="white" size={25} />
</TouchableOpacity>
<TouchableOpacity onPress={() => this.props.removeItem(products.item)} >
<Icon name="ios-remove" color="white" size={25} />
</TouchableOpacity>
</View>
</View>
)
}
render() {
return (
<View>
<FlatList
style={styles.listContainer}
data={this.state.filteredProducts}
renderItem={this.renderProducts}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
export default Products;
Code looks alright for most part.
The react-redux connect part in ProductList looks off. qty is always 0. It should be 1.
Also, mapStateToProps should be present to get the products from the cart.
In your ProductList, I'd approach the action binding like so:
const mapDispatchToProps = (dispatch) =>{
return bindActionCreators({
addItemToCart: (row, qty) => dispatch({
type:'ADD_TO_CART', payload: {row, qty}
}),
removeItem: (product, qty) => dispatch({
type:'REMOVE_FROM_CART' , payload: {product, qty}
})
})
}
export default connect(null, mapDispatchToProps)(ProductList);
Remove the action binding from your component's constructor as that's unnecessary.
You may want to split the code out into a Container/Component/ HOC approach, as I find it makes code far easier to read. As you've defined your actions in separate file, I'd also import these rather than redeclaring them.
If you follow this advice, you'd end up with the following:
container.js
import { bindActionCreators } from 'redux';
import ProductList from './product-list';
// Actions
import { addItemToCart, removeItem } from './actions';
function mapStateToProps(state) {
return {}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
addItemToCart,
removeItem,
})
}
export default connect(mapStateToProps, mapDispatchToProps)(ProductList);
product-list.js
import React from 'react';
import { View, Text } from 'react-native';
import Products from '../components/Products';
export class ProductList extends React.Component {
static navigationOptions = {
header: null,
};
constructor(props) {
super(props);
const { rows } = this.props.navigation.state.params;
const arrays = Object.values( {rows});
this.state = {
arrays,
filteredProducts: arrays,
};
this.handleProductPress = this.handleProductPress.bind(this);
}
handleProductPress(e) {
e.preventDefault();
// This is pseudo code...
this.props.addItemToCart(e.target.value, 1);
return;
}
render() {
return (
<View style={styles.container} >
<Text style={styles.title} >
{this.state.arrays[0].name}
</Text>
<Products products={this.state.arrays[0].data} onPress={this.handleProductPress} />
</View>
)
}
}
export default ProductList;
Have a play with that and see how you get on.

React Component Props are receiving late. (Meteor JS)

I am working on a react-native and meteor js project.
My problem is that the props received from withTracker() function are only received in componentDidUpdate(prevProps) I don't get them in constructor or componentWillMount.
Another issue is when i pass props directly from parent to child. it receives them late due to which my component does not update
iconGroups prop comes from withTracker() method
and openSection props which i am using in this showGroupIcons()
is passed directly from parent to this component.
I want to open Accordian section that is passed to it via parent. but problem is in componentDidUpdate(prevProps) I am changing state due to which component re-renders.
openSection variable by default value is Zero. when props arrvies it value changes which i required But Accordian does not update.
Below is my code
import React, { Component } from 'react';
import Meteor, { withTracker } from 'react-native-meteor';
import {
View, Image, ScrollView, TouchableOpacity,
} from 'react-native';
import PopupDialog from 'react-native-popup-dialog';
import {Text, Icon, Input, Item, List,} from 'native-base';
import Accordion from 'react-native-collapsible/Accordion';
import { Col, Row, Grid } from 'react-native-easy-grid';
import styles from './styles';
import CONFIG from '../../config/constant';
import {MO} from "../../index";
const staticUrl = '../../assets/img/icons/';
class IconPickerComponent extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: [],
itemName: 'apple1',
activeSections: 0,
showAccordian: true,
accordianData: []
};
}
componentDidUpdate(prevProps) {
if(prevProps.iconGroups !== this.props.iconGroups) {
let images = this.props.iconGroups.map(icon => icon.images);
let flatten = [].concat.apply([], images).map(img => { return {name: img, icon: CONFIG.ICON_URL+img+'.png'} })
this.setState({ filteredItems: flatten, dataSource: flatten, accordianData: this.props.iconGroups });
}
}
componentDidMount() {
this.props.onRef(this);
}
componentWillUnmount() {
this.props.onRef(null);
}
method() {
// this.setState(...this.state,{
// searchText: ''
// })
this.iconPicker.show(); // show icon picker
}
onSearchChange(text) {
this.setState({
showAccordian: !(text.length > 0)
});
const searchText = text.toLowerCase();
const filteredItems = this.state.dataSource.filter((item) => {
const itemText = item.name.toLowerCase();
return itemText.indexOf(searchText) !== -1;
});
this.setState({ filteredItems });
}
onIconSelect(item) {
this.setState({
itemName: item,
});
this.iconPicker.dismiss();
if (this.props.onIconChanged) {
this.props.onIconChanged(item);
}
}
_renderSectionTitle = section => {
return (
<View style={styles.content}>
<Text></Text>
</View>
);
};
_renderHeader = section => {
return (
<View style={styles.accordHeader}>
<Text style={{color: 'white'}}>{this.state.showAccordian} - {section.group}</Text>
<Text>
<Icon style={styles.downArrow} name="ios-arrow-down" />
</Text>
</View>
);
};
_renderContent = section => {
return (
<View style={styles.accordContent}>
{
section.images.map((img, key) => (
<TouchableOpacity onPress={() => this.onIconSelect(img)} key={key}>
<View style={styles.iconsGrid}>
<Image style={styles.image} source={{uri: CONFIG.ICON_URL+ img + '.png'}}/>
</View>
</TouchableOpacity>
))
}
</View>
);
};
_updateSections = activeSections => {
this.setState({ activeSections });
};
hasGroupIcons() {
return this.props.iconGroups.length > 0;
};
showGroupIcons() {
if(this.state.showAccordian){
let openSection;
if(!!this.props.openSection) {
let groupIndex = this.state.accordianData.findIndex(icon => icon.group === this.props.openSection);
if(groupIndex !== -1) {
openSection = groupIndex;
} else {
openSection = 0;
}
} else {
openSection = 0;
}
return(<Accordion
sections={this.state.accordianData}
activeSections={this.state.activeSections}
renderSectionTitle={this._renderSectionTitle}
renderHeader={this._renderHeader}
renderContent={this._renderContent}
onChange={this._updateSections}
initiallyActiveSection={openSection} />);
} else {
return(<View style={{flexWrap: 'wrap', flexDirection: 'row'}}>
{
this.state.filteredItems.map((item, key) => (
<TouchableOpacity onPress={() => this.onIconSelect(item.name)} key={key}>
<View style={styles.iconsGrid}>
<Image style={styles.image} source={{uri: item.icon}}/>
</View>
</TouchableOpacity>
))
}
</View>)
}
};
render() {
return (
<PopupDialog
overlayOpacity={0.8}
overlayBackgroundColor="#414141"
dialogStyle={styles.dialogBox}
containerStyle={styles.dialogContainer}
ref={(popupDialog) => { this.iconPicker = popupDialog; }}
>
<ScrollView>
<View style={styles.dialogInner}>
<Item searchBar rounded style={styles.searchbar}>
<Icon style={styles.searchIcon} name="search" />
<Input onChangeText={this.onSearchChange.bind(this)} style={styles.inputSearch} placeholder="Search" />
</Item>
{
this.hasGroupIcons() && this.showGroupIcons()
}
</View>
</ScrollView>
</PopupDialog>
);
}
}
export default withTracker(params => {
MO.subscribe('ipSubsId3', 'IconGroups');
return {
iconGroups: MO.collection('IconGroups', 'ipSubsId3').find({}),
};
})(IconPickerComponent);
I am new to react. I am assuming when props change component re-renders.
Use this life cycle method
static getDerivedStateFromProps(prevProps, prevState) {
if(prevProps.iconGroups !== this.props.iconGroups) {
let images = this.props.iconGroups.map(icon => icon.images);
let flatten = [].concat.apply([], images).map(img => { return {name: img, icon: CONFIG.ICON_URL+img+'.png'} })
this.setState({ filteredItems: flatten, dataSource: flatten, accordianData: this.props.iconGroups });
}
}
getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.
Read more about this lifecycle method here
I have fixed this issue. Actually my concepts were not right. I thought props are first received in constructor and componentWillMount. But I get all props in render() and everything works fine i dont have to use any lifecycle method to use props now

React-Redux FlatList Updation

I have an AuditionsList component which displays a FlatList. In a different reducer I'm changing Settings called location and roleType. Depending on these new settings I want to refresh the AuditionList.
Here is my code:
import React from 'react';
import { Text, View, FlatList, ActivityIndicator } from 'react-native';
import { connect } from 'react-redux';
import AuditionItem from './AuditionItem';
import Auditions from './../data/Auditions';
class AuditionsList extends React.Component {
constructor(props) {
super(props);
this.state = { isLoading: true, data: [], refresh: false }
}
componentDidMount() {
this._refreshData();
}
_onRefresh() {
this.setState({ isLoading: true }, this._refreshData() );
}
_refreshData = () => {
Auditions.fetchAuditions(this.props.productionType, this.props.location, this.props.roleType).then(auditions => {
this.setState({ isLoading: false, data: this._addKeysToAuditions(auditions) });
});
}
_addKeysToAuditions = auditions => {
return auditions.map(audition => {
return Object.assign(audition, { key: audition.Role});
});
}
_renderItem = ({ item }) => {
return (
<AuditionItem
auditionId={item.objectId}
role={item.Role}
project={item.Project.Name}
productionType={item.Project.ProductionType.Type}
auditionDate={JSON.stringify(item.Date.iso)}
productionHouse={item.Project.ProductionHouse.Name}
/>
);
}
render() {
console.log("Here...");
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList onRefresh={() => this._onRefresh()} refreshing={this.state.isLoading} data={this.state.data} renderItem={this._renderItem} />
</View>
);
}
}
const mapStateToProps = state => {
return {
location: state.settings.location,
roleType: state.settings.roleType,
};
}
export default connect(mapStateToProps)(AuditionsList);
I need to call the _onRefresh() or _refreshData() function AFTER the callback to mapStateToProps (which runs successfully) but BEFORE the FlatList re-renders (which also happens successfully, but with old data). So where do I call the _onRefresh() or _refreshData() functions? Putting them in render() causes an infinite loop.
Have you tried using the ComponentDidUpdate react lifecycle method? This will fire after you receive new props, and you could make a call to this._refreshData there.
https://reactjs.org/docs/react-component.html#componentdidupdate

Resources