Unable to process Azure Functions Html response with React - Expo - node.js

Disclaimer: I am new to JS so this problem is probably pretty simple to solve, but I have consulted all resources and stack overflow posts I could find online on the subject
This is probably something simple that's missing but I haven't been able to debug this problem :
I want to connect an Azure Function API to a React app, but I am unable to process the Html Response, which makes my await functions hang.
Here is the very simple Azure functions API code :
import logging
import azure.functions as func
import json
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
return func.HttpResponse(
json.dumps({
'recommendations': [1,2,3,4,5]
}),
mimetype="application/json"
);
And here is my React based app (running with Expo) :
class App extends Component {
state = {
selectedUser: 0,
step: 0,
recommendations: 1
};
goToStep0 = async () => {
this.setState({ step: 0, recommendations: 1 });
};
goToStep1 = async () => {
const { data: rec } = await axios.get(config.API_URL);
//const rec = await axios.get(config.API_URL);
// const options = {method: "GET"};
// var rec;
// fetch(config.API_URL,options)
// .then(data => {rec = data.recommendations;});
this.setState({ step: 1, recommendations: rec });
};
The Azure function is receiving input and returns a successful answer everytime.
What I have tried :
Changing the the Azure function app to return a string
Trying to get the request with fetch
Removing async and running a more old school request :
var rec;
fetch(config.API_URL)
.then(response => response.json())
.then(data => {
rec = data
});
Probably at least 10 other things I have forgotten (I have wasted more than 4 hours on this...)
Thanks a lot in advance for your help!
Edit : Below is the code for the rendering part of the app (text is in French) :
render() {
if (!config.API_URL) {
return (
<View style={styles.container}>
<Image
style={{ resizeMode: "center", width: 450, height: 150 }}
source={require("./assets/icon-flat.png")}
/>
<Text style={{ color: "red", margin: 20 }}>
L'app n'est pas configurée correctement. Mettez à jour le fichier
config.json comme indiqué dans le README.
</Text>
</View>
);
}
return (
<View style={styles.container}>
<Image
style={{ resizeMode: "center", width: 450, height: 150 }}
source={require("./assets/icon-flat.png")}
/>
<Text style={{ fontSize: 18, padding: 20, textAlign: "center" }}>
Choisissez votre profil afin de recevoir des recommendations de
lecture personnalisées : {this.state.recommendations.toString()}
Quel est mon état : {this.state.step}
</Text>
<Picker
style={{ height: 200, width: "80%", margin: 30 }}
selectedValue={this.state.selectedUser}
onValueChange={value => this.setState({ selectedUser: value })}
>
{[...Array(10000).keys()].map(e => (
<Picker.Item key={e} label={`User ${e}`} value={e} />
))}
</Picker>
<Button title="Se connecter" onPress={this.goToStep1} />
</View>
);
}
}

I have managed to find the problem. The code was not problematic as much as the fact that I was using an old version of react and axios.
With the newest version, I am unable to send requests to localhost but I managed to get responses after deploying the application.
I have "deployed" my Azure function API to the internet using Ngrok which allows me to handle requests.

You can call the callback function on Axios call like this:
axios.get(config.API_URL)
.then(res => {
if (res) {
console.log("Data fetched: ", res)
// do stuff
}
else {
// do stuff
}
}).catch(err => {
console.log(err)
})
Like you did with fetch

Related

React Typescript project .map function working only one time after compilation

