how to read a multipage API using react using Axios - reactjs

so im trying to use this api that even if you call just the element/people he returns only ten people instead of the 82 total i want , so i have to slowly go through them all and keep adding the values on a list, i wanted to have a list with all the character names and one with all the other results,so i did this (im using SWAPI star wars api btw)
import React, { useEffect, useState } from "react";
import api from "./components/services/api";
export default function App() {
const [listPeople, setList] = useState();
const [page,setPage]=useState(1)
const [names,setNames]=useState([])
const [full,setFull]=useState([])
const [finished,setFinished] =useState(false);
useEffect(() => {
api
.get(`people/?page=${page}`)
.then((response) => setList(response.data))
.catch((err) => {
console.error("an error occured: " + err);
});
},[page]);
function generateList() {
if(listPeople.next != null) {
var i=0
while(i <9) {
setPage(page +1)
setNames(names.concat(listPeople.results.map((res) => (res.name))))
setFull(full.concat(listPeople.results))
i++
}
}
else {
if(!finished) {
setNames(names.concat(listPeople.results.map((res) => (res.name))))
setFull(full.concat(listPeople.results))
setFinished(true)
}
}
console.log(names)
console.log(full)
}
return (
<>
<button onClick={generateList}>click here!</button>
<ul></ul>
</>
);
}
as you can see i need to press a button everytime i want to get the rest of the elements in the api(first 10 buttons then 20.. until 82)
what i wanted was to click the button once and have the whole list(of only the names and of all the results) already in an array/object
but if i try to do that using a while loop the hoops it doesnt work,i ve tried using mapp but cant think of a way to apply it in this situation and the few that i did got me the same effect, and there is no numeric atribute on the api that shows the number of pages, just one that says next with the next page url, like so
ex:https://swapi.dev/api/people
"count": 82,
"next": "https://swapi.dev/api/people/?page=2",
"previous": null,
"results": [
{
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"hair_color": "blond",
"skin_color": "fair",
"eye_color": "blue",
"birth_year": "19BBY",
"gender": "male",
"homeworld": "https://swapi.dev/api/planets/1/",
"films": [
"https://swapi.dev/api/films/1/",
"https://swapi.dev/api/films/2/",
"https://swapi.dev/api/films/3/",
"https://swapi.dev/api/films/6/"
],
"species": [],
"vehicles": [
"https://swapi.dev/api/vehicles/14/",
"https://swapi.dev/api/vehicles/30/"
],
"starships": [
"https://swapi.dev/api/starships/12/",
"https://swapi.dev/api/starships/22/"
],
"created": "2014-12-09T13:50:51.644000Z",
"edited": "2014-12-20T21:17:56.891000Z",
"url": "https://swapi.dev/api/people/1/"
}, ... until the 10th person
again i just want to press the button and have the entire list in my console.log, and the '/people' adress doesnt return everything.
im still a beginner at react so every help is appreciated, thanks :)

Related

SurveyJS and NextJS - Issue when setting new state of variable

