Mapping API response to JSX - arrays

I have a JSON response in the form:
[
{
"author": 2,
"title": "how to draw",
"slug": "how-to-draw",
"content": "second attempt",
"status": 0,
"date_created": "2020-11-28T20:25:41.650172Z",
"date_posted": "2020-11-28T20:25:41.650172Z",
"tags": "none"
},
{
"author": 1,
"title": "Admin test post",
"slug": "admin-test-post",
"content": "This is just a test of the content field!\r\n\r\nParagraph 2",
"status": 4,
"date_created": "2020-11-16T21:02:02.521705Z",
"date_posted": "2020-11-16T21:37:40.477318Z",
"tags": "a b c"
}
]
And I am struggling to map the response correctly to a series of divs in ReactJS. The idea would be to have a div for each post, each with the author, title, etc. of the post. I can use post[0].title to take the title of the first element of the array. Am I missing something obvious?
import React, {useState} from 'react';
import axios from 'axios';
const Django = () => {
const [posts, setPosts] = useState([]);
// proxy set to http://127.0.0.1:8000/
const apiLink = "/api/post/";
const fetchData = async () => {
const res = await axios.get(`${apiLink}`, { headers: { Accept: "application/json"} });
console.log(res.data);
setPosts([res.data]);
}
return (
<div>
<h1>Posts:</h1>
<button onClick={fetchData}>Load jokes</button>
{posts.map((post, key) => {
//console.log(joke);
return (
<div className="data" key={key}>
{post.title}
{key}
</div>
)})}
</div>
);
}
export default Django;

It is not exactly clear what your problem is, but I suppose you're confused about why you aren't getting any data by directly calling {post.title} in the map body. That's because you're putting the result from your fetch, which is already an array into another array: setPosts([res.data]);. Just change the state setter to setPosts(res.data); and everything in the map should work fine.

Related

How to embed dynamic data into a JSON string

I'm working on a React app that interacts with JW Playe API. The API wants me to send data in JSON format. The problem is that I need to embed dynamic input data into that JSON string. I don't know how to do that. Please help me out.
Here is the JSON object:
const data =
'{ "upload": { "method": "fetch", "download_url": I NEED TO PUT DATA COMING FROM REACT STATE (Input) HERE }, "metadata": {"title": "My Fetch Video", "author": "Dzenis H."} }';
You can write something like this:
const App = () => {
const [url, setUrl] = useState('');
const data = '{ "upload": { "method": "fetch", "download_url": "I NEED TO PUT DATA COMING FROM REACT STATE (Input) HERE" }, "metadata": {"title": "My Fetch Video", "author": "Dzenis H."} }';
const updateJson = () => {
let parseData = JSON.parse(data);
parseData.upload.download_url = url;
let converted = JSON.stringify(parseData);
console.log(converted);
}
return (
<div>
<input type="text" value={url} onChange={e => setUrl(e.target.value)} />
<button onClick={updateJson}>Update</button>
</div>
)
}

Fetching , rendering and sorting data in Reactjs