I want to create a frontend for my app using React and TypeScript.
I am gathering data from my backend (simple DRF app) and fetching it using Axios and pushing it into separate arrays.
const myApi = axios.create({
baseURL: 'http://127.0.0.1:8000/app/',
headers: {
"Content-type": "application/json"
}
})
let idArr:number [] = new Array()
let titleArr:string [] = new Array()
let contentArr:string [] = new Array()
myApi.get('/getallarticles').then( res => {
for(let elem in res.data)
{
idArr.push(res.data[elem].id)
titleArr.push(res.data[elem].title)
contentArr.push(res.data[elem].content)
}
})
The app contains a page named feed into which I parse the id Array.
function App() {
console.log(idArr)
return (
<Box>
<Navbar/>
<Grid alignItems={"center"} justifyContent={"center"} marginLeft={'15%'} marginRight={'15%'}>
<Stack spacing={2} margin={2}>
<Box id='res_viewer'>
<Feed content={"CONTENT FROM MAIN"} id={10} title={"Sherlock Holmes"} total={5} idArr={idArr}/>
</Box>
</Stack>
</Grid>
</Box>
);
}
Then in my page Feed I copy the ActionCard (from Mui5) and the problem is that all the Cards show only if I recompile the project. It only works one time, and I have no idea why. Moreover if I put the console.log and alert into the Feed the console.log would show an array every time, while Alert only the first time. Anyone have any idea what the issue might be?
const Feed = ({ id, title, content, total, idArr }: { id: number; title: string; content: string; total: number; idArr:Array<number>}) => {
console.log(idArr)
alert(idArr)
return(
<div>
{idArr.map(customId =>
<Card key={customId} variant="outlined" sx={{ margin: 0 }}>
<CardActionArea>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
{customId}
</Typography>
<Typography variant="body2" color="text.secondary">
{content}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button style={{
color: "#ff0000",
}} size="large" startIcon={<DeleteForever />}>
DELETE
</Button>
<Button style={{
color: "#68ee06",
}} size="large" startIcon={<Edit />}>
EDIT
</Button>
</CardActions>
</Card>
)}
</div>
)
}
export default Feed
I suspect the asynchronous nature of the call to your API is what's giving this behavior. When making calls to an API for data, it's common to use a useEffect hook that updates a state. This ensures that the UI is in sync with the data. Try rewriting as follows:
const myApi = axios.create({
baseURL: 'http://127.0.0.1:8000/app/',
headers: {
"Content-type": "application/json"
}
})
const [idArr, setIdArr] = useState([] as number[]);
const [titleArr, setTitleArr] = useState([] as string[]);
const [contentArr, setContentArr] = useState([] as string[]);
useEffect(() => {
myApi.get('/getallarticles').then(res => {
const newIds: number[] = [];
const newTitles: string[] = [];
const newContents: string[] = [];
for (let elem in res.data) {
newIds.push(res.data[elem].id);
newTitles.push(res.data[elem].title);
newContents.push(res.data[elem].content);
}
setIdArr(newIds);
setTitleArr(newTitles);
setContentArr(newContents);
});
}, []);

AsyncStorage not functioning, errors and database input buttons freezing

