Infinite FlatList problem - React Native , Expo - react-native

I am using Expo for developing react-native applications.
I want to make an Infinite list, but every time onEndReached event is fired, FlatList is refreshed automatically scrolls to the top of the page!
import React, { useState, useEffect } from "react";
import { SafeAreaView, Text, FlatList } from "react-native";
export default function App() {
const [config, setConfig] = useState({
result: [],
page: 0
});
async function fetchData() {
const response = await fetch("http://192.168.2.49:3500/q", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({ page: config.page })
});
const data = await response.json();
setConfig({
result: [...config.result, ...data],
page: config.page++
});
}
const onEndReached = async () => {
await setConfig({
page: config.page++
});
fetchData();
};
useEffect(() => {
fetchData();
}, []);
return (
<SafeAreaView>
<Text>Current Page : {config.page}</Text>
<FlatList
data={config.result}
renderItem={o => <Text>X :{o.item.t.c}</Text>}
keyExtractor={item => item._id}
onEndReached={() => onEndReached()}
onEndReachedThreshold={0}
></FlatList>
</SafeAreaView>
);
}

You are calling setConfig twice, before calling fetchData and after a successful API request. Which triggers a rerender.
I refactored your code, try this.
import React, { useState, useEffect, useCallback } from 'react';
import {
SafeAreaView,
Text,
FlatList,
NativeSyntheticEvent,
NativeScrollEvent,
} from 'react-native';
export default function App() {
const [config, setConfig] = useState({
result: [],
page: 0,
});
const [isScrolled, setIsScrolled] = useState(false);
const fetchData = useCallback(() => {
async function runFetch() {
const response = await fetch('http://192.168.2.49:3500/q', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ page: config.page }),
});
const data = await response.json();
setConfig({
result: [...config.result, ...data],
page: config.page++,
});
}
runFetch();
}, [config.page, config.result]);
const onEndReached = useCallback(() => fetchData(), [fetchData]);
const onScroll = useCallback(
e => setIsScrolled(e.nativeEvent.contentOffset.y > 0),
[],
);
useEffect(() => {
fetchData();
}, [fetchData]);
return (
<SafeAreaView>
<Text>Current Page : {config.page}</Text>
<FlatList
data={config.result}
keyExtractor={item => item._id}
onEndReached={onEndReached}
onEndReachedThreshold={0.1}
onScroll={onScroll}
renderItem={o => <Text>X :{o.item.t.c}</Text>}
/>
</SafeAreaView>
);
}

Related

How to Subscribe to Platform Event Notifications with React Native

I am developing a mobile application with React Native. With this application, I connect to salesforce and make transactions.
I created Platform Events on Salesforce side and I want React Native Component to subscribe to it.
I can't use lightning/empApi or CometD because I don't code web. According to the document, my only option is Pub/Sub API.
I tried to pull it directly with FETCH but with no result.
import {View, Text} from 'react-native';
import React, { useEffect, useState } from 'react';
import { Force, oauth } from 'react-native-force';
function History() {
const [session, setSession] = useState()
const eventName = "Test_PE__e";
const replayId = -1;
const [subscription, setSubscription] = React.useState();
useEffect(() => {
oauth.getAuthCredentials(
(data) => console.log(data), // already logged in
() => {
oauth.authenticate(
() => setSession(data),
(error) => console.log('Failed to authenticate:' + error)
);
});
},[]);
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text> Subscribed to {eventName} </Text>
</View>
);
async function setPEvents(session) {
const headers = new Headers({
'Authorization': `Bearer ${session.accessToken}`,
'Content-Type': 'application/json'
});
const body = {
"event": `/event/${eventName}`,
"replayId": replayId
};
const url = `${session.instanceUrl}/services/data/v57.0/event/${eventName}/subscriptions`;
const requestOptions = {
method: 'POST',
headers: headers,
body: JSON.stringify(body)
};
const resp = await fetch(url, requestOptions)
.then(async (response) => {
console.log(response);
return await response.json();
})
.catch(error => console.log('Error:', error));
setSubscription(resp);
console.log("FIRATTT");
console.log("Result:",resp);
}
}
export default History;
What can I do to subscribe?
Or how can I use the SalesForce Pub/Sub API in React Native?

How to solve React Native expo authSession google login issue?