How can i fetch data from a json file and render them in an ascending order? For example let´s say i have this json file
[{
"id": 0,
"name": "Vernon Dunham",
"company": "Qualcore",
"email": "vernon.dunham#qualcore.com"
}, {
"id": 1,
"name": "Dori Neal",
"company": "Sunopia",
"email": "dori.neal#sunopia.com"
}, {
"id": 2,
"name": "Rico Muldoon",
"company": "Airconix",
"email": "rico.muldoon#airconix.com"
}
and i want to render the data with only Id and name in ascending order.
I tried coding it in different ways but since i am still a beginner i can´t really figure it out. Thank you so much
You can use .map and .sort:
const data = [{
"id": 0,
"name": "Vernon Dunham",
"company": "Qualcore",
"email": "vernon.dunham#qualcore.com"
}, {
"id": 1,
"name": "Dori Neal",
"company": "Sunopia",
"email": "dori.neal#sunopia.com"
}, {
"id": 2,
"name": "Rico Muldoon",
"company": "Airconix",
"email": "rico.muldoon#airconix.com"
}]
const sorted = data.map(d => ({id: d.id, name: d.name})).sort((el1, el2) => el1.id - el2.id)
console.log(sorted)
Once you set the new sorted and mapped array to a state variable, you can map over it to render.
Example
//Functional Component:
const Example = ({data}) => {
const [sortedData, setSortedData] = useState([])
useEffect(() => {
if(data) setSortedData(data
.map(d => ({id: d.id, name: d.name}))
.sort((el1, el2) => el1.id - el2.id)))
}, [data])
return(
sortedData?.map(element => <div key={element.id}> {element.name} </div>)
)
}
You can short your array data using the .sort method.
App.js
import React, { useState, useEffect } from "react";
import Data from "./data.json";
function App() {
const [data, setData] = useState();
const sortDataByField = (array, field) => {
return array.sort((a, b) =>
a[field].toString().localeCompare(b[field].toString())
);
};
useEffect(() => {
const sortData = sortDataByField(Data, "name");
setData(sortData);
}, []);
return (
<div className="App">
{data?.map((item) => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}
export default App;
data.json
[{
"id": 0,
"name": "Vernon Dunham",
"company": "Qualcore",
"email": "vernon.dunham#qualcore.com"
}, {
"id": 1,
"name": "Dori Neal",
"company": "Sunopia",
"email": "dori.neal#sunopia.com"
}, {
"id": 2,
"name": "Rico Muldoon",
"company": "Airconix",
"email": "rico.muldoon#airconix.com"
}]
Import and set up a useState hook
import React, {usesState} from 'react'
const [companies, setCompanies] = useState([])
fetch the data
const data = fetch('/api/v1/companies', {method: 'GET'})
sort first to order, then map to only get id and name
const sorted = data.sort((a, b)=>{return a.name > b.name ? 1 : -1})
.map(({id, name}) => {return {id, name}})
/* this return only id and name sorted by name
*
* if you wanna sort by id, do this:
* .sort((a, b) => a.id - b.id)
*/
When you done sorting and mapping, store it to that useState hook so that you can render it:
setCompanies(sorted)
Then render it, also prevent errors by checking if you state has data before rendering:
companies &&
companies.map(({id, name}) => <h1 key={id}>{name}</h1>)
// or
companies
? companies.map(({id, name}) => <h1 key={id}>{name}</h1>)
: <p>No data</p>
All code
import React, {usesState, useEffect} from 'react'
export default App = () =>{
const [companies, setCompanies] = useState([])
useEffect(()=>{
fetchData()
}, [])
const fetchData = async () => {
try{
const data = fetch('/api/v1/companies', {method: 'GET'})
if(data){
const sorted = data.sort((a, b)=>{return a.name > b.name ? 1 : -1})
.map(({id, name}) => {return {id, name}})
setCompanies(sorted)
}
else{
console.log('No data')
}
}
catch (err){
console.log(err)
}
}
return(
<div>
companies &&
companies.map(({id, name}) => <h1 key={id}>{name}</h1>)
</div>
)
}

How can I acces a nested object from my API

I made an API with spring-boot that is able to create Cook objects with Recipes objects in it. The idea is that one cook has a relationship with (multiple) recipe(s). When I perform a get request to my server, I am able to show the entire (Cook) object with nested (Recipe) object(s). I am also able to render Cook object properties to the browser, e.g name, age etc. However, I am not able to render Recipe properties to the browser from within the Cook object. It always gives me an undefined.
My code for the API request that shows the name from a cook correctly :
const [posts, setPosts] = useState([]);
useEffect(()=> {
axios.get(" http://localhost:9090/api/cooks")
.then(response => {
console.log(response.data) //response.data.recipe.x doesn't seem to work.
setPosts(response.data);
})
.catch(error => {
console.log(error)
})
},[])
return (
<div>
{posts.map(post => <li key={post.id}>{post.name}</li>)}; //post.recipe.x also doesn't work
</div>
)
}
const Index = () => {
return (
<div>
<Alt />
</div>
)
}
The raw JSON data from the server looks like this:
{
"id": 1,
"name": "bart",
"surName": "Janssen",
"gender": "male",
"age": 3,
"recipes": [
{
"id": 1,
"recipeName": "Hamburger",
"portion": 1,
"meat": "Ground meat",
"vegetable": "Tomato, Pickles, other greens",
"other": "salt, pepper, mustard, ketchup",
"instructions": "bla bla"
},
{
"id": 2,
"recipeName": "tomato soup",
"portion": 2,
"meat": null,
"vegetable": "tomato",
"other": "salt, stock",
"instructions": "bla bla"
}
]
}
]
However, I really need to access the Recipe properties too.
I tried accessing recipes through response.data[0], response.data[0][0], response.data[0][1], response.data[[0][0]] However, they all seem to give me an undefined back or an error.
I also tried to use JSON.Stringify(response.data), but this didn't give me any successes too.
I really could use some help. Thanks in advance :)
try to use useEffect with async/await
useEffect(async () => {
const result = await axios(
'http://localhost:9090/api/cooks',
);
setData(result.data.recipes);
});
and another moment, you initialize
const [posts, setPosts] = useState([]);
as an array - useState([ ]), but trying to write a data as object -
{
"id": 1,
"name": "bart",
"surName": "Janssen",
....
}
that's why you need to straight extract recipes
setData(result.data.recipes);

star wars api, how do I get values from api?

