Hi I'm currently working on a react native app and I'm trying to get a users playlist and then return it in a flatlist. I've completed getting a users access_token but I'm a little stuck on figuring out how to actually use the data. I'm fairly new to using api data.
export default function SpotifyGetPlaylist(props) {
const { colors } = useTheme();
const [token, setToken] = useState('');
const [data, setData] = useState({});
React.useEffect(() => {
getData();
}, []);
const getData = async() => {
setToken (await AsyncStorage.getItem('#access_token'));
console.log("token retrieved")
}
const handleGetPlaylists = () => {
axios.get("https://api.spotify.com/v1/me/playlists", {
headers: {
Authorization: `Bearer ${token}`,
},
}).then(response => {
setData(response.data);
console.log(response.data)
})
.catch((error) => {
console.log(error);
});
};
}
This part works fine and returns data into the console as such
Object {
"collaborative": false,
"description" : "Maailman – Päivittäinen katsaus siihen, mitä kappaleita kuunnellaan eniten juuri nyt.",
"external_urls": Object {
"spotify": "https://open.spotify.com/playlist/37i9dQZEVXbMDoHDwVN2tF",
},
"href" : "https://api.spotify.com/v1/playlists/37i9dQZEVXbMDoHDwVN2tF",
"id" : "37i9dQZEVXbMDoHDwVN2tF",
"images": Array [
Object {
"height": null,
"url" : "https://charts-images.scdn.co/assets/locale_en/regional/daily/region_global_large.jpg",
"width" : null,
},
],
"name": "Maailman – Top 50",
"owner": Object {
"display_name" : "Spotify",
"external_urls": Object {
"spotify": "https://open.spotify.com/user/spotify",
},
"href": "https://api.spotify.com/v1/users/spotify",
"id" : "spotify",
"type": "user",
"uri" : "spotify:user:spotify",
},
"primary_color": null,
"public" : true,
"snapshot_id" : "NzAzNDIxMzk0LDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDY2Njk=",
"tracks": Object {
"href" : "https://api.spotify.com/v1/playlists/37i9dQZEVXbMDoHDwVN2tF/tracks",
"total": 50,
},
"type": "playlist",
"uri" : "spotify:playlist:37i9dQZEVXbMDoHDwVN2tF",
},
But I'm having problems actually rendering anything into the Flatlist which at the moment looks like this.
const renderItem = ({item}) => {
<Item title={item.name}/>
}
return (
<View style={styles.container}>
<Button
onPress = {handleGetPlaylists}
color = "#1DB954"
style = {{ color: colors.text, width: 100 }}
title = "Get your playlists"/>
<FlatList
data = {data}
renderItem = {renderItem}
keyExtractor= {(item) => item.id.toString()}
/>
</View>
)
I'm unsure how I get the data from the api my hunch is that I would have to use data.items.name to access it but it doesnt work for me. Help is much appreciated
Nothing is appearing in the FlatList render because you're not returning your Item. Because you have curly braces around the body of the function, you have to explicitly return the component.
const renderItem = ({item}) => {
return <Item title={item.name} />;
}
Related
When making a request to my API from a component and using react-data-table-component everything works perfectly but if I try to make the request from my Product Provider the pagination is incorrect and no longer works as expected.
With this code I make the request, datatable and pagination from my component working perfectly:
import React, { useState, useEffect, useCallback, useMemo } from "react";
import axiosClient from "../config/axiosClient";
import DataTable from 'react-data-table-component-with-filter'
import { CSVLink } from "react-csv"
import { Link } from 'react-router-dom'
import useProducts from "../hooks/useProducts";
const removeItem = (array, item) => {
const newArray = array.slice();
newArray.splice(newArray.findIndex(a => a === item), 1);
return newArray;
};
const ProductsTest = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [currentPage, setCurrentPage] = useState(1);
const [searchBox, setSearchBox] = useState('')
const STRING_TRADUCTIONS = { "KILOGRAM" : "KILOGRAMOS", "GRAMS" : "GRAMOS", "BOX" : "CAJA", "PACKAGE" : "PAQUETE", "BOTTLE" : "BOTE", "PIECES" : "PIEZAS", "BAG" : "BOLSA", "LITER" : "LITRO" }
const fetchUsers = async (page, limit = perPage, search = searchBox) => {
setLoading(true)
const dataOnLs = localStorage.getItem('cmtjs')
const config = {
headers: {
"Content-Type": "application/json",
apiKey: dataOnLs
} ,
params: {
limit,
page,
search
}
}
const response = await axiosClient(`/products`, config)
const data = response.data.products.docs.map( doc => (
{
_id: doc._id,
idProduct: doc.idProduct,
barCode: doc.barCode,
name: doc.name,
presentation: STRING_TRADUCTIONS[doc.presentation],
salePrice: doc.salePrice,
purchasePrice: doc.purchasePrice,
stock: doc.stock,
user: doc.user.username,
category: doc.category.name,
provider: doc.provider.name
}
))
setData(data);
setTotalRows(response.data.products.totalDocs);
setLoading(false);
};
useEffect(() => {
fetchUsers(1)
}, []);
const columns = useMemo(
() => [
{
name: "ID",
selector: "idProduct",
sortable: true
},
{
name: "Código de Barras",
selector: "barCode",
sortable: true
},
{
name: "Nombre",
selector: "name",
sortable: true
},
{
name: "Presentación",
selector: "presentation",
sortable: true
},
{
name: "Precio",
selector: "salePrice",
sortable: true
},
{
name: "Stock",
selector: "stock",
sortable: true
},
{ cell: row =>
<Link to={ `/dashboard/product/${row._id}`}>
<button className='btn btn-ghost text-xs'>
Mas
</button>
</Link>}
]
);
const handlePageChange = page => {
fetchUsers(page);
setCurrentPage(page);
};
const handlePerRowsChange = async (newPerPage, page) => {
fetchUsers(page, newPerPage);
setPerPage(newPerPage);
}
const headers = [
{ label: "ID", key: "idProduct" },
{ label: "Código de Barras", key: "barCode" },
{ label: "Nombre", key: "name" },
{ label: "Presentación", key: "presentation" },
{ label: "Precio Venta", key: "salePrice" },
{ label: "Precio Compra", key: "purchasePrice" },
{ label: "Stock", key: "stock" },
{ label: "Creador", key: "user" },
{ label: "Categoría", key: "category" },
{ label: "Proveedor", key: "provider" }
]
const paginationComponentOptions = {
rowsPerPageText: 'Mostrar',
rangeSeparatorText: 'de',
selectAllRowsItem: true,
selectAllRowsItemText: 'Todos',
};
const clear = () => {
setPerPage(10)
setSearchBox('')
fetchUsers(1, 10, '')
}
return (
<div>
<input type="text" onChange={(e)=> setSearchBox(e.target.value)}/>
<button onClick={ ()=> fetchUsers()}>Buscar</button>
<CSVLink data={data} headers={headers} filename={"productos.cdtmx.csv"} className="cursor-pointer">
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABmJLR0QA/wD/AP+gvaeTAAACnUlEQVRoge2YXUhUQRiGn7HVRVddc8k0QawQw6hQohKSlsp+oDulQBGCbuxGQ1itDIx+IIWINKK86S4o6ibFEG9ywQzCEJLVSCylorYf1CJaXXe6SPvZ1ePZs3tczfNcfsP3zfvOzHc4M2BgYGAQSYTWxJSrReUg6oHEYHNtuRu1TDkupKx27Tp38+9glJZKv9AmPgQSpYiq9w+GYGBBxU8jrf6RUAwsCgwDkcYwEGlM/gFLVf4JkBcEWJQSvw+/Ua4cJYixWjElxoemcB4CDKgRrwqfZGJ0lImpqYChFe53IZefIeAIhUX8DDJsleZkyffA8jBgNkXTe+ouXy93U2Ev+R3PSE7jU70T96VHZNrSdROphCoDHu8kZ1quAVC1u4x4cxwAdQfLMZuiaei4xevPb/VTqYDqI9Ta56Rj4Ak2SxLHCw6zaU0Wxbl7Gfw4QlPnbT01KjLLZ3RuTj9oxJ61lQp7CYUbdiAQVN5rwOOd1EvfvATVxAMfXtHcdZ+k2ATy127hzrN2nIM9emlTRVA7AJAwff4BhJj/QmfpDuwNT0pCsNPOSVA7ULA+j7Jth3g63EfXUC9H8vazJ3t72MRoQbUBsymaK0UOhBCcbbtBbUsTUkoai2uIi4nVU6Miqg2c3HeM7NWZtPc/xjnYQ8+Ii9Y+JxnJadQUHtVToyKqDOSkrqPSXopP+jj/8M+jQF3bdby+KSrspWxOz9JNpBKqmtj1fojk6oKA+Ev3CCsdO8MuKhiWx7/QYmY2A9/CVt2n/4Vgth6oBS4Cod0FfVKa3Z4fgMd/yPb8i+ay/pma30ZTD+RoXl5rxiqtqbxo7vxH83/ZA0sKw0CkWdYGxsOmQj1j/gHNBoQQDhABBXVkTEocCzifgYGBgQp+AlpMnf09Cu/RAAAAAElFTkSuQmCC"/>
</CSVLink>
<DataTable
columns={columns}
data={data}
progressPending={loading}
pagination
paginationServer
paginationTotalRows={totalRows}
paginationDefaultPage={currentPage}
onChangeRowsPerPage={handlePerRowsChange}
onChangePage={handlePageChange}
selectableRows
//onSelectedRowsChange={({ selectedRows }) => console.log(selectedRows)}
paginationComponentOptions={paginationComponentOptions}
noDataComponent="No hay resultados"
/>
{
searchBox && searchBox !== '' && <button onClick={ () => clear() }>Limpiar</button>
}
</div>
)
}
But I have my product provider where I make a get request to all my products, avoiding making the requests from my component and having the data globally, but if I use "getProducts" from my provider, the first view of the datatable is correct however , when clicking on a page or next, the pager advances but the data displayed does not, for example: it shows me the first 10 records but I ask for the next 10 and the pager advances correctly but the data is still the first 10 records, No I know how to use my provider and make the pager show the following data depending on what the user needs.
This is the code of my provider to obtain the product data
const getProducts = async(page, limit, search) => {
const dataOnLs = localStorage.getItem('cmtjs')
const config = {
headers: {
"Content-Type": "application/json",
apiKey: dataOnLs
} ,
params: {
page,
limit,
search
}
}
try {
const { data } = await axiosClient(`/products`, config)
setProducts(data)
} catch (error) {
So in my component I call "getProducts" from my provider to have the products data in "products" using the useEffect hook
useEffect(() => {
getProducts()
setData(products.products?.docs)
setTotalRows(products.products?.totalDocs)
setLoading(false)
}, []);
In my paginator to obtain the following 10 product records, I click and the text changes that indicates which page is being shown, but the data remains the same as the first page
const handlePageChange = page => {
getProducts(page); // it does not show the next 10 records as it happened in the fetch of my component
setCurrentPage(page); // OK
};
In the same way, my browser no longer works using it in this way, I only changed the function to call my provider now, but it does not work
<input type="text" onChange={(e)=> setSearchBox(e.target.value)}/>
<button onClick={ ()=> getProducts()}>Buscar</button>
I would like to know if you can help me to make my datatable and browser work using my provider. Thanks.
In this code
useEffect(() => {
getProducts()
setData(products.products?.docs)
setTotalRows(products.products?.totalDocs)
setLoading(false)
}, []);
You are calling getProducts(), which is asynchronous. Then you try setData(products...), but the asynchronous call did not finish yet, so products was not updated yet. When eventually the asynchronous code terminates, the useEffect statement is not triggered again, because the dependency array states that the effect is only executed when the component mounts.
Split up your effect in two parts instead, so the second effect gets triggered when new products are available:
useEffect(() => {
getProducts()
}, []);
useEffect(() => {
setData(products.products?.docs)
setTotalRows(products.products?.totalDocs)
setLoading(false)
}, [products]);
i have a json file as a server, i need filter the data json when i click in a button, example, in the Navbar i have:
const NavBar = ({setSearchValue, setType}) => {
const handleType = (heroType) =>{
setType(heroType)
}
return (
// this input is ok, i can search data from here
<input id='searchInput' placeholder='Search' onChange={(event) => {setSearchValue(event.target.value)}}/>
//these are the buttons
<Nav.Link onClick={() => handleType('All')}>All Heroes</Nav.Link>
<Nav.Link onClick={() => handleType('Flying')} >Flying Heroes</Nav.Link>
<Nav.Link onClick={() => handleType('Flightless')}>Flightless Heroes</Nav.Link>
and this is where i need to show it
//import Navbar
import NavBar from "./NavBar";
const Home = () => {
// saved the data i need to show
const [content, setContent] = useState();
// saved the searchvalue of navbar, its ok.
const [searchValue, setSearchValue] = useState("");
// i tried save the button data here, next with a IF function i tried to show it, no work
const [type, setType] = useState("Flying");
useEffect(() => {
// get json dta
const getData = async () => {
const response = await db.get("data");
let data = response.data.filter((val) => {
// if no searchValue, return all
if (searchValue === "") return val;
//if searchVale, return coincidences
else if (val.nombre.toLowerCase().includes(searchValue.toLowerCase()))
return val;
});
// returns bootstrap rows depending on number of elements
const rows = [...Array(Math.ceil(data.length / 3))];
const imagesRows = rows.map((row, idx) =>
data.slice(idx * 3, idx * 3 + 3)
);
//saved all in setContent
setContent(
//code
)
getData();
}, [searchValue]);
return (
<>
<NavBar setSearchValue={setSearchValue} setType={setType} />
//show content
<Container>{content >= 0 ? <p>No results</p> : content}</Container>
</>
);
};
I've tried a lot of things, i think i need change a lot of code i a want this work.
Any help would be extremely appreciated.
EDIT
Json:
{
"data": [
{
"id": 0,
"nombre": "Punisher",
"puedeVolar": false,
"nombreReal": "Frank Castle",
"avatarURL": ""
},
{
"id": 1,
"nombre": "Green Arrow",
"puedeVolar": false,
"nombreReal": "Oliver Jonas Queen",
"avatarURL": ""
},
{
"id": 2,
"nombre": "Human Torch",
"puedeVolar": true,
"nombreReal": "Jonathan Lowell Spencer",
"avatarURL": ""
},
{
"id": 3,
"nombre": "Martian Manhunter",
"puedeVolar": true,
"nombreReal": "J'onn J'onzz",
"avatarURL": ""
},
{
"id": 4,
"nombre": "Aquaman",
"puedeVolar": false,
"nombreReal": "Arthur Curry",
"avatarURL": ""
}
}
So when clicked button display heroes.puedeVolar === false or display heroes.puedeVolar === true, depending of the button clicked
Post your JSON to help you.
You're lifting up correctly the states, you just need to do the same that you did with the input.
Put the type as a dependecie of useEffect and inside it filter your JSON with your type value.
useEffect(()=>{
//... Keep your working code
let data = response.data.filter((val) => {
//Keep your working code or refactor it
//Add you new filter params
if (val.type === type) return val;
}
}, [type, searchValue]);
I am using use-axios hooks in my React application to fetch data from an API and paginate it with 5 items each page.
Sandbox: https://codesandbox.io/s/axios-hooks-infinite-scrolling-forked-hxbnq?file=/src/index.js
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import useAxios from "axios-hooks";
function App() {
const [dataPerPage, setDataPerPage] = useState({});
const [page, setPage] = useState(1);
const [{ data, loading, error }] = useAxios({
url: "http://localhost:6366/api/rule-sets",
params: { page: page }
});
useEffect(() => {
setDataPerPage((d) => ({ ...d, [page]: data }));
}, [page, data]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error!</p>;
return (
<div>
<button onClick={() => setPage((p) => p + 1)}>load more</button>
<pre>
{JSON.stringify(
[].concat(...Object.values(dataPerPage).map((p: any) => p)),
null,
2
)}
</pre>
</div>
);
}
The API is returning me this:
[
{
"user": {
"username": "q123123",
"displayName": "John Snow"
},
"active": false,
"_id": "612496f44683924ea85d731b",
"filename": "my file 2.xls",
"rules": [
{
"_id": "612496f44683924ea85d731c",
"sourceSystem": "CRM_MOBILE",
"classifications": "OPT,BLM",
"segment": null,
"calendar": "Standard",
"createSla": true,
"slaDurationInMinutes": 300,
"sendNotification": true,
"useHolidays": false
}
],
"createdAt": "2021-08-24T06:51:32.552Z",
"updatedAt": "2021-08-24T06:51:32.552Z",
"__v": 0
},
...
]
I want now to pass each object in the Array to my <RuleSetsItem> component as a prop:
<RuleSetsItem ruleSets={ruleSet} />
But I can not iterate it like this:
{ [].concat(...Object.values(dataPerPage).map((p: any) => (
p ? p.map(item => <RuleSetsItem>) : ''
)))}
as there is no map possible and also the first render is undefinded || null.
I also tried it with Object.keys(p).map() but this just returns me 01234.
This is my original implementation as reference before I implemented pagination with just a basic GET that returned me all stored items:
{ruleSets?.length
? ruleSets.map((ruleSet: RuleSetType) => (
<div key={ruleSet._id}>
<RuleSetsItem ruleSets={ruleSet} />
</div>
))
: ''}
I've got a problem with making a correct loop in React. I want to fetch data from JSON to don't repeat components. I tried to make two loops and then two maps, but everything was in bad order. The other problem is that "description" is also an array that's why I'm not able to deal with it
JSON:
{
"oswiecim": [
{
"header": "Oświęcim Zasole",
"description": [
"Rejon ulic św Maksymiliana Kolbego",
"i Stanisławy Leszczyńskiej"
]
},
{
"header": "Oświęcim Zasole",
"description": [
"Rejon ulic Więźniów Oświęcimia",
"Obozowej, Polnej i Legionów"
]
},
{
"header": "Stare Miasto",
"description": [
"Rejon Rynku i Placu ks. Jana Skarbka oraz ",
"ulic Zamkowej i Władysława Jagiełły"
]
},
{
"header": "Stare Miasto",
"description": [
"Cmentarz Parafialny oraz rejon",
"ul. Wysokie Brzegi."
]
},
{
"header": "Osiedle Chemików",
"description": [
"Największa pod względem liczby ludności",
"dzielnica Oświęcimia"
]
}
]
}
React:
import '../../styles/selection/Selection.scss'
import { useEffect, useState } from 'react';
const Selection = () => {
const [data, setData] = useState({})
const getData = async () => {
await fetch('https://jsoneditoronline.org/#left=cloud.b95a27020e1c45e9b3a7c95a74fc5d49', {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
.then(res => res.json())
.then(data => {
setData(data)
})
}
useEffect(() => {
getData()
}, [])
const headers = []
const descriptions = []
for (const item of data.oswiecim) {
headers.push(item.header)
descriptions.push(item.description)
}
return (
<div className="selection">
{headers.map(item => (
<h1>{item}</h1>
))}
{descriptions.map(item => (
item.map(elem => (
<p>{elem}</p>
))
))}
</div>
);
}
export default Selection;
The result should look like this:
You don't need to separate header and description in two different variables.
So try something like this:-
return (
<div className="selection">
{data.oswiecim?.map((item) => (
<>
<h1>{item.header}</h1>
{item.description?.map((description) => (
<p>{description}</p>
))}
</>
))}
</div>
);
Live demo
Replace the setData(data); with following. It will just give the array you need to iterate,
setData(data.oswiecim);
Remove the following code,
const headers = []
const descriptions = []
for (const item of data.oswiecim) {
headers.push(item.header)
descriptions.push(item.description)
}
Replace return statement with following,
<div className="selection">
{data &&
data.map(item => (
<>
<div>{item.header}</div>
{item.description &&
item.description.map(descriptionItem => <p>{descriptionItem}</p>)}
</>
))}
</div>
I'm currently working on a react native app, and I have an issue about render information (in an array) that I fetch from my DB. To do that I have to write a .map.
I receive the data from the fetch in the console.log.
When I call my function with this "()".
<Text>Choisissez votre Choix Club </Text>
<TouchableOpacity>
<View>
<Text>{this.renderMesClubs()}</Text>
</View>
</TouchableOpacity>
An error message appear
TypeError: undefined is not an object (evaluating '_this.state.sport.club.map')
Below you can find the all code page.
class ChoixClub extends Component {
constructor(props) {
super(props);
this.state = {
sport: {club: []},
};
}
getMesClubs = () => {
const headers = new Headers({'Content-type': 'application/json'});
const options = {
method: 'GET',
headers: headers,
};
fetch('http://localhost:8080/inscription/sport', options)
.then((response) => {
return response.json();
})
.then(
(data) => {
const club = JSON.stringify(data);
this.setState({sport: club});
console.log(this.state.sport.club);
},
(err) => {
console.log(err);
},
);
};
renderMesClubs = () => {
return this.state.sport.club.map((element) => {
return (
(
<View className="articles">
<Text>{element.nomClub}</Text>
</View>
),
console.log(element.nomClub)
);
});
};
componentDidMount() {
this.getMesClubs();
}
render() {
return (
<SafeAreaView>
<Text>Choisissez votre Choix Club </Text>
<TouchableOpacity>
<View>
<Text>{this.renderMesClubs()}</Text>
</View>
</TouchableOpacity>
<Text>Choisissez votre rival</Text>
<TouchableOpacity></TouchableOpacity>
</SafeAreaView>
);
}
}
export default ChoixClub;
I hope my message is clear enough for you to solve it, and thanks in advance for your answer!
You are calling JSON.stringify(data), which turns "data" into a string. assuming that the server is returning valid JSON, then calling response.json(), which you are already doing, should give you a Javascript object, hopefully an array, you should map over that, not turn it back into a string and map over the string.
to check if data really is an array you can use:
if(!Array.isArray(data)){
throw new Error('expected the response to be an array');
}
All your data is actually contained by an array, so you need to specify the element or iterate it. As #user1852503 said, no JSON.stringify is needed because .then((response) => { return response.json(); }) does the trick
// Let's your data
let data = [ { "_id": "5f44dcc0a3da3a3008a71e5d", "sport": { "_id": "5f44dcc0a3da3a3008a71e5e", "club": [ { "_id": "5f44dcc0a3da3a3008a71e5f", "nomClub": "String", "classement": "String", "dateMatch": "String", "classementDB": "String" } ] }, "__v": 0 } ];
data[0].sport.club.map(element => {
// I just console log it to see if it works
console.log(element.nomClub)
})