I have a simple list in React where I'm fetching data from an array, and it's working.
But now that I want to fetch data from an external API, I have the following error
Cannot read property 'map' of undefined
I tried replacing .data with .json() but didn't work.
https://codesandbox.io/s/silly-taussig-e3vy7?file=/src/App.js:561-571
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import axios from "axios";
export default () => {
const initialList = [
{
id: "1",
name: "John"
},
{
id: "2",
name: "Doe"
},
{
id: "3",
name: "Seb"
}
];
const [list, setList] = React.useState([]);
const [name, setName] = React.useState("");
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/users/")
.then((response) => {
setList(response.data.list);
})
.catch((err) => console.log(err));
}, []);
function handleChange(event) {
setName(event.target.value);
}
function handleAdd() {
const newList = list.concat({ name });
setList(newList);
setName("");
}
return (
<div>
<div>
<input type="text" value={name} onChange={handleChange} />
<button type="button" onClick={handleAdd}>
Add
</button>{" "}
</div>
<ul>
<div>
{list.map((item, index) => (
<li key={item.id}>
<div>{item.name}</div>
</li>
))}
</div>
</ul>
</div>
);
};
The result of your API doesn't have data.list
Try instead:
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/users/")
.then((response) => {
setList(Object.values(response.data));
})
.catch((err) => console.log(err));
}, []);
Issue is with the way you are processing the response from the XHR call. The data returned by the URL https://jsonplaceholder.typicode.com/users/ is an array. There is no element called list in the data. So when you do response.data.list, the list variable gets set to undefined.
PFB revised code
https://codesandbox.io/s/hungry-tdd-xjw6z
...
axios
.get("https://jsonplaceholder.typicode.com/users/")
.then((response) => {
setList(response.data); //Change here
})
.catch((err) => console.log(err));
}, []);
...
Related
I have an app that allows user take and delete notes and I'm trying to implement the edit function. The problem is that I don't want to take the user to a different page, I was the note they click on to change into a form or some kind of editable space (prepopulated with the current content of the note) that they can then save so that it renders again but with the updated values (Google Keep style).
The things that I'm struggling with is how to change just one note since the notes are mapped to components through an array - I've tried using the filter() method and playing with ternary operators, but I either copy the entire array or nothing happens. I came across this question: Google Keep edit functionality, but I'm struggling to understand what's happening and how to adapt it in my code.
So the main question is: how can I change one component without disrupting the other elements in the array or their positions on the page?
Here's my Note component so far:
import React, { useState, useEffect} from "react";
import axios from "axios";
function Note(props) {
const [noteToEdit, setNoteToEdit] = useState({
title: "",
content: "",
category: ''
})
const [isEditNote, setEditNote] = useState(false)
const [idToEdit, setIdToEdit] = useState('')
function deleteNote(id) {
axios.delete(`http://localhost:5000/notes/${id}`)
.then(() => { console.log("Note successfully deleted")
props.setFetch(true)
});
}
function editNote(id, title, content, category){
setEditNote(true)
setNoteToEdit(prevNote => {
return {
title : title,
content : content,
category : category
};
});
console.log("Current note to edit after useState:")
setIdToEdit(id)
console.log(noteToEdit)
}
return (
<div>
{isEditNote && <h1>want to edit: {idToEdit}</h1>}
{!isEditNote &&
<div>
{props.notes.map((noteItem) => {
return (
<div className="note">
<h1>{noteItem.title}</h1>
<p>{noteItem.content}</p>
<button onClick={() => {editNote(noteItem._id, noteItem.title, noteItem.category, noteItem.content)}}>
Edit
</button>
<button onClick={() => {deleteNote(noteItem._id)}}>
Delete
</button>
<p>{noteItem.category}</p>
</div>
);
})}
</div>
}
</div>
)
}
export default Note
and my CreateArea component:
import React, { useState, useEffect } from "react";
import Header from "./Header";
import Footer from "./Footer";
import ListCategories from "./ListCategories";
import CreateCategory from "./CreateCategory";
import Note from "./Note";
import axios from "axios"
import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
function CreateArea() {
const [isExpanded, setExpanded] = useState(false);
const [categories, setCategories] = useState([])
const [notes, setNotes] = useState([])
const [fetchB, setFetch] = useState(true)
const [fetchCategories, setFetchCategories] = useState(true)
const [noteToEdit, setNoteToEdit] = useState({
title: "",
content: "",
category: ''
})
const [ieditNote, setEditNote] = useState(false)
const [note, setNote] = useState({
title: "",
content: "",
category: ''
});
useEffect(() => {
if(fetchCategories){
fetch('http://localhost:5000/categories')
.then(res => res.json())
.then(json => {
setCategories(json)
setFetchCategories(false)
})
}
}, [fetchCategories])
useEffect(() => {
if(fetchB) {
fetch('http://localhost:5000/notes')
.then(res => res.json())
.then(json => {
console.log(json)
setNotes(json)
setFetch(false)
})
}
}, [fetchB])
function handleChange(event) {
const { name, value } = event.target;
setNote(prevNote => {
return {
...prevNote,
[name]: value
};
});
}
function submitNote(e){
e.preventDefault();
axios.post("http://localhost:5000/notes/add-note", note)
.then((res) => {
setNote({
category: '',
title: "",
content: ""
})
setFetch(true)
console.log("Note added successfully");
console.log(note)
})
.catch((err) => {
console.log("Error couldn't create Note");
console.log(err.message);
});
}
function expand() {
setExpanded(true);
}
function filterNotes(category){
fetch('http://localhost:5000/notes')
.then(res => res.json())
.then(json => {
const filtered = json.filter((noteItem) => (noteItem.category === category));
setNotes(filtered);
})
}
function editNote(id, title, content, category){
setNoteToEdit(prevNote => {
return { ...prevNote };
});
console.log("Current note to edit after useState:")
console.log(noteToEdit)
}
return (
<div>
<Header/>
<div className="categories">
<CreateCategory setFetchCategories={setFetchCategories}/>
<button className="all-button" onClick={()=>{setFetch(true)}}>All</button>
<ListCategories categories={categories} notes={notes} filterNotes={filterNotes} setFetch={setFetch}/>
</div>
<div className="notes-container">
<form className="create-note">
{isExpanded && (
<input
name="title"
onChange={handleChange}
value={note.title}
placeholder="Title"
/>
)}
<textarea
name="content"
onClick={expand}
onChange={handleChange}
value={note.content}
placeholder="Take a note..."
rows={isExpanded ? 3 : 1}
/>
<select
name="category"
onChange={handleChange}
value={note.category}>
{
categories.map(function(cat) {
return <option
key={cat.category} value={cat.value} > {cat.category} </option>;
})
}
</select>
<button onClick={submitNote}>Add</button>
</form>
<div className="notes-group">
<Note notes={notes} setFetch={setFetch} editNote={editNote} setEditNote={setEditNote}/>
</div>
</div>
<Footer/>
</div>
);
}
export default CreateArea;
Would appreciate any guidance on this, thanks!
I am getting the response and storing that into the array but I am unable to destructure the data from that array,How do i log title of every product inside the div ?
import React, { useEffect, useState } from "react";
import axios from "axios";
const ProductsAPI = () => {
const [item, setItem] = useState([]);
useEffect(() => {
axios
.get("https://fakestoreapi.com/products")
.then((res) => setItem(res.data));
}, []);
return <div></div>;
};
export default ProductsAPI;
You can map over the state value inside your render
<div>
<ul>
{item.map((item,index) =><li key={index}>
{item.title}
</li>) }
</ul>
</div>
You need to loop through your res.data by mapping inside of your render().
useEffect(() => {
axios.get(`https://fakestoreapi.com/products`)
.then(res => {
const yourSavedData = res.data;
this.setState({yourSavedData });
})
}
render() {
return (
<div>
{
this.state.yourSavedData
.map(someName => // <-- Your Callback
<div data-something={yourSavedData.whatever_key}>{yourSavedData.another_key}</div><br />
)
}
</div>
)
}
I need help (Api calls in React Js Hooks) Why is this nort working?
I need to call the values from that API
import React, { useEffect, useState } from 'react';
function Customers() {
const [customers, setCustomers] = useState(null);
useEffect(() => {
fetch('https://reactstarter-app.herokuapp.com/api/customers') **API CALLS**
.then(res => res.json())
.then(customers => setCustomers(customers))
}, [])
return (
<div>
<h2>Customers</h2>
<ul>
{customers.map((customer) => {
return <li key={customer.id}>{customer.firstName} {customer.lastName}</li>
})}
</ul>
</div>
);
}
export default Customers;
Maybe it isn't a solution, but I cannot paste code to comment, so I have to post an answer:
function Customers() {
// this is where you declare the "customers" const
const [customers, setCustomers] = useState(null);
useEffect(() => {
fetch('https://reactstarter-app.herokuapp.com/api/customers') **API CALLS**
.then(res => res.json())
// this is where you should change the "customers" to "data"
// because of the variable name conflict
.then(data => setCustomers(data))
}, [])
It looks like you're trying to map through a null state and probably getting an error, use conditional rendering to avoid the error and render the customers after the api request:
import React, { useEffect, useState } from 'react';
function Customers() {
const [customers, setCustomers] = useState(null);
useEffect(() => {
fetch('https://reactstarter-app.herokuapp.com/api/customers') **API CALLS**
.then(res => res.json())
.then(customers => setCustomers(customers))
}, [])
return (
<div>
<h2>Customers</h2>
{!customers ? <h2>Loading customers...</h2> :
<ul>
{customers.map((customer) => {
return <li key={customer.id}>{customer.firstName} {customer.lastName}</li>
})}
</ul>}
</div>
);
}
export default Customers;
My API returns complex json like these.
[
{id: 1, pub_date: "2021-01-06T20:24:57.547721Z"},
{id: 2, pub_date: "2021-01-06T20:24:57.547721Z"},
{id: 3, pub_date: "2021-01-06T20:24:57.547721Z"}
]
So my trial is like this
const [result, setResult] = useState({});
const [result, setResult] = useState(null);
const [result, setResult] = useState([]);
useEffect(() => {
axios.get('http://localhost:8000/api/results/')
.then(res=>{
console.log(res.data); // correctly received
setResult(res.data); // error
console.log(result); // nothing appears
})
.catch(err=>{console.log(err);});
}, []);
However for any try, it shows the error like
Error: Objects are not valid as a React child (found: object with keys {id, pub_date}). If you meant to render a collection of children, use an array instead.
I have some trial and errors.
There is still difficult behaiver to understand.
const [cnt,setCnt] = useState(0);
useEffect(() => {
axios.get('http://localhost:8000/api/results/')
.then((res)=> {
setCnt(2);
console.log(cnt);//shows 0
})
.catch(err=>{console.log(err);});
}, []);
why setCnt is not workd?? I am more confused......
This error comes from your JSX render, where you're certainly trying to display directly your datas from API
useEffect(...)
...
return (
<ul>
{
result.map(r => (
<li key={r.id}>{r.id} - {r.pub_date}</li>
))
}
</ul>
)
If you are calling setResult(res.data), then your result state should be of type [].
import React, { useEffect, useState } from "react";
const fetchData = () =>
Promise.resolve({
data: [
{ id: 1, pub_date: "2021-01-06T20:24:57.547721Z" },
{ id: 2, pub_date: "2021-01-06T20:24:57.547721Z" },
{ id: 3, pub_date: "2021-01-06T20:24:57.547721Z" }
]
});
const ListItem = ({ id, pub_date }) => (
<li>
{id} — {pub_date}
</li>
);
const ListItems = ({ items }) => (
<ul>
{items.map((props) => (
<ListItem key={props.id} {...props} />
))}
</ul>
);
const App = () => {
const [result, setResult] = useState([]);
useEffect(() => {
fetchData().then((res) => {
setResult(res.data);
});
}, []);
return (
<div className="App">
<ListItems items={result} />
</div>
);
};
export default App;
I am building a COVID tracker Website in which I am getting data from an API.
But the data is coming in nested Object pattern through JSON.
"totalCandidates":"46",
"phases":[
{
"phase":"Phase 3",
"candidates":"5"
},
{
"phase":"Phase 2/3",
"candidates":"2"
},
{
"phase":"Phase 2b",
"candidates":"1"
}
],
To get the totalCandidates value from JSON I am just using {vaccines.totalCandidates} this is working fine.
But if I am trying to get phases which is an Array object I am getting issues. Please see the below code:
import {
MenuItem,
FormControl,
Select,
Card,
CardContent,
} from "#material-ui/core";
import "./App.css";
import StatsComp from "./StatsComp";
import Map from "./Map";
import VaccineTable from "./VaccineTable";
//import { useHistory } from "react-router-dom";
function App() {
const [countries, initCountries] = useState([]);
//To Capture the selected value in dropdown
const [country, initCountry] = useState("Universe");
const [countryInfo, initCountryInfo] = useState([]);
const [vaccineInfo, initVaccineInfo] = useState([]);
//const history = useHistory();
useEffect(() => {
fetch("https://disease.sh/v3/covid-19/all")
.then((response) => response.json())
.then((data) => {
initCountryInfo(data);
});
}, []);
//hook - use async always for api calls
useEffect(() => {
const getCntryData = async () => {
fetch("https://disease.sh/v3/covid-19/countries")
.then((response) => response.json())
.then((data) => {
const countries = data.map((country) => ({
name: country.country,
value: country.countryInfo.iso2,
flag: <img src={country.countryInfo.flag} alt="countryFlag" />,
}));
initCountries(countries);
});
};
getCntryData();
}, []);
useEffect(() => {
const getVaccineData = async () => {
fetch("https://disease.sh/v3/covid-19/vaccine")
.then((response) => response.json())
.then((data) => {
initVaccineInfo(data);
});
};
getVaccineData();
}, []);
//Listener
const listenCountrySelect = async (event) => {
const countryValue = event.target.value;
initCountry(countryValue);
const url =
countryValue === "Universe"
? "https://disease.sh/v3/covid-19/all"
: `https://disease.sh/v3/covid-19/countries/${countryValue}`;
await fetch(url)
.then((response) => response.json())
.then((data) => {
initCountry(countryValue);
initCountryInfo(data);
});
};
console.log("URL :::: ", countryInfo);
console.log("Vcccinee :::", vaccineInfo);
return (
<div className="app">
<div className="HC__left">
<div className="HC__Header">
{/*Title of the Website*/}
<h1>Honest Covid</h1>
</div>
{/* Countries Dropdown for viewing information */}
<FormControl className="HC__countries__dropdown">
<Select
variant="outlined"
value={country}
onChange={listenCountrySelect}
className="HC__select"
>
{/*This will give all countries aggregate value*/}
<MenuItem value="Universe" className="HC__menuitem">
Universe
</MenuItem>
{/* Here we map through all countries and display a menuitem individually*/}
{countries.map((country) => (
<MenuItem value={country.value} className="HC__menuitem">
{" "}
{country.flag} {country.name}{" "}
</MenuItem>
))}
</Select>
</FormControl>
<div className="HC__statistics">
<StatsComp
title="Recovered"
cases={countryInfo.todayRecovered}
total={countryInfo.recovered}
/>
<StatsComp
title="Cases"
cases={countryInfo.todayCases}
total={countryInfo.cases}
/>
<StatsComp
title="Deaths"
cases={countryInfo.todayDeaths}
total={countryInfo.deaths}
/>
</div>
<Map />
</div>
{/* Here comes vaccine status and video*/}
<Card className="HC__right">
<CardContent>
<h3> Vaccine Status</h3>
<VaccineTable vaccines={vaccineInfo} />
<h3> Good Practices during COVID</h3>
</CardContent>
</Card>
</div>
);
}
export default App;
Above is my App.js in which I am fetching data and setting it in vaccineInfo state.
And then passing that object as Prop to another component VaccineTable.js.
import React from "react";
import "./VaccineTable.css";
function VaccineTable({vaccines}) {
return (
<div className="HC__vaccinetable">
{vaccines.map(({ phases }) => (
<tr>
<td>{phases}</td>
<td>
<strong>{phases}</strong>
</td>
</tr>
))}
</div>
);
}
export default VaccineTable;
But for this, I am getting an error :
" TypeError: vaccines.map is not a function "
What is the issue?
Thanks.
data is an object. you need to use data.phases to extract array and save in initVaccineInfo
useEffect(() => {
const getVaccineData = async () => {
fetch("https://disease.sh/v3/covid-19/vaccine")
.then((response) => response.json())
.then((data) => {
initVaccineInfo(data.phases);
});
};
getVaccineData();
}, []);
The problem is that https://disease.sh/v3/covid-19/vaccine returns an object. You need to access the data property, which is an array. i.e.
fetch("https://disease.sh/v3/covid-19/vaccine")
.then((response) => response.json())
.then(({data}) => { <---- destructure the 'data' property here, and use that
initVaccineInfo(data);
});
are you getting an array here console.log("Vcccinee :::", vaccineInfo); ? if yes then put a check inside VaccineTable.js before vaccines.map like vaccine.length && vaccines.map, since passed props are asynchronous.
If no, then put a console inside below useEffect and check what you are getting into data.
const getVaccineData = async () => {
fetch("https://disease.sh/v3/covid-19/vaccine")
.then((response) => response.json())
.then((data) => {
initVaccineInfo(data.phases);
});
};
getVaccineData();