I wanted to implement login with google feature on my React native app. I'm using expo-cli and I used expo authSession for this.
LoginScreen.js
import * as React from "react";
import * as WebBrowser from "expo-web-browser";
import * as Google from "expo-auth-session/providers/google";
import { Button, View, Text, TouchableOpacity } from "react-native";
WebBrowser.maybeCompleteAuthSession();
export default function LoginScreen() {
const [googleAccessToken, setGoogleAccessToken] = React.useState(null);
const [userInfo, setUserInfo] = React.useState(false);
const [request, response, promptAsync] = Google.useAuthRequest({
expoClientId: "###",
iosClientId: "###",
androidClientId: "###",
webClientId: "###",
});
React.useEffect(() => {
if (response?.type === "success") {
const { authentication } = response;
setGoogleAccessToken(authentication.accessToken);
fetchUserInfo();
}
}, [response]);
const fetchUserInfo = () => {
if (googleAccessToken) {
fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
headers: { Authorization: `Bearer ${googleAccessToken}` },
})
.then((response) => response.json())
.then((userInfoObj) => {
setUserInfo(userInfoObj);
console.log(userInfoObj);
})
.catch((err) => {
console.log(err);
});
}
};
return (
<View>
<Button
disabled={!request}
title="Login Here"
onPress={() => {
promptAsync();
}}
/>
{userInfo ? <Text>{userInfo.email}</Text> : <Text>Nope</Text>}
</View>
);
}
But once, I click LOGIN HERE button and login with google account it doesn't set userInfo state in the first time. I have to click LOGIN HERE button again to login. Even though I have authenticated with google, it doesn't set userInfo state in the first time. But once I click LOGIN HERE again and after continue the process, then it works. How can I solve this issue?
Also I'm getting this warning as well.
Warning
EventEmitter.removeListener('url', ...): Method has been deprecated. Please instead use `remove()` on the subscription returned by `EventEmitter.addListener`.
at node_modules/react-native/Libraries/vendor/emitter/_EventEmitter.js:164:4 in EventEmitter#removeListener
at node_modules/react-native/Libraries/EventEmitter/NativeEventEmitter.js:108:4 in removeListener
at node_modules/react-native/Libraries/Linking/Linking.js:57:4 in removeEventListener
at node_modules/expo-web-browser/build/WebBrowser.js:354:4 in _stopWaitingForRedirect
at node_modules/expo-web-browser/build/WebBrowser.js:347:31 in _openAuthSessionPolyfillAsync
I found the answer for this question by myself. You need to put fetchUserInfo() inside useEffect.
import * as React from "react";
import * as WebBrowser from "expo-web-browser";
import * as Google from "expo-auth-session/providers/google";
import { Button, View, Text, TouchableOpacity } from "react-native";
WebBrowser.maybeCompleteAuthSession();
export default function LoginScreen() {
const [googleAccessToken, setGoogleAccessToken] = React.useState(null);
const [userInfo, setUserInfo] = React.useState(false);
const [request, response, promptAsync] = Google.useAuthRequest({
expoClientId: "###",
iosClientId: "###",
androidClientId: "###",
webClientId: "###",
});
React.useEffect(() => {
const fetchUserInfo = () => {
if (googleAccessToken) {
fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
headers: { Authorization: `Bearer ${googleAccessToken}` },
})
.then(response => response.json())
.then(userInfoObj => {
setUserInfo(userInfoObj);
console.log(userInfoObj);
})
.catch(err => {
console.log(err);
});
}
};
if (response?.type === "success") {
const { authentication } = response;
setGoogleAccessToken(authentication.accessToken);
fetchUserInfo();
}
}, [response]);
return (
<View>
<Button
disabled={!request}
title="Login Here"
onPress={() => {
promptAsync();
}}
/>
{userInfo ? <Text>{userInfo.email}</Text> : <Text>Nope</Text>}
</View>
);
}

connecting stripe to react native app error