Version of NextJS: 12.0.8
Version of SurveyJS: 1.9.5
Testing Browser: Chrome, FF
Testing the app (vercel):
You can test the app here: https://jem-surveyjs-nextjs.vercel.app/
Description of the issue:
I combine SurveyJS with NextJS to generate a multi-page survey. The code for the survey is added into a component.
A custom variable can be manipulated using useState.
Every time the variable is set to a different value the survey reloads and jumps back to the first page instead of staying on the current page.
You can clearly see this effect in the dev-tools console.
To replicate the issue simply do the following:
Create a Nextjs app
Add the survey-react module (npm i survey-react)
add the following content to assets/json/survey-json.js
export const surveyJSON = {
"pages": [
{
"name": "page1",
"elements": [
{
"type": "radiogroup",
"name": "Speech Test",
"hasComment": true,
"commentText": "add comment",
"choices": [
"item1",
"item2",
"item3"
]
}
]
},
{
"name": "page2",
"elements": [
{
"type": "radiogroup",
"name": "Try again",
"hasComment": true,
"commentText": "add comment",
"choices": [
"item4",
"item5",
"item6"
]
}
]
}
]
};
Add the following content to components/Survey.js
import {useState, useEffect} from 'react'
import * as SurveyJs from "survey-react";
import "survey-react/survey.css";
import {surveyJSON} from "../assets/js/survey-json";
// Survey Component
const Survey = (props) => {
const [custParam, setCustParam] = useState(false);
// Avoid rehydration conflict
// https://nextjs.org/docs/messages/react-hydration-error
const [hasMounted, setHasMounted] = useState(false);
useEffect(() => {
setHasMounted(true);
}, []);
if (!hasMounted) {
return null;
}
/**
* Handle Custom Function
*/
const handleClick = () => {
// Example 3:
setCustParam(!custParam);
console.log("HANDLE CLICK - CUSTOM PARAM: ", custParam)
};
/**
* afterRenderQuestionHandler
*/
const afterRenderQuestionHandler = function (survey, options) {
// Example 2:
//setCustParam(!custParam);
console.log("AFTER RENDER QUESTION - CUSTOM PARAM: " , custParam)
const tarea = document.querySelector('.sv_q_other');
if ( tarea ) {
tarea.onclick = function(){
handleClick();
}
}
}
let mySurvey = new SurveyJs.Model(surveyJSON);
mySurvey.showCompletedPage = true;
// Example 1:
//setCustParam(true);
console.log("BEFORE RENDER SURVEY - CUSTOM PARAM: " , custParam)
return (
<SurveyJs.Survey
model={mySurvey}
onAfterRenderQuestion={afterRenderQuestionHandler}
/>
);
}
export default Survey;
Finally add this to your pages/index.js
import styles from '../styles/Home.module.css'
import Survey from '../components/Survey'
export default function Home() {
return (
<div>
<Survey />
</div>
)
}
As you can see I have added 3 different examples of how the issue can be created in the components/Survey.js. Example 3 is the active one right now, but you can un-comment Example 2 or Example 1 to see a different effect of the issue.
How can I avoid this reloading and jump to the first page?
Any help that can point me in the right direction would be appreciated
UPDATE:
Many thanks #juliomalves!!
I have added a useState for my Survey Object
const [mySurvey, setMySurvey] = useState({});
and inside the already existing useEffect I've added the following:
setMySurvey( new SurveyJs.Model(surveyJSON) );
I finally just had to remove this line to make it all work:
let mySurvey = new SurveyJs.Model(surveyJSON);

How should I display single-data-object fetched by API in react-native?

I am learning React-Native and trying to display user profile on profile screen.
Displaying a list of objects obtained from the server is pretty straightforward, but I am confused on how do I display a single-object that my API-call fetches.
This is my fetched object data output which I get when I do console.log(getProfileApi.data) right before displaying it.
Object {
"email": "user1#gmail.com",
"first_name": "User",
"gender": "Male",
"id": 2,
"last_name": "1",
"profile": Object {
"address": null,
"city": null,
"country": null,
"dob": "2021-11-01",
"profile_pic": "http://192.168.0.218:8000/media/profile/user1_profile.jpg",
"title": null,
"zip": null,
},
"user_type": "Owner",
}
This is how I am trying to display the fetched data, but the data is not displayed.
<FlatList
// getProfileApi contains the response from server which is shown above
data={getProfileApi.data}
renderItem={({ item }) => (
<Text>{item.email}</Text>
)}
/>
How do I extract the data and display it. Is there any other component to use instead of Flat-List to display a single-object data?
First of all, FlatList is used to display the data of the array. It is usually used to display big lists, like products list in an e-commerce app, users lists, posts, etc. To display simple data as you have, you can use ScrollView. Follow the below approach to display your data in ScrollView.
import React, { useCallback, useEffect, useState } from 'react';
import { View, Text, ScrollView, StyleSheet } from 'react-native';
const YourScreenName = () => {
// declare state variable to handle loading UI
// set initial value to true, so when screen load initially it display loading
const [isLoading, setLoading] = useState(true);
const [user, setUser] = useState(null);
useEffect(() => {
getData();
}, []);
const getData = useCallback(() => {
setLoading(true); // Start the loader, So when you start fetching data, you can display loading UI
getProfileApi().then((data) => {
setUser(data);
setLoading(false);
}).catch((error) => {
// display error
setLoading(false); // stop the loader
})
// or your any data fetching query
// setUser(getProfileApi.data);
// setLoading(false);
}, []);
return (
<ScrollView
style={styles.container}
contentContainerStyle={styles.contentContainer}
>
{
isLoading && (
<Text>Loading</Text>
);
}
{
!isLoading && user && (
<View>
<Text>Email : {user.email}</Text>
<Text>Name : {user.name}</Text>
</View>
)
}
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
contentContainer: {
flexGrow: 1,
justifyContent: 'center',
alignItems: 'center'
}
});
export default YourScreenName;
Instead of using a flat list you can directly render the UI component and make it's content dynamic just like below
<Text>{getProfileApi.data.email}</Text>
there is no need for a flat list here because it is used to render a list of data but for a single data set, you can directly use it in your UI.
First create a state like this
const [myArray, setMyArray] = useState([]);
then you have to set your array in the state after you have fetched the data from your api instead of directly calling getProfileApi which looks like a function
setMyArray(yourresponsearray);
then inside your Flatlist component you can pass that state inside the data prop
<FlatList
data={myArray}
renderItem={({ item }) => (
<Text>{item.email}</Text>
)}
/>

