I am trying to create a dynamic list that depends on the amount of items from the backend; using axios get request, I am able to successfully get the data. However, when I try passing the data on to a function or printing the data, it doesn't print out the full data. It only shows the item tag (1, 0...) and when I tried accessing the data itself its giving me undefined.
await axios.get("backend.com/category")
.then((res) => {
var result = [];
//console.log(res.data) shows the array with the actual data
for (var resdata in res.data) {
//console.log(resdata) shows only 1 and 0
result.push(
<div>
<ItemCards data = {resdata}></ItemCards>
</div>
);
}
Here are the responses I've gotten:
when logging before mapping :
when logging inside mapping/ItemCards function:
Does anyone know how to fix this? Thank you very much for your time.
change your loop to this one if you want to pass your array data as props to child component:
for (var resdata of res.data) {
result.push(
<div>
<ItemCards data={resdata} />
</div>
);
}
Related
I have the code below
import React from 'react';
import axios from 'axios'
//let termExample = "https://en.wikipedia.org/w/api.php?action=query&origin=*&format=json&generator=search&gsrnamespace=0&gsrlimit=5&gsrsearch='New_England_Patriots'";
function APIThing({ term }) {
let wikioutput = []
axios.get(term).then((response) => {for (let i in response.data.query.pages) if(wikioutput) wikioutput.push(response.data.query.pages[i])})
function safe(x){
console.log("test", x[0], x)
if (x[0]){
console.log("test2", x[0], x[0].title)
return x[0].title
}
}
return (
<>
<p>TEST {safe(wikioutput)} TEST</p>
<p>EXAMPLE {safe([{title: "Test"}])}</p>
</>
)
}
export default APIThing;
When term is a wikipedia api url, I cannot return the value of wikioutput[0]. When I log wikioutput (or x in this case) I can see the entire array of objects, but x[0] returns undefined
I came across this post when I looked for an explanation for my very similar problem. I also have an asynchronous API call that gives me an array with several objects.
`useEffect(() => {
async function fetchMailContent() {
const response = await fetch(insertapiwebsitehere.com;
if (open) {
try {
const res = await response.json();
setMailContent(res);
setLoading(false);
} catch (err) {
console.error(err);
setError(true);
setLoading(false);
}
}
}
fetchMailContent();
}, [open]);`
The array I get from this is then given to another function where I attempted to place a value of one of the array's objects into a textfield component.
`export function MainTab(){
[shortened]
return(
<TextField variant={'outlined'} type="text" value={array[0].field_entry} />
)
}`
This would then result in the error of thejonymyster: array[0] is undefined. But a console.log(array[0]) would work fine and gave me the right object.
The fix is indeed rooted in the asynchronicity. You need to ask first if the object value exists before rendering it. This can be done with a simple ? after the array[0] and fixed the issue for me. So the Textfield component looks like this:
<TextField variant={'outlined'} type="text" value={array[0]?.field_entry} />
You should really be using hooks + useEffect in ReactJS that will most likely fix your problem, you can find examples here
what i ended up doing was json stringifying the array and regexing for the first element. json stringify array didnt give empty, miraculously. if anyone knows why array existed but array[0] didnt, id love to know someday. link to this post
My app is trying to make an API call and display the data from it in a modal. It isn't working because I'm storing the response from the API call as an array at the moment, meaning I can't access the sub-elements of the response.
My question, then: is there some way to either:
Store an object using State so that I can conveniently reference parts of that object?
OR
Parse an array as a JSON so I can take the data from the response and process it as a JSON when needed?
I realise this is kind of a weird question, so any answer that would achieve the same result would also be great.
Here's my code:
const DrinkPopup = (props) => {
let [drinkDetails,setDrinkDetails] = useState([])
let selectedDrinkId = props.drink
const getDrinkAsync = async (selectedDrinkId) => {
try {
let response = await fetch('https://www.thecocktaildb.com/api/json/v1/1/lookup.php?i='+selectedDrinkId);
let data = await response.json();
setDrinkDetails(data.drinks)
return true;
} catch (error) {
console.error(error);
}
}
useEffect (() => {
getDrinkAsync(selectedDrinkId)
console.log(selectedDrinkId)
console.log(drinkDetails)
},[]);
return(
<Modal isVisible={props.modalVisible}
onBackdropPress={()=>{props.setModalVisible(false)}} //allows closing modal by tapping outside it or back button
onBackButtonPress={()=>{props.setModalVisible(false)}}
animationIn={"slideInUp"}>
<View style={styles.infocard}>
<View style={styles.titleBox}>
<Text style={styles.header}>{drinkDetails.idDrink}</Text>
</View>
</View>
</Modal>
)
}
A really easy way to convert your array to an object is to use Object.assign like so:
Object.assign({}, data.drinks)
It would return an object with numbered keys and you can store that to state.
I am trying to access a detail page that i have for a list of items in my html(ionic). So when I click on 1 item I need to go on a detail page with information about that specific item. I am using mongo database for the items , the model of the items are : name and ingredients.
I am stuck here and don't know what to do , please help if you have a bit of time , thank you.
My code :
<ion-list *ngFor="let recipe of recipes ; let i = index">
<ion-item>
<ion-label>{{recipe.name}}</ion-label>
</ion-item>
</ion-list>
Request for the database trying to get the id to access the item (I get the
public findByName(id: string) {
return this.http.get<any>(
"http://localhost:9020/:id" + id
);
}
This is what I tried in typescript
this.route.paramMap.subscribe((paramMap) => {
if (!paramMap.has("id")) {
this.navCtrl.navigateBack("/home");
return;
}
this._recipeDetail = this.nutService
.findByName(paramMap.get("id"))
.subscribe((id) => {
this.id = id;
});
});
Also in route module :
{
path: "name/:id",
loadChildren: () =>
import("./r-d/r-d.module").then(
(m) => m.RDPageModule
),
},
But its not working , maybe I don't have the right approach? Can someone please guide me a bit ?
Thank you for your time
this._recipeDetail = this.nutService
.findByName(paramMap.get("id"))
.subscribe((id) => {
this.id = id;
});
You should be expecting your api to return you the recipeDetail.. you are not storing it properly.
this.nutService.findByName(paramMap.get("id"))
.toPromise().then((data) => {
this.recipeDetail = data;
});
And then, you can display your recipeDetail inside your template. I have also changed .subscribe() to .toPromise().then(). This is a better approach to avoid memory leaks in your app.
I am trying to create a chat application using React Virtualized. So far everything is working but I think think I am doing something wrong when using keyMapper and rowRenderer from the list.
The cache is using the id to store the height in the _rowHeightCache but it seems that the heights are looked up using the index and not the id. Im not sure if I should be passing the id as the rowIndex to CellMeasurer to get the right heights or something of that nature.
The issue is that the heights are wrong due to the changing order of the messages list so the index's don't have the proper heights and also that messages can be removed messing up the index orders. This I think should be fixed by using keyMapper to look up the heights but i must be doing it wrong.
The following is an edited down version of how I am using it.
constructor(props) {
this._cache = new CellMeasurerCache({
defaultHeight: 45,
fixedWidth: true,
keyMapper: (index) => _.get(this.props.messageList[index], ['msgId'])
});
}
render() {
const props = this.filterProps(this.props),
list = this.props.messageList,
rowCount = list.length;
return (
<InfiniteLoader
threshold={0}
isRowLoaded={this._isRowLoaded}
loadMoreRows={this._loadMoreRows}
rowCount={rowCount}>
{({onRowsRendered, registerChild}) => (
<AutoSizer>
{({width, height}) => (
<List
ref={(ref) => {
this.streamLane = ref;
registerChild(ref);
}}
height={height}
width={width}
onRowsRendered={onRowsRendered}
rowCount={rowCount}
rowRenderer={this._rowRenderer}
deferredMeasurementCache={this._cache}
rowHeight={this._cache.rowHeight}
/>
)}
</AutoSizer>
)}
</InfiniteLoader>
);
}
_rowRenderer({ index, key, parent, style }) {
return (
<CellMeasurer
cache={this._cache}
columnIndex={0}
key={key}
parent={parent}
rowIndex={index}>
<div style={{...style}}>
{this.props.messageList[index]}
</div>
</CellMeasurer>
);
}
_loadMoreRows({startIndex, stopIndex}) {
return new Promise((resolve) => {
if (!!stopIndex || !!startIndex || this.props.hasLastMessage || this.state.isPinned) {
return resolve();
}
this.props.loadOlder(() => resolve);
});
}
_isRowLoaded({index}) {
if (index === 0) return false;
return !!this.props.messageList[index];
}
}
Any help, suggestions or criticisms would be amazing.
The issue is that the heights are wrong due to the changing order of the messages list so the index's don't have the proper heights and also that messages can be removed messing up the index orders.
Your example code doesn't show how the order changes or what you do after it changes, so it's hard to say what you're doing wrong.
But when the order changes (eg you sort the data, or you add new items to the beginning of the list causing older items to shift) then you'll need to let List know that its cached positions may be stale. Do do this in your case, you'd need to call this.streamLane.recomputeRowHeights().
(You can pass the index of the first changed item to recomputeRowHeights but I assume in your case that maybe all items have shifted? I don't know, since I don't have all of your code.)
After you call recomputeRowHeights, List will re-calculate the layout using the heights reported to it by CellMeasurerCache. That cache uses your keyMapper function:
rowHeight = ({index}: IndexParam) => {
const key = this._keyMapper(index, 0);
return this._rowHeightCache.hasOwnProperty(key)
? this._rowHeightCache[key]
: this._defaultHeight;
};
So it won't need to remeasure items, even though their indices have changed.
First: This is the first project in which I am using RxJs, I thought I will learn best by using it.
I found this answer: Turning paginated requests into an Observable stream with RxJs
But it says in the comments:
You're exceeding the maximum call stack still. At around 430 pages returned. I think recursion might not be the best solution here
I want to query the Youtube Data API, the results come back in pages and I need to paginate through them.
I imagined a work flow like this could work:
1)Initiate a call
2)Check if the response has a 'nextPageToken'
3)If it has, do another request to the Youtube API
4)If not, finish
So to do this I could Imagine the following Observables / streams:
FirstRequestStream -A-X--------------->
ResponseStream -A-A-A-A--X-------->
RequestStream -I-A-I-A----------->
A = Action
I = Info from upper stream
X = Termination
(Not sure if this diagram is correct the way I made it)
So the ResponseStream depends on FirstRequestStream and RequestStream(using the merge function). The RequestStream depends on the ResponseStream( is this called a circulating observable ?)
-Is this the right approach ?
-Are 'circulating observables' a good thing, are they even possible ?(I had problems creating one).
-Any other way I should try first?
-Is it possible to create interdependent observable streams ?
Thank you for your help.
You are overcomplicating this problem, it can be solved a lot easier using defer operator.
Idea is that you are creating deferred observable (so it will be created and start fetching data only after subscription) and concatenate it with the same observable but for the next page, which will be also concatenated with the next page, and so on ... . And all of that can be done without recursion.
Here is how the code looks:
const { defer, from, concat, EMPTY, timer } = rxjs; // = require("rxjs")
const { mergeMap, take, mapTo, tap } = rxjs.operators; // = require("rxjs/operators")
// simulate network request
function fetchPage(page=0) {
return timer(100).pipe(
tap(() => console.log(`-> fetched page ${page}`)),
mapTo({
items: Array.from({ length: 10 }).map((_, i) => page * 10 + i),
nextPage: page + 1,
})
);
}
const getItems = page => defer(() => fetchPage(page)).pipe(
mergeMap(({ items, nextPage }) => {
const items$ = from(items);
const next$ = nextPage ? getItems(nextPage) : EMPTY;
return concat(items$, next$);
})
);
// process only first 30 items, without fetching all of the data
getItems()
.pipe(take(30))
.subscribe(e => console.log(e));
<script src="https://unpkg.com/rxjs#6.2.2/bundles/rxjs.umd.min.js"></script>
Here is my solution using the rxjs operators expand, reduce and empty using the HttpClient module:
Suppose your API response is an object containing shaped like the following
interface Response {
data: items[]; // array of result items
next: string|null; // url of next page, or null if there are no more items
}
You could use expand and reduce like so
getAllResults(url) {
return this.http.get(url).pipe(
expand((res) => res.next ? this.http.get(res.next) : EMPTY),
reduce((acc, res) => acc.concat(res.data), [])
);
}
I shamelessly reuse the code snippet from Oles Savluk, with its good fetchPage function, and I apply the ideas explained in the blog article linked to by Picci (in the comments), using expand.
Article on expand by Nicholas Jamieson
It gives a slightly simpler code, with recursion hidden in the expand call (and comments of the article show how to linearize it, if needed).
const { timer, EMPTY } = rxjs; // = require("rxjs")
const { concatMap, expand, mapTo, tap, toArray } = rxjs.operators; // = require("rxjs/operators")
// simulate network request
const pageNumber = 3;
function fetchPage(page = 0) {
return timer(1000).pipe(
tap(() => console.log(`-> fetched page ${page}`)),
mapTo({
items: Array.from({ length: 10 }).map((_, i) => page * 10 + i),
nextPage: ++page === pageNumber ? undefined : page,
}),
);
}
const list = fetchPage().pipe(
expand(({ nextPage }) => nextPage ? fetchPage(nextPage) : EMPTY),
concatMap(({ items }) => items),
// Transforms the stream of numbers (Observable<number>)
// to a stream with only an array of numbers (Observable<number[]>).
// Remove if you want a stream of numbers, not waiting for all requests to complete.
toArray(),
);
list.subscribe(console.log);
<script src="https://unpkg.com/rxjs#6.2.2/bundles/rxjs.umd.min.js"></script>
LuJaks is definitively the simplest approach !
For a one line example, suppose you have a function that make a http request for a given page and that returns a (partial) array of data. We call that function until server returns empty array :
import { Observable, EMPTY, of } from "rxjs";
import { expand, reduce } from "rxjs/operators";
// Mock a request that returns only 5 pages...
function httpGet(p): Observable<number[]> {
if (p > 5) { return of([]); }
return of(new Array(10).fill(0).map((_, i) => p * 10 + i));
}
httpGet(0).pipe( // get the fist page
expand((value, index) => (value.length > 0 ? httpGet(index + 1) : EMPTY)), // other pages
reduce((a, v) => [...a, ...v], []), // optional if you want only one emit
).subscribe((x) => console.log(x));