So, I am currently working on a program that is supposed to be a mock-up of a Student Educational Plan for a school. The basic gist of it is that it displays a home page with buttons to a form to submit information (text input has buttons to submit or clear the information entered into the to an AsyncStorage database and a page that displays reports of information collected from the form. All pages display somewhat normally, but for some reason, when I press the submit button, the page freezes and does not submit the info. Here is the code I have as of right now. I am stuck trying to figure out how to use AsyncStorage in the context, which is why I don't have it added here. Another thing to add is that the names in the StyleSheet are the way they are is because I had originally based the code from a previous program (mock-up Music Player) I had worked on. Please explain everything as simply as possible.
Edit: I have tried to do more with the AsyncStorage, but the buttons don't work properly. Below is the edited version of the previous code.
import { Image, ScrollView, StyleSheet, Text, View, Button, TextInput,} from 'react-native';
//import {AsyncStorage} from '#react-native-async-storage/async-storage';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
const Stack = createNativeStackNavigator();
const MyStack = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Welcome' }}
/>
<Stack.Screen name="SEP Form" component={SEPScreen} />
<Stack.Screen name="SEP Database" component={SEPInfo} />
</Stack.Navigator>
</NavigationContainer>
);
};
const HomeScreen = ({ navigation }) => {
return (
<View style={styles.container}>
<Button
title="Your Student Educational Plan"
onPress={() =>
navigation.navigate('SEP Form')
}
/>
<Button
title="All Student Educational Plans"
onPress={() =>
navigation.navigate('SEP Database')
}
/>
</View>
);
};
const SEPScreen = ({ navigation, route }) => {
const[SEPInfo, setForm] = useState({});
const save = async() => {
try{
await AsyncStorage.setItem(SEPInfo)
if (SEPInfo.name == null || SEPInfo.name == '') {
alert("Please Enter your Name");
}
else if (SEPInfo.id == null || SEPInfo.id == '') {
alert("Please Enter your ID");
}
else if (SEPInfo.major == null || SEPInfo.major == '') {
alert("Please Enter your Major");
}
else if (SEPInfo.goal == null || SEPInfo.goal == '') {
alert("Please Enter your Goal");
}
else {
await AsyncStorage.setItem("form", JSON.stringify(SEPInfo));
alert(`Form Saved! Name: ${SEPInfo.name}\nID: ${SEPInfo.id}\nMajor: ${SEPInfo.major}\nGoal: ${SEPInfo.goal}`)
}
}
catch (error) {
alert(error);
};
/* const clear = async() => {
try{
await AsyncStorage.removeItem(SEPInfo);
setForm({});
alert('Form Cleared');
}
catch (error){
alert(error);
}
}; */
const load = async () => {
try {
let form = JSON.parse (await AsyncStorage.getItem(form));
if (form !== null) {
setForm(SEPInfo);
}
}
catch (error) {
alert(error);
}
};
}
function clear () {
setState({name:'', id:'', major:'', goal:''});
alert('Form has been cleared!');
}
return (
<View style={styles.container}>
<Text style={styles.sectionTitle}>Enter your info here:</Text>
<TextInput style={styles.input} name="name" label="StudentName " placeholder="Name" onChangeText={(text) => setForm({...SEPInfo, name: text})} />
<TextInput style={styles.input} name="id" label="StudentID " placeholder="ID" onChangeNumber={(number) => setForm({...SEPInfo, id: number})} />
<TextInput style={styles.input} name="major" label="Major " placeholder="Major" onChangeText={(text) => setForm({...SEPInfo, major: text})} />
<TextInput style={styles.input} name="goal" label="Goal " placeholder="Goal" onChangeText={(text) => setForm({...SEPInfo, goal: text})} />
<Button title="Submit"
onPress={() => save()
}
></Button>
<Button title="Clear"
onPress={clear}
></Button>
</View>
);
};
const SEPInfo = ({navigation, route}) => {
return(
<View style={styles.container}>
<Text style={styles.sectionTitle}>SEP Database</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#CFEAF1'
},
listWrapper: {paddingTop: 80, paddingHorizontal: 20},
sectionTitle: {fontWeight: 'bold', fontSize: 30, margin: 5, color: '#2E2090', alignSelf: 'center'},
sectionTitleTwo: {fontWeight: 'bold', fontSize: 25, margin: 5, color: '#206290', alignSelf: 'center'},
flexContainerWaiting: {backgroundColor: '#20BD57', padding: 15, borderRadius: 10, flexDirection: 'row', alignItems: 'center', marginTop: 5, marginBottom: 5, justifyContent: 'space-evenly'},
flexContainerOne: {backgroundColor: '#0B97B0', fontSize: 16, flex: 0.5, padding: 15, borderRadius: 10, flexDirection: 'row', alignItems: 'center', marginTop: 2.5, marginBottom: 2.5, justifyContent: 'space-evenly'},
flexContainerTwo: {backgroundColor: '#D773EB', fontSize: 16, flex: 0.5, padding: 15, borderRadius: 10, flexDirection: 'row', alignItems: 'center', marginTop: 2.5, marginBottom: 2.5, justifyContent: 'space-evenly'},
input: {
height: 40,
margin: 12,
borderWidth: 1,
padding: 10,
},
});
export default MyStack;
Looking at the AsyncStorage docs (https://reactnative.dev/docs/asyncstorage#setitem), it says that when using setItem(), you need to pass a key: string and a value: string. In your code, you only seem to pass a value: AsyncStorage.setItem(TextInput). Furthermore, you pass the value TextInput into setItem(), but you don't seem to define TextInput anywhere. What you should do is use the value prop and onChangeText prop to store the value of the TextInput in a state variable, as is shown on the docs: https://reactnative.dev/docs/textinput. Then, pass that state variable into setItem() as they value and pass another item as its key.
for saving an item in async storage:-
AsyncStorage.setItem('usertoken', token);
for fetching an item from async storage:-
// need to be called inside an async function
let token = await AsyncStorage.getItem('userToken');

delete from firebase real time database

it makes a flat list of rooms and inside those rooms are messages. So I want to long press on a specific room in the list, and when i do this, I want to delete a room.
onLongPress={() => this.roomsRef.remove({item})}
I thought the above would work, but it fails. I put it in the render row part. It comes up saying Reference.remove failed. first argument must be a valid function. Like inside these rooms it has messages and I think thats where im going wrong but im not sure how to go about it. thank you
'use strict'
import React, { Component, useEffect } from 'react';
import { Text, Image, TextInput, TouchableHighlight, StatusBar, FlatList, View, TouchableOpacity, Dimensions, LogBox, Alert } from 'react-native';
// import firebaseApp from './firebaseConfig.js';
import firebaseApp from '../config/firebasekeys';
import {SafeAreaView} from 'react-native-safe-area-context'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'
import {Avatar} from 'react-native-elements'
import { color } from 'react-native-reanimated';
const DeviceHeight = Dimensions.get('screen').height
const DeviceWidth = Dimensions.get('screen').width
class Rooms extends Component {
static navigationOptions = {
title: 'Rooms',
};
constructor(props) {
super(props);
var firebaseDB = firebaseApp.database();
this.roomsRef = firebaseDB.ref('rooms'); // rooms collection
this.state = {
rooms: [],
newRoom: ''
}
}
// maybe its this.roomsRef.remove(item.name) or maybe its name idk hmmm
componentDidMount() {
LogBox.ignoreLogs(['VirtualizedLists should never be nested']); // doing this to ignore the log box error for scroll view
this.listenForRooms(this.roomsRef);
}
listenForRooms(roomsRef) {
roomsRef.on('value', (dataSnapshot) => {
var roomsFB = [];
dataSnapshot.forEach((child) => {
roomsFB.push({
name: child.val().name,
key: child.key
});
});
this.setState({ rooms: roomsFB });
});
}
addRoom() {
if (this.state.newRoom === '') {
return
}
this.roomsRef.push({ name: this.state.newRoom });
this.setState({ newRoom: '' });
}
openMessages(room) {
this.props.navigation.navigate("Messages", {roomKey: room.key, roomName: room.name});
}
renderRow(item) {
return (
<TouchableOpacity
onPress={() => this.openMessages(item)}
onLongPress={() => this.roomsRef.remove({name:item})}
>
<Text style={{
color: 'orange',
fontSize: 22,
}}>{item.name}</Text>
</TouchableOpacity>
)
}
render() {
return (
<SafeAreaView style={styles.container}>
<StatusBar style="auto"/>
<KeyboardAwareScrollView>
<View>
<View >
<TextInput>
}}
placeholder={"enter chat name"}
onChangeText={(text) => this.setState({newRoom: text})}
value={this.state.newRoom}
/>
<TouchableHighlight
style={{
marginRight: 20
}}
onPress={() => this.addRoom()}
>
>
</TouchableHighlight>
</View>
<View>
<FlatList
data={this.state.rooms}
renderItem={({item}) => (this.renderRow(item)
)}
/>
</View>
{/* <View>
<FlatList
data={this.state.rooms}
renderItem={({item}) => (this.renderRow(item)
)}
/>
</View> */}
</View>
</KeyboardAwareScrollView>
</SafeAreaView>
);
}
}
export default Rooms;
I recommend keeping the reference docs for the API handy as you're developing, as there are quite a few problems you can catch that way. For example, the Reference.remove() method doesn't take an item as its argument (in fact: it typically is called without arguments).
To delete a specific node, you should build a reference to that node, and then call remove() on the reference:
roomsRef.child("keyOfRoomToRemove").remove()