cannot access value passed from context api in firestore fetch data function

so I am trying to fetch some data from my firestore through the following bit of code
import React, { useContext, useState, useEffect } from 'react';
import { CocktailContext } from '../../context/CocktailContext';
import fire, { db } from '../../Config/firebase';
import RecipeCard from '../RecipeCard/RecipeCard';
import NavBar from '../NavBar/NavBar';
export default function SavedItemPage() {
const [ content, setContent ] = useState(null);
const { uid } = useContext(CocktailContext);
useEffect(() => {
listenForMessages(uid)
}, []);
const listenForMessages = (id) => {
db.collection('users').doc(id).onSnapshot(function(doc) {
const allMessages = [];
allMessages.push(doc.data());
setContent(allMessages);
console.log(content);
});
};
return (
<div>
<NavBar />
</div>
);
}
what should basically happen here is that the context API which looks like below, takes the uid and feeds it into listenForMessages() which then does the job of fetching the content
{
"name": "State",
"value": null,
"subHooks": []
},
{
"name": "Context",
"value": {
"recipes": "[{…}, {…}, {…}, {…}, {…}, {…}]",
"searchTerm": "ƒ searchTerm() {}",
"updateUid": "ƒ updateUid() {}",
"uid": "MfLXrE5czaYK7fYiTFuqFv9SZV02"
},
"subHooks": []
},
{
"name": "Effect",
"value": "ƒ () {}",
"subHooks": []
}
]
trouble is that when the page loads i am met with this errors- FirebaseError: Function CollectionReference.doc() requires its first argument to be of type non-empty string, but it was: ""
so the trouble is that listenForMessages() is not able to access the uid value in the context API.
Can anyone tell me how to solve this issue?
I don't know how Firebase works, but the useContext maybe is incorrect ?
usually you use it this way :
[state, dispatch] = useContext(CocktailContext)
Which will give you the whole state. In the state you provided there is 3 objects in your state so i guess you will access the uid by doing something like:
useEffect(() => listenForMessages(state[1].value.uid));
I see there is a ' ] ' closing your provided state but nothing at the beginning, just adapt in case i miss a node level.

React Axios useEffect hook, different types of errors when rendering to the user interface