I'm using the https://swapi.dev/api/people/ and I'm having an interesting issue with an api I haven't encountered before. Here is an example response of the first value form the api using https://swapi.dev/api/people/
[
{
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"hair_color": "blond",
"skin_color": "fair",
"eye_color": "blue",
"birth_year": "19BBY",
"gender": "male",
"homeworld": "http://swapi.dev/api/planets/1/",
"films": [
"http://swapi.dev/api/films/1/",
"http://swapi.dev/api/films/2/",
"http://swapi.dev/api/films/3/",
"http://swapi.dev/api/films/6/"
],
"species": [],
"vehicles": [
"http://swapi.dev/api/vehicles/14/",
"http://swapi.dev/api/vehicles/30/"
],
"starships": [
"http://swapi.dev/api/starships/12/",
"http://swapi.dev/api/starships/22/"
],
"created": "2014-12-09T13:50:51.644000Z",
"edited": "2014-12-20T21:17:56.891000Z",
"url": "http://swapi.dev/api/people/1/"
},
]
The value I want to get is the first value of the films array, which I can achieve however its just a url. I'd need to somehow grab that url, make an api call with it and then get the first film. An example response after calling the api for the first value of films looks like this:
{
"title": "The Empire Strikes Back",
"episode_id": 5,
"opening_crawl": "It is a dark time for the\r\nRebellion. Although the Death\r\nStar has been destroyed,\r\nImperial troops have driven the\r\nRebel forces from their hidden\r\nbase and pursued them across\r\nthe galaxy.\r\n\r\nEvading the dreaded Imperial\r\nStarfleet, a group of freedom\r\nfighters led by Luke Skywalker\r\nhas established a new secret\r\nbase on the remote ice world\r\nof Hoth.\r\n\r\nThe evil lord Darth Vader,\r\nobsessed with finding young\r\nSkywalker, has dispatched\r\nthousands of remote probes into\r\nthe far reaches of space....",
"director": "Irvin Kershner",
"producer": "Gary Kurtz, Rick McCallum",
"release_date": "1980-05-17",
}
I've never used an api that has urls as values, any ideas on how this might work?
Here is my current code:
Page.jsx
import React, {useState, useEffect} from 'react';
import '../css/Page.css';
import axios from 'axios';
const Page = () => {
const [data, setData] = useState([]);
const fetchData = async () => {
try {
const res = await axios.get(
"https://swapi.dev/api/people/"
);
setData(res?.data.results);
} catch (error) {
console.log(error);
}
}
useEffect(() => {
fetchData();
}, [])
const mapData = () => {
if(data && data.length > 0) {
return (
<>
{data.map(item => (
<div className="itemContainer" key={item.name}>
<div className="image"><img src="" alt=""/></div>
<div className="content">
<span className="title">Name: </span><span className="name">{item.name}</span>
<br />
<span className="title">Birthyear: </span><span className="name">{item.birth_year}</span>
<br />
<span className="title">Homeworld: </span><span className="name">{item.homeworld}</span>
</div>
</div>
))}
</>
)
}
}
return (
<div className="container">
{mapData()}
</div>
);
}
export default Page;

how do I Mock API and following the same approach as my static array?

I have a React hooks component, which uses an HTML div-alike table to render data, and the data is being fetched from the server. I need to test the component to see if the table has data by mocking the API call. Below is the current code. I want to remove the use of arr completely and make avoid getting {users} rendered as text/plain as you can you in the below image
const arr = [
{
"demo": [
{
"_id": "T0810",
"title": "Historian",
"tags": [
"demo"
],
"queries": [],
},
{
"_id": "T0817",
"title": "book",
"tags": [
"demo"
],
"queries": [],
},
],
"demo_2": [
{
"_id": "T0875",
"title": "Program",
"tags": [
"demo_2",
"Control"
],
"queries": [],
},
{
"_id": "T0807",
"title": "Interface",
"tags": [
"demo_2"
],
"queries": [],
}
]
}];
const keys = Object.keys(arr[0]);
export default function Demo () {
const [isModalOpen, setModalIsOpen] = useState(false);
const [users, setUsers] = useState([]);
const handleOnClick = async () => {
try {
const { data } = await axios.get('https://run.mocky.io/v3/0d7aa6e3-fc01-4a47-893d-7e1cc3013d4e');
setUsers(data);
// Now that the data has been fetched, open the modal
setModalIsOpen(true);
} catch (err) {
console.error("failed", err);
}
};
return (
<div className="container">
<>
{keys.map((key) => (
<div className="col" key={key}>
<div className="row">{key}</div>
{arr[0][key].map((item) => (
<div className="row" key={item.technique_id} onClick={() => handleOnClick(item)}>{item.technique}</div>
))}
</div>
))}
</>
{isModalOpen && <Modal onRequestClose={() => setModalIsOpen(false)} data={users}/>}
</div>
);
}
Second attempt...Assuming I understand the ask then a package like Nock can do the trick (https://github.com/nock/nock).
The approach is to allow Nock to mock the API and return the result locally without calling the server. In your case it will be something like:
const nock = require('nock');
const arr = [...]; // your original definition of arr
const scope = nock('https://run.mocky.io')
.get('/v3/0d7aa6e3-fc01-4a47-893d-7e1cc3013d4e')
.reply(200, arr);
// The rest of your code "as is"
...
This setup of Nock will intercept every HTTP call to https://run.mocky.io and will specifically return the content of arr in response to a GET /v3/0d7aa6e3-fc01-4a47-893d-7e1cc3013d4e.
Typically, I would not put this code in the main code.
Instead you can use it as part of a testing script that runs without dependency on an active server. E.g. testing during build.

Resources