it appears the api im using breaks a list of 250 assets into multiple pages. im trying to call numerous pages to be listed in an ant design table
constructor(props) {
super(props);
this.state = {
data: [],
loading: true
}
}
componentDidMount() {
axios.all([
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=1&sparkline=true&price_change_percentage=24hr'),
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=2&sparkline=true&price_change_percentage=24hr')
])
.then(axios.spread((res => {
const data = res.data;
this.setState({ data, loading: false })
})))
}
render() {
const { data } = this.state;
const tableData = data.map(row => ({
Rank: row.market_cap_rank,
Symbol: row.symbol,
Name: row.name,
Price: row.current_price,
marketCap: row.market_cap,
priceChange: row.price_change_percentage_24h,
sparkline: row.sparkline_in_7d.price
}))
const columns = [{
title: 'Rank',
dataIndex: 'Rank',
key: 'market_cap_rank',
}, {
title: 'Symbol',
dataIndex: 'Symbol',
key: 'symbol',
render: (value) => {
return <span>{value.toUpperCase()}</span>;
},
}, {
title: 'Name',
dataIndex: 'Name',
key: 'name',
}, {
title: 'Price',
dataIndex: 'Price',
key: 'current_price',
render: (value) => {
return <span>$<b>{value.toFixed(2)}</b></span>;
},
}, {
title: 'Market Cap',
dataIndex: 'marketCap',
key: 'market_cap',
render: (value) => {
return`$${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
},
...
<Table
pagination="false"
loading={this.state.loading}
dataSource={tableData}
columns={columns}
size="small"
/>
this works, but only displays the first page and not the second as well
sorry for the silly question, maybe someone can take a moment to assist me as this question probably stems from a lack of general understanding. it's sure nice to hear from other people on here! :)
You have to update your componentDidMount like below
axios.all([
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=1&sparkline=true&price_change_percentage=24hr'),
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=2&sparkline=true&price_change_percentage=24hr')
])
.then(resArr =>{
const data = [];
resArr.map(res=> data.push(...res.data));
this.setState({ data, loading: false });
});
This is because the function you pass to axios.spread receives the result of the requests in two different arguments.
Like in the example from the axios doc
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));
your axios.spread will receive separately the two pages :
You can then concatenate the two pages to have your data
axios
.all([
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=1&sparkline=true&price_change_percentage=24hr'),
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=2&sparkline=true&price_change_percentage=24hr')
])
.then(axios.spread(((page1, page2) => {
const data = [...page1.data, ...page2.data];
this.setState({ data, loading: false })
})))
If you want to have more than a determinate number of pages you can make use of rest operator and flatten the array using spread and concat
axios
.all(arrayOfLinks)
.then(axios.spread(((...pages) => { // use rest operator to get an array of pages containing your data
const data = [].concat(...pages.data); // use spread operator in a concat to flatten your arrays of data
this.setState({ data, loading: false })
})))
Related
i'm tryning to play with the react table mui kitchen sink and do not understand why the value aren't visible . the Header is visible when switching to JSON it's the data in the table not visible anymore. on the console.log, data and data1 have the same structure.
const columns = React.useMemo(
() => [
{
Header: 'ID',
accessor: 'id',
},
{
Header: 'LICENSE PLATE',
accessor: 'licenseplate',
},
{
Header: 'DRIVER NAME',
accessor: 'driver',
},
{
Header: 'SUBMITTED ON',
accessor: 'submittionDate',
},
{
Header: 'NOTES',
accessor: 'notes',
},
{
Header: 'STATUS',
accessor: 'status',
},
],
[]
)
const NoteList = Object.keys(NOTES).map(key => ({ ...NOTES[key], id: key }))
const [data1, setData1] = React.useState(React.useMemo(() => makeData(10), []))
const [data, setData] = React.useState(React.useMemo(() => NoteList), [])
const [skipPageReset, setSkipPageReset] = React.useState(false)
useEffect(() => {
// This gets called after every render, by default
// (the first one, and every one after that)
// console.log('NOTES', NOTES);
// console.log('NOTESLIST', NoteList);
console.log('DATA', data);
console.log('DATA1', data1);
}, [])
i don't know where's the mistake.
thanks for the help
After some hours, the mistake has been solved.
The accessor name has to match the name attribute in the json file. And there's no need to use the object.key function. the use of useMemo is enough.
I am doing this:
case LOAD_PAGES:
return {
...state,
pages: [...state.pages, action.pages],
};
And I have a component that every time I enter to it, it send the same data to the store so I am getting lots of duplicate data.
The pages array looks like this:
pages: [
{
key: 0,
menuName: 'Home',
pageType: 'HomePage',
dataIndex: 'HomePage0'
},
{
key: 1,
menuName: 'Employer Chat',
pageType: 'EmployerChat',
dataIndex: 'EmployerChat1'
},
]
This is the React component:
const handlePageLoad = () => {
if (siteById.data) {
siteById.data.pages.map((p, index) => {
return loadPagesAction({
key: index,
menuName: p.menuName,
pageType: p.pageType,
dataIndex: p.pageType + index,
});
});
}
};
useEffect(() => {
if (siteById.data.pages.length) {
handlePageLoad();
}
}, []);
Any ideas?
Here's "smart" way to filter duplicates.
function filterDuplicates(array, areEqual) {
return array.filter((item, pos) => {
return array.findIndex((other) => areEqual(item, other)) == pos;
});
}
console.log(
filterDuplicates([
{ key: 1, name: 'test' },
{ key: 2, name: 'apple' },
{ key: 1, name: 'test' },
], (a, b) => a.key == b.key)
);
Pass array to first argument and equality comparer to second argument of filterDuplicates. I got this idea from that answer.
TypeScript
function filterDuplicates<T>(array: T[], areEqual: ((a: T, b: T) => boolean)): T[] {
return array.filter((item: T, pos: number) => {
return array.findIndex((other: T) => areEqual(item, other)) == pos;
});
}
You can do something like this :
Check if there is an existing page in your state or not, if return same or else push
case LOAD_PAGES:
return {
...state,
pages: state.pages.findIndex(page => page.key === action.pages.key) >= 0 ?
state.pages :
[...state.pages, action.pages]
};
There are many ways to solve your problem and here is how I would approach it:
Instead of using an array, store your pages in an object and use the already defined keys as keys for the object. You can use Object.values(store.pages) or Object.entries(store.pages) to get an array of the pages the way you did before.
Simply using set, the duplicated element can be omitted.
return { ...state, arr: Array.from(new Set([...state.arr, ...newArr]))};
I have a scenario
{
data:'',
skus: [
{ id: 1, ......}
{ id: 2, ......}
{ id: 3, ......}
]
api_first:'',
}
I have that schema and want to setState in somewhere skus on selected sku item and return changed item to original array
this.setState(produce(prevstate =>
prevstate.data.sku.obj="change"
))
this works for me
I'd recommend to use functional setState and map:
const updateSku = (skuId, data) => {
this.setState(prevState => ({
...prevState,
skus: prevState.skus.map(sku => {
if (sku.id === skuId) {
return {...sku, ...data}
} // else
return sku
})
}))
}
State immutability is important sometimes devs mutate states those are complex with multiple nested levels. You can always update state with simple javascript object update stratigy but I would suggest you to use immerjs. It reduces the code and makes it much more cleaner and easy to understand what is going to change. It helps a lot in redux reducers where a complex state needs to be updated with mutation
Here is example
https://immerjs.github.io/immer/docs/example-setstate
/**
* Classic React.setState with a deep merge
*/
onBirthDayClick1 = () => {
this.setState(prevState => ({
user: {
...prevState.user,
age: prevState.user.age + 1
}
}))
}
/**
* ...But, since setState accepts functions,
* we can just create a curried producer and further simplify!
*/
onBirthDayClick2 = () => {
this.setState(
produce(draft => {
draft.user.age += 1
})
)
}
Using immerjs, it will be
const updateSku = (skuId, data) => {
this.setState(produce(draft => {
const sku = draft.skus.find(s => s.id === skusId);
Object.assign(sku, data);
}));
}
What I have understood from your explanation is that when the SKU item gets changed you want to update the state Skus.
Here I've provided a solution for the same please try to relate with your example.
let's assume you have the following react component.
import React, { Component } from "react";
export class Sku extends Component {
constructor(props) {
super(props);
this.state = {
data: "",
skus: [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
{ key: "key3", value: "value3" },
],
APIFirst: "",
};
}
handleSkuChange = (data) => {
this.setState(({ skus }) => {
const newSkus = skus.map(sku => (sku.key === data.key ? { ...sku, ...data } : sku));
return { skus: newSkus };
});
};
render() {
const { data, skus, APIFirst } = this.state;
const newSku = { key: 'key2', value: 'newSku' };
console.log("states =>", data, skus, APIFirst);
return (
<button type="button" onClick={() => this.handleSkuChange(newSku)}>'Change sku'</button>
);
}
}
The handleSkuChange function will work like it,
const skus = [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
{ key: "key3", value: "value3" },
];
const handleSkuChange = (data) => (
skus.map(sku => (sku.key === data.key) ? { ...sku, value: "newValue" } : sku));
const newSku = { key: 'key2', value: 'newSku' };
console.log('old skus', skus);
console.log('new skus', handleSkuChange(newSku));
I have an function to get the data from firebase,
and I want to set data into a state after getting,
so I just declare an array and push all data to them after that I setState this array with my state
But when I log these state or render it, I got some issues With RN 0.61.2, It's Work perfect In RN 0.58.0 I don't Know Why/How!!!
when I log the state I just see overrated data "I just have 4 items in DB" but the logger print to me more than tens,
and when I just render these data "state" they tell me
JSON value "5" of type NSNumber cannot be converted to Nsstring
So how can I avoid that?
here is my code Snippet
this.state = {
RecommendedProviders: [],
}
componentDidMount() {
this._recommendedProvider();
}
_recommendedProvider = () => {
let Recommended = [];
firebase
.database()
.ref('recommendationProviders')
.once('value')
.then(snapshot => {
snapshot.forEach(childSnapshot => {
Recommended.push({
gKey: childSnapshot.key,
id: childSnapshot.val().id,
username: childSnapshot.val().username,
service: childSnapshot.val().service,
aboutMe: childSnapshot.val().aboutMe,
coordinates: {
longitude: childSnapshot.val().coordinates.longitude,
latitude: childSnapshot.val().coordinates.latitude,
},
city: childSnapshot.val().city,
mobileNumber: childSnapshot.val().mobileNumber,
token: childSnapshot.val().token._55,
});
});
console.log('Recommended', Recommended); //when i log this i can just see array of 4 item
this.setState({RecommendedProviders: Recommended});
});
// .then(() => this.setState({RecommendedProviders: Recommended}));
};
UI
<FlatList
horizontal
data={this.state.RecommendedProviders}
renderItem={({item}) => {
console.log('#item/', item); /when i log this i can just see more than 4 item maybe 20 :D
}}
keyExtractor={(item, index) => index.toString()}
/>
Okay, first about that logger:
JSON value "5" of type NSNumber cannot be converted to Nsstring
This error is because keyExtractor expects a string as returned value. So, to avoid it, you can try this:
<FlatList
keyExtractor={(item, index) => 'item' + index}
/>
Finally, about your log:
I created an expo's snack with your gist, wich SDK is based on react-native 0.59, you can check it here. I also tried to simulate your async fetch with the code below and it worked fine.
new Promise((resolve, reject) => {
setTimeout(resolve, 2000, [
{ id: 1, username: 'test', service: 'example' },
{ id: 2, username: 'test', service: 'example' },
{ id: 3, username: 'test', service: 'example' },
{ id: 4, username: 'test', service: 'example' },
]);
}).then(res => {
const providers = [];
res.forEach(item => providers.push(item));
this.setState({ RecommendedProviders: providers }, () =>
console.log('object', this.state.RecommendedProviders)
);
});
I believe that may be the solution to downgrade your RN version, if it does not negatively impact your application.
Hope it will help you.
I´m gonna break my head with a stone ^^"
I have this code:
this.af.database.list('/Documentos', { preserveSnapshot: true })
.subscribe(snapshots => {
snapshots.forEach(snapshot => {
console.log(snapshot.key, snapshot.val());
});
})
With that I extract all the data correctly, but now I want to add to an object array or something like that (I started few weeks ago with Firebase + Angular2).
I wanna fill that array to load the [ng2 smart table] and if I´m thinking partially well with a properly well-formed array I will fill the table but I don´t know how. Hope anyone can help.
If you want an map (object) with key: value, you can easily do this with Array.prototype.reduce():
const map = snapshots.reduce((map, snapshot) => {
map[snapshot.key] = snapshot.val();
}, {});
Well, according to the example : https://akveo.github.io/ng2-smart-table/#/examples/using-filters ...
(You can find the source code here: https://github.com/akveo/ng2-smart-table/blob/master/src/app/pages/examples/filter/basic-example-source.component.ts)
... you have to put your data in a JSON object :
settings = {
columns: {
id: {
title: 'ID',
filter: false,
},
name: {
title: 'Full Name',
filter: false,
},
username: {
title: 'User Name',
filter: false,
},
email: {
title: 'Email',
filter: false,
}
}
};
data = [
{
id: 1,
name: 'Leanne Graham',
username: 'Bret',
email: 'Sincere#april.biz',
},
{
id: 2,
name: 'Ervin Howell',
username: 'Antonette',
email: 'Shanna#melissa.tv',
}
];
"settings" contain your columns names and "data" must match the columns from "settings".
It would be easier if we knew a bit more of your code (columns of your table + data returned by your service), but I assume something like that would work :
data = [];
this.af.database.list('/Documentos', { preserveSnapshot: true })
.subscribe(snapshots => {
snapshots.forEach(snapshot => {
data.push(
{ [snapshot.key]: snapshot.val() }
);
});
})
Please note that this will create a JSON array with only one key/val per row. We do need to know more about your data to give you a propre answer.
Ok, I found the solution with a simple Array() x)
this.af.database.list('/Documentos', { preserveSnapshot: true })
.subscribe(snapshots => {
snapshots.forEach(snapshot => {
let length = todo.documentos.push(snapshot.val()); // documentos is an array in the class
todo.source.load(todo.documentos);
});
});