I am pretty new to full stack, and im using MERN, so forgive me if this is an easy/silly fix (though I looked at several and different types of sources, with no luck thus fur).
I can not seem to render my array of items to reacts ui. I get the GET request and have logged it to the console so i Know its there but when i go and try and map it or put it into a div with a simple function to iterate it or just simply to display some information from the JSON file, i keep getting errors. Here is the JSON file (which passed a validation check) and the Component I'm working in.
[
{
"_id": "5e1ff19d926f7c245ce01d7c",
"foodmenu": [
{
"food": "burger",
"orders": 0
},
{
"food": "fish",
"orders": 0
},
{
"food": "salad",
"orders": 0
},
{
"food": "curry",
"orders": 0
},
{
"food": "sushi",
"orders": 0
},
{
"food": "egg rolls",
"orders": 0
},
{
"food": "Jacket potatoe",
"orders": 0
},
{
"food": "hash browns",
"orders": 0
},
{
"food": "mash potatoe",
"orders": 0
},
{
"food": "pizza",
"orders": 0
},
{
"food": "sandwhich",
"orders": 0
},
{
"food": "omlete",
"orders": 0
}
]
}
]
import React, { Fragment, useState, useEffect } from "react";
import axios from "axios";
function Orders() {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = await axios("api/orders/foodmenu/foodtypes");
setData(result.data);
console.log(result.data);
};
fetchData();
}, []);
return (
<div>
{data[0].foodmenu.map(data => (
<div key={data._id}>{data.foodmenu}</div>
))}
</div>
)
};
export default Orders;
I get Type errors like:
TypeError: Cannot read property 'foodmenu' of undefined
&
TypeError: Cannot read property 'map' of undefined
I appreciate the help.
const tablelist = data.map((data, key) => {
return (
<tr key={key}>
<td>{data.orderID}</td>
<td>{data.orderDateTime}</td>
<td>{data.priceTotal}</td>
)
Do this in your useeffect() and call {tablelist} inside return
If you add a simple length check first, it should work. The problem is that the component is rendered before the data has loaded. This means that data[0] will be undefined and therefore accessing properties on it will fail.
Using a length check, since && is evaluated lazily, your component will return false until your data array has elements inside it and not try to execute the rest of the code in the component.
return (data.length &&
<div>
{data[0].foodmenu.map(data => (
<div key={data._id}>{data.foodmenu}</div>
))}
</div>)
You can do it like this. First you need to validate the length before accessing data[0] because the array is empty before fetching data from server. Don't use same variable name data in both state and map function parameter. Though it works, it is not clear to read. Also you cannot use data._id as the key of mapped div because it need a unique value. In your scope, data variable is the local parameter, not the state. That is the reason for using another parameter name item instead of data. Also you can use the second parameter index in map function for the key. Finally you have to use proper display text like {item.food} - {item.orders} instead trying to print the object or array.
return (
<div>
{data.length > 0 && data[0].foodmenu.map((item, index) => (
<div key={index}>{item.food} - {item.orders}</div>
))}
</div>
);

Filter/skip missing API array results not working in React (Hooks)

I'm building a React app (with hooks) that gets data from the NASA image search API. Turns out, not all query results actually have an image attached. If I search "cars" I get an empty array. If I search "space" I get the word "undefined" in console.log.
Image of the missing items in API:
nasa image results
GitHub: https://github.com/irene-rojas/nasa/tree/searchErrorHandling
I was advised to separate the filtering function away from setData. I have tried two versions of a function, one using filter and map, the other using if then map. I want to create a 1:1 match to pass data to the ImageSearch component.
State
const [data, setData] = useState([
{
"id": "",
"title": "",
"date": "",
"description": "",
"src": ""
}
]);
const imageSearch = () => {
axios.get(`https://images-api.nasa.gov/search?q=${query}`)
.then(res => {
setData(transformImgSearch(res.data.collection.items.slice(0,6)));
console.log(res.data.collection.items.slice(0,6));
// endpoint testing
// console.log(res.data.collection.items[0].links[0].href);
});
console.log(data);
};
Function attempt 1:
function transformImgSearch(props) {
props.filter(prop =>
prop.links).map(prop =>
[
{
"id": prop.data[0].nasa_id,
"title": prop.data[0].title,
"date": prop.data[0].date_created,
"description": prop.data[0].description,
"src": prop.links[0].href
}
]);
console.log(data);
}
Function attempt 2
function transformImgSearch(props) {
if (props.links) {
props.map(prop =>
[
{
"id": prop.data[0].nasa_id,
"title": prop.data[0].title,
"date": prop.data[0].date_created,
"description": prop.data[0].description,
"src": prop.links[0].href
}
]);
}
}
The component I ultimately want to use the data, mapped 1:1 with state.
{data.map(image => {
return (
<ImageSearch
key={image.id}
title={image.title}
date={image.date}
description={image.description}
id={image.id}
src={image.href}
/>
)
})}
This console.log(res.data.collection.items.slice(0,6)); returns the correct basic data, but attempts at returning the data to match the array in state don't work.

Resources