onEndReached FlatList React Native

i currently have a FlatList in my React Native project that renders the posts from nodejs backend. I have applied the pagination to my backend and it is working when i am testing it on postman. My problem is when i test my app on a simulator when i reach the end of the page 0, page 1 does not load.
Here is my FlatList:
<FlatList
data={posts} // to have all the data
keyExtractor={(post) => post.id.toString()}
renderItem={({ item }) => (
<PostCard
title={item.title}
subTitle={item.subTitle}
/>
)}
onEndReached={onScrollHandler}
onEndThreshold={0}
/>
const { data: posts, error, loading, request: loadPosts } = useApi(
postsApi.getPosts
);
useEffect(() => {
loadPosts();
}, []);
const[page,setPage]=useState(0);
const[profiles,setProfiles]=useState([]);
const fetchRecords=(page)=>{
const newRecords = []
for(var i = page * 10, il = i + 10; i < il && i < posts.length; i++){
newRecords.push(posts[i]);
}
setProfiles(...profiles, ...newRecords)
}
const onScrollHandler =()=>{
setPage(page+1);
fetchRecords(page)
}
Here is my nodeJS backend:
router.get("/",
async (req, res) => {
const getPagination = (page, size) => {
const limit = size ? +size : 10;
const offset = page ? page * limit : 0;
return { limit, offset };
};
const { page, size } = req.query;
const { limit, offset } = getPagination(page, size);
const posts = await Post.findAll({
limit,offset,
Here my route is /posts which i declared in my index.js in my backend.
Before applying pagination in my backend, when i used to do console.log(posts) in frontend i used to get all my posts but after i applied pagination when i do console.log(posts) i only get the posts for the first page.
Can you try to put this in useEffect.
useEffect(()=>{ apiCall()},[page])
If I am not wrong, you want to concatenate newly fetched records with the records that you already have, if that is the case, then you are supposed to do it like below:
setProfiles([...profiles, ...newRecords])
example:
let str1 = [1,2,3,4]
let str2 = [5,6,7,7]
// you dont want that
console.log(...str1, ...str2)
// but this, it will create a new array,
// having elements of both str1 and str2 which is what should be
console.log([...str1, ...str2])
Example:
import React, { useState } from 'react';
import { Text, View, StyleSheet, FlatList } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import AssetExample from './components/AssetExample';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default function App() {
const [posts, setPosts] = useState(
Array.from({ length: 10 }, () => Math.floor(Math.random() * 100))
);
const onEndReached = () => {
let newPosts = Array.from(
{ length: 4 },
() => 'New Item Added ' + Math.floor(Math.random() * 100)
);
console.log(newPosts)
setTimeout(() => {
setPosts([...posts, ...newPosts]);
}, 1000);
};
return (
<View style={styles.container}>
<FlatList
data={posts}
onEndReached={onEndReached}
keyExtractor={(posts) => Math.floor(Math.random() * 1000)}
renderItem={({ item }) => (
<Card style={styles.card}>
<Text style={styles.paragraph}>{item}</Text>
</Card>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
card: {
margin: 10,
padding: 10,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
Expo Demo
Screenshow:
Did you inspect if your onScrollHandler function is called whenever you reached at the end of the flat list.
Also please wrap your flat list inside SafeAreaView and give SafeAreaView style of flex : 1
<SafeAreaView>
<FlatList/>
</SafeAreaView>

How can I modify this code so that render waits for Promise Items?

I am new to React and I load all the data from my database initially on page load but there is info I need to find in an array and apparently it isn't instant. What do I need to do to make sure the render method only renders the objects when the object promises have resolved?
I haven't tried much... I'm really stuck here.
This seems different than the other problems I've read here because I load a bunch on info in the beginning just fine but I need to call some team information every time a function is called so it isn't as simple as loading it once because the object i need is always different.
This code is the main issue. I also included the full file below:
I did some modification to the code in a edit: I realized that I just need to call the opponent team because I have the player team already.
if (team.id === game.team_1) {
var redTeam = team;
// set blueTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_2}')
} else {
var blueTeam = team;
// set redTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_1}')
}
Full file:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import Async from 'react-promise'
import { withFirebase } from '../Firebase';
// import * as ROUTES from '../../constants/routes';
import { Container, Image, Spinner, Col, Row, Card, Accordion, Button } from 'react-bootstrap'
class PlayerGameList extends Component {
constructor(props) {
super(props);
this.state = {
loadingTeams: false,
loadingSchedule: false,
teams: [],
schedule: []
};
}
componentDidMount() {
this.setState({
loadingTeams: true,
loadingSchedule: true,
});
this.unsubscribe = this.props.firebase
.teams()
.where('players', 'array-contains', '-LXkkB7GNvYrU4UkUMle')
.onSnapshot(snapshot => {
let teams = [];
snapshot.forEach(doc =>
teams.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
teams,
loadingTeams: false,
});
});
this.unsubscribe2 = this.props.firebase
.schedule()
.onSnapshot(snap => {
let schedule = [];
snap.forEach(doc =>
schedule.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
schedule,
loadingSchedule: false,
});
});
}
componentWillUnmount() {
this.unsubscribe();
this.unsubscribe2();
}
render() {
const { teams, schedule, loadingTeams, loadingSchedule } = this.state;
return (
<div>
<h2>Games</h2>
{loadingTeams && loadingSchedule && <div colSpan="12"><Spinner animation="border" role="status">
<span className="sr-only">Loading...</span>
</Spinner></div>}
{/* CONTENT */}
<Container fluid>
<Row>
{getTeams({ teams, schedule })}
</Row>
</Container>
</div >
);
}
}
function getTeams({ teams, schedule }) {
if (!teams) {
return null;
}
if (!teams.length) {
return null;
} else {
return teams.map(team => getGames({ team, schedule }))
}
}
function getGames({ team, schedule }) {
schedule.sort((a, b) => (a.time > b.time) ? -1 : 1)
if (!schedule) {
return null;
}
if (!schedule.length) {
return null;
} else {
return schedule.map(game => guts({ team, game }));
}
}
function guts({ team, game }) {
const image = {
height: '25px',
width: '25px'
}
if (team.id === game.team_1) {
var redTeam = team;
// set blueTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_2}')
} else {
var blueTeam = team;
// set redTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_1}')
}
if (game.team_1 === team.id || game.team_2 === team.id) {
var time = new Date(game.time.seconds * 1000);
var dateFormat = require('dateformat');
var finalTime = dateFormat(time, 'ddd mmm dd, h:MM tt')
return (
<Col lg='4' md='6' sm='12' key={game.uid} style={{ marginBottom: '15px' }}>
<Card>
<Card.Body>
<Row>
<Image src={team.logo} style={image} roundedCircle />
<p>{team.name}</p>
<div style={{ height: '25px', width: '25px', backgroundColor: 'red' }}></div>
</Row>
<Row>
<Image src={team.logo} style={image} roundedCircle />
<p>{team.name}</p>
<div style={{ height: '25px', width: '25px', backgroundColor: 'blue' }}></div>
</Row>
<Row>
<div>
{finalTime}
</div>
</Row>
</Card.Body>
<Accordion>
<Card style={{ margin: '0', padding: '0' }}>
<Card.Header>
<Accordion.Toggle as={Button} variant="link" eventKey="0">
Show Match IDs
</Accordion.Toggle>
</Card.Header>
<Accordion.Collapse eventKey="0">
<Card.Body>{game.match_id}</Card.Body>
</Accordion.Collapse>
</Card>
</Accordion>
</Card>
</Col>
);
}
}
export default withFirebase(PlayerGameList);
The items all load blank then a few seconds later all the console logs come through with the array objects. When I tell it to await the program just throws an error.

Resources