I'm trying test a payment using stripe for my react native project. But when I press the pay button i get an error stating. since there is not an official doc for stripe for react native, its been challenging
here the error
TypeError: null is not an object (evaluating 'StripeBridge.createPayment')
onPaymentPress
PaymentScreen.js:17:33
what that error mean exactly and how do I remove it?
essentially my goal is for the user to make a one time payment once they enter their details and press the pay button
here is my code
import React, { useState } from 'react'
import { Image, Text, TextInput, TouchableOpacity, View,ImageBackground,NativeModules} from 'react-native'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import styles from './styles';
var StripeBridge = NativeModules.StripeBridge;
export default function PaymentScreen({navigation}) {
const [ccNumber, setCCNumber] = useState('')
const [month, setMonth] = useState('')
const [year, setYear] = useState('')
const [zipCode, setZipCode] = useState('')
const [cvv, setCvv] = useState('')
const onPaymentPress = () => {
fetch('http://localhost:3001/createStripePaymentIntent', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.then(responseJson => {
console.log(responseJson.setupIntentId);
StripeBridge.createPayment(
responseJson.setupIntentId,
ccNumber,
month,
year,
cvv,
zipCode,
(error, res, payment_method) => {
if (res == 'SUCCESS') {
Alert.alert('Stripe Payment', 'Your Stripe payment succeeded', [
{text: 'OK', onPress: () => console.log('OK Pressed')},
]);
}
},
);
})
.catch(error => {
console.error(error);
});
// setTimeout(navigation.navigate("Products"), 1000)
}
return (
</View>
......
style={styles.button}
onPress={() => onPaymentPress()}>
<Text style={styles.buttonTitle}>Submit Payment</Text>
</TouchableOpacity>
<View style={styles.footerView}>
<Text style={styles.footerText}> Payment secured by Stripe <Text style={styles.footerLink}></Text></Text>
</View>
</KeyboardAwareScrollView>
</View>
</ImageBackground>
)
}

Invariant Violation in React Native: Text strings must be rendered within a <Text> component

I'm working on a React-Native project with REST APis, and I've currently got an invariant violation error. I've experienced this before, but I can't quite figure out what is causing it and how to fix it. If someone could point me in the right direction, I would really appreciate it! The full error is pictured below, and appears to be referencing a number of tags in the code, so I'm unsure exactly where it is originating. Thank you for reading, and thanks in advance!
The code is here:
import React, { Component } from 'react'
import { View, Text, Image, StyleSheet, FlatList} from 'react-native';
import * as Font from 'expo-font';
import styled from 'styled-components';
import dimensions from '../components/ScreenSize';
import colours from '../components/Colours';
import { Audio } from 'expo-av';
import { TouchableHighlight } from 'react-native-gesture-handler';
const client_id = {Client_ID}
const client_secret = {Client_Secret}
const item = ({item}) => (
<View style={{ flex:1, flexDirection: 'column', margin:1}}>
<TouchableHighlight onPress={() => this.fetchTracks(item.id)}>
<View>
<Text>{item.name}</Text>/>
</View>
</TouchableHighlight>
</View>
)
export default class HomeScreen extends React.Component {
state={
fontsLoaded:false,
}
async componentDidMount() {
await Font.loadAsync({
'montserrat-regular': require('../assets/fonts/Montserrat/Montserrat-Regular.ttf'),
'montserrat-light': require('../assets/fonts/Montserrat/Montserrat-Light.ttf'),
'montserrat-semibold': require('../assets/fonts/Montserrat/Montserrat-SemiBold.ttf'),
'montserrat-bold': require('../assets/fonts/Montserrat/Montserrat-Bold.ttf'),
}
).then(() => this.setState({ fontsLoaded:true }))
this.getToken();
this.setAudio();
}
constructor (props) {
super(props)
this.playbackInstance=null;
this.state = {
playing:false,
token: '',
DATA:[],
};
}
setAudio=() => {
Audio.setAudioModeAsync({
allowsRecordingIOS:false,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
playsInSilentModeIOS: true,
shouldDuckAndroid: true,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX,
playThroughEarpieceAndroid: false,
});
}
componentDidCatch(error, info)
{
console.log(error, info.componentStack);
}
getToken = async() =>
{
try
{
const getspotifytoken = await fetch("https://accounts.spotify.com/api/token",
{
method:'POST',
body: `grant_type=client_credentials&client_id=${client_id}&client_secret=${client_secret}`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
const spotifytoken = await getspotifytoken.json();
this.setState({
token: spotifytoken.access_token
});
console.log(this.state.token);
}
catch(err)
{
console.log("Error fetching data", err);
}
}
search = async () => {
try
{
console.log("Searching: mood")
const spotifyApiCall = await fetch(`https://api.spotify.com/v1/browse/categories/mood/playlists?`, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${this.state.token}`,
"Content-Type":'application/json'
}
})
const spotify = await spotifyApiCall.json();
console.log("Items", spotify);
this.setState({
DATA: spotify.playlists.items,
})
}
catch (err)
{
console.log("Error fetching data", err);
}
}
fetchTracks = async (playlistId) => {
console.log('Playlist ', playlistId)
try
{
const getplaylist = await fetch(`https://api.spotify.com/v1.playlist/${playlistId}`,
{
method:'GET',
headers: {
Accept:"application/json",
Authorization:`Bearer ${this.state.token}`,
"Content-Type":"application/json"
}
});
const playlist = await getplaylist.json();
console.log('music ', playlist.tracks.items[0].preview_url);
}
catch (err)
{
console.log("Error fetching data ", err);
}
}
async _loadNewPlaybackInstance(playing, track) {
if(this.playbackInstance != null)
{
await this.playbackInstance.unloadAsync();
this.playbackInstance.setOnPlaybackStatusUpdate(null);
this.playbackInstance = null;
}
const source = {uri: track};
const initialStatus = {
shouldPlay: true,
rate: 1.0,
shouldCorrectPitch: true,
volume: 1.0,
isMuted: false
};
const {sound, status} = await Audio.Sound.createAsync(
source.initialStatus);
this.playbackInstance=sound;
this.playbackInstance.setIsLoopingAsync(false);
this.playbackInstance.playAsync();
if (this.state.selected === playlistId) {
console.log("Playing, so stop");
this.setState({selected:null});
this.playbackInstance.pauseAsync();
return;
}
this.setState({ selected:playlistId});
this._loadNewPlaybackInstance(true, playlist.tracks.items[0].preview_url);
}
render() {
if(!this.state.fontsLoaded ) {
return null
}
return (
<Container>
<Titlebar>
<Title>Music</Title>
</Titlebar>
<HeaderBar2>
<TouchableHighlight onPress={() => this.search()}>
<Header2>Playlists for your Mood</Header2>
</TouchableHighlight>
</HeaderBar2>
<View style={styles.MainContainer}>
{
this.state.DATA.length == 0 &&
<Text style={{padding:10, color:'#D3D3D3'}}/>
}
<FlatList
data = {this.state.DATA}
renderItem={item}
keyExtractor = {item.id}
numColumns={2}
extraData = {this.state}
/>
</View>
</Container>
);
}
}
I think u just have a little typo ..
check this line: <Text>{item.name}</Text>/>
change the last Text to </Text>

React Native getting collections from Zomato API

I am trying to get collections from Zomato API (https://developers.zomato.com/documentation) and I am trying to retrieve the collections list and display them onto a flatList. However every time I try to retrieve it my terminal seems to output undefined
Here is my code
async componentDidMount(){
try {
const res = await axios.request({
method: 'get',
url: `https://developers.zomato.com/api/v2.1/collections`,
headers: {
'Content-Type': 'application/json',
'user-key': 'a31bd76da32396a27b6906bf0ca707a2'
},
params: {
'city_id': `${this.state.loca}`
}
});
this.setState({ data: res.data });
console.log(res.data.collections.title)
} catch (err) {
console.log(err);
} finally {
this.setState({ isLoading: false });
}
};
when I console.log(res.data.collections) I get the entire list of all components within the collections Array from the API. However when I try to access the title component; the terminal outputs undefined
what am I doing wrong?
Do check out the below code, i think there was a small problem with your code, you were not extracting the exact data. Ive corrected it by displaying the title of restuarent. you can do more. expo link is as expo-link
import React from 'react';
import {
View,
Text,
FlatList,
StyleSheet,
TouchableHighlight,
Dimensions,
Image,
} from 'react-native';
import Modal from 'react-native-modal';
import { createAppContainer } from 'react-navigation';
import {createStackNavigator} from 'react-navigation-stack';
import { Card, Icon, Button } from 'react-native-elements';
import Constants from 'expo-constants';
// import {apiCall} from '../src/api/Zomato';
// import Logo from '../assets/Logo.png';
import axios from 'axios';
export default class HomeScreen extends React.Component {
constructor(props){
super(props);
// this.navigate = this.props.navigation.navigate;
this.state={
data : [],
isModalVisible: false,
loca: 280
}
}
async componentDidMount(){
try {
const res = await axios.request({
method: 'get',
url: `https://developers.zomato.com/api/v2.1/collections`,
headers: {
'Content-Type': 'application/json',
'user-key': 'a31bd76da32396a27b6906bf0ca707a2'
},
params: {
'city_id': `${this.state.loca}`
}
});
// alert(res.data.collections, 'response');
this.setState({ data: res.data.collections });
} catch (err) {
console.log(err);
} finally {
}
}
render() {
return (
<View>
<FlatList
style={{marginBottom: 80}}
keyExtractor={item => item.id}
data={this.state.data}
renderItem={({ item }) =>
<TouchableHighlight onPress={()=> this.props.navigation.navigate('CategoryScreen', { category: item.categories.id, city: this.state.loca })}>
<Card>
<Text style={{color:'#000',fontWeight:'bold'}}>{item.collection.title} </Text>
</Card>
</TouchableHighlight>}
/>
</View>
);
}
}
do revert if any doubts, ill clear it. hope it helps;
Axios returns a promise try keeping the setState in .then and stop trusting console.log
axios.request({
method: 'get',
url: `https://developers.zomato.com/api/v2.1/collections`,
headers: {
'Content-Type': 'application/json',
'user-key': 'a31bd76da32396a27b6906bf0ca707a2'
},
params: {
'city_id': `${this.state.loca}`
}
}).then( res => this.setState({res}))