Access Data in nested JSON API Object in react - reactjs

I have this Data in a JSON-Api but my react app does not want to let get the values from the nested Objects.
[
{
"_id": "61715a5351b12fadc073940a",
"name": "Betty",
"owner": {
"_id": "614323ed282bfd3e68bbaf4f",
"name": "Kirk Douglas",
"email": "kirk#example.com",
"__v": 0
},
"addressText": "Some cool street",
"addressNumber": "91",
"zipCode": "34567",
"city": "Washington"
"__v": 0
},
{
"_id": "61715cf92bb6de6eca7e10f8",
"name": "Jeremy",
"owner": {
"_id": "614323ed282bfd3e68bbaf4f",
"name": "Paul Bettany",
"email": "paul#example.com",
"__v": 0
},
"addressText": "Another street",
"addressNumber": "233",
"zipCode": "09234",
"city": "New York",
"__v": 0
}
]
My code for the react component looks like this.
const BarrelDetails = () => {
const { id } = useParams();
const { data: barrel, error, isPending } = useFetch('localhost:8000/api/barrels/' + id);
const history = useHistory();
// const handleClick = () => {
// fetch('http://localhost:8000/api/barrels' + barrel.id, {
// method: 'DELETE'
// }).then(() => {
// history.push('/');
// })
// }
return (
<div className="content">
<div className="barrel-details">
{ isPending && <div>Loading...</div> }
{ error && <div>{ error }</div> }
{ barrel && (
<div>
<h2>{ barrel.title }</h2>
<p>Ansprechpartner { barrel.owner }</p>
<p>Standort: { barrel.city }</p>
<Bookbarrel />
</div>
)}
</div>
</div>
);
}
export default BarrelDetails;
It displays the barrel.owner id, but nothing else.
I also have the problem that i cannot access the data from my main List anymore...
I was using a useEffect hook, and passing data down to a list, but this did not work anymore.
useEffect(() => {
fetch('https://devsauna.de/api/barrels/')
// fetch('http://localhost:8000/api/barrels')
.then(res => {
if (!res.ok) {
throw Error('Could not fetch the data for that resource');
}
return res.json();
})
.then(data => {
setBarrels(data);
setError(null);
setIsPending(false);
})
.catch(err => {
setIsPending(false);
setError(err.message);
});
}, []);
I get the Error Error: Objects are not valid as a React child (found: object with keys {_id, name, email, __v}). If you meant to render a collection of children, use an array instead.

Your data is an array of objects, so you need to map it.
barrel && barrel.map(item => {
return <div>
<h2>{ item.title }</h2>
<p>Ansprechpartner { item.owner }</p>
<p>Standort: { item.city }</p>
<Bookbarrel />
</div>
})

Related

Uncaught TypeError: Cannot read property 'photo' of undefined

{
"data": {
"photo": {
"id": "3",
"categoryId": 1,
"src": "https://images.unsplash.com/photo-1513360371669-4adf3dd7dff8?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60",
"likes": 7,
"userId": "1",
"liked": false
}
}
}
The query is working correctly.
const query = gql`
query getSinglePhoto($id: ID!) {
photo(id: $id) {
id
categoryId
src
likes
userId
liked
}
}
`;
I would like get the object named photo
But I am not sure cuz the console show undefined.
Uncaught TypeError: Cannot read property 'photo' of undefined
"photo": {
"id": "3",
"categoryId": 1,
"src": "https://images.unsplash.com/photo-1513360371669-4adf3dd7dff8?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60",
"likes": 7,
"userId": "1",
"liked": false
}
Here is the problem. In the part const {photo = {}} = data. I found to get the object named photo, but I was unlucky.
export const PhotoCardWithQuery = ({ id }) => (
<Query query={query} variables={{ id }}>
{({ loading, error, data }) => {
const { photo = {} } = data;
console.log(photo);
return <PhotoCard />;
}}
</Query>
);
The first time your component renders, loading will be true and data will be undefined, as the data has not loaded.
You need to add a check to make sure that loading is false and data is populated before trying to extract photo from it.
<Query query={query} variables={{ id }}>
{({ loading, error, data }) => {
if (loading || !data) return <p>Loading...</p>;
const { photo = {} } = data;
console.log(photo);
return <PhotoCard />;
}}

how to fetch api data in ReactJS?

i was trying to fetch api data in reactjs but data are not loading perfectly as expected, instead of i am getting an empty page as result. here below is my source code;
i am probably new to reactjs, it would be great if anybody could help me where i am doing thing wrong. thank you so much in advance.
endpoint_url : http://localhost:8000/api/blog_list
api-data:
[
{
"id": 1,
"url": "http://localhost:8000/api/blog_detail/brown",
"title": "brown",
"slug": "brown",
"image": "http://localhost:8000/media/blog/image_2.jpg",
"description": "",
"created_on": "2020-05-08T15:20:53Z",
"status": true,
"category": [
1
]
},
{
"id": 2,
"url": "http://localhost:8000/api/blog_detail/black",
"title": "black",
"slug": "black",
"image": "http://localhost:8000/media/blog/loc.png",
"description": "",
"created_on": "2020-05-08T17:14:31Z",
"status": true,
"category": [
2
]
}
]
./src/Base.js
export default class App extends Component{
state = {
bloglist:[]
};
componentDidMount(){
this.fetchData()
}
fetchData = async () => {
try{
const response = await fetch("http://localhost:8000/api/blog_list");
const jsonResponse = await response.json()
this.setState({bloglist:jsonResponse})
}
catch(error){
console.log(error)
}
}
render(){
const {bloglist} = this.state
if(!bloglist){
return (
<div>
<h1>loading...</h1>
</div>
)
}
return (
{
bloglist.map(bloglist => (
<h3 class="mb-2">
{bloglist.title}
</h3>
<p class="mb-4">{bloglist.description}</p>
))
}
)
}
}
Wrap it in a div. React expects a single element.
<h3 class="mb-2">
{bloglist.title}
</h3>
<p class="mb-4">{bloglist.description}</p>
Check this code.
export default class App extends Component {
state = {
bloglist: [],
};
componentDidMount() {
this.fetchData();
}
fetchData = async () => {
try {
const response = await fetch('http://localhost:8000/api/blog_list');
const jsonResponse = await response.json();
this.setState({ bloglist: jsonResponse });
} catch (error) {
console.log(error);
}
};
render() {
const { bloglist } = this.state;
var page = (
<div>
<h1>loading...</h1>
</div>
);
if (bloglist.length > 0)
page = bloglist.map((bloglistEntry) => {
return (
<React.Fragment key={bloglistEntry.id}>
<h3 className='mb-2'>
<a href='single.html'>{bloglistEntry.title}</a>
</h3>
<p className='mb-4'>{bloglistEntry.description}</p>
</React.Fragment>
);
});
return page;
}
}

How to pass argument to function in reactjs?

How can I send sport_id form getSport to getEvents to show each sports events?
Can I put getSport function to other component, call and use it in this component?
events json:
[
{
"id": "912653",
"time": "1536471082",
"time_status": "1",
"league": {
"id": "900",
"name": "Hong Kong 2nd Division",
"cc": "hk"
},
"home": {
"id": "13767",
"name": "Yau Tsim Mong",
"image_id": "193606",
"cc": "hk"
},
"away": {
"id": "63770",
"name": "Tuen Mun SA",
"image_id": "56045",
"cc": "hk"
},
"timer": {
"tm": 74,
"ts": 25,
"tt": "1",
"ta": 0
},
"scores": {}
}
]
sports json:
[
{
"id": 8,
"name": "Rugby Union",
"is_active": null,
"slug": "rugby-union"
}
]
Here is my code:
import React, { Component } from "react";
import axios from "axios";
import moment from "moment";
export default class Feutred extends Component {
state = {
sports: [],
events: [],
isLoading: true,
errors: null
};
getSports() {
axios
.get("/api/v1/sports.json")
.then(response =>
response.data.map(sport => ({
id: sport.id,
name: sport.name,
slug: sport.slug
}))
)
.then(sports => {
this.setState({
sports,
isLoading: false
});
})
.catch(error => this.setState({ error, isLoading: false }));
}
getEvents() {
axios
.get("/api/v1/events?sport_id=${sport_id}")
.then(response =>
response.data.map(event => ({
id: event.id,
time: event.time,
league: event.league,
time_status: event.time_status,
homeTeam: event.home,
awayTeam: event.away
}))
)
.then(events => {
this.setState({
events,
isLoading: false
});
})
.catch(error => this.setState({ error, isLoading: false }));
}
componentDidMount() {
this.getSports();
(this.interval = setInterval(
() => this.getEvents({ time: Date.now() }),
12000
));
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
const { sports, isLoading } = this.state;
return (
<React.Fragment>
{!isLoading ? (
sports.map(sport => {
const { id, name } = sport;
return (
<div key={sport.id}>
<div className="text">
<p className="meta">
<span className="matchinfo">
<span className="block">time</span>
<span className="block">timestatus</span>
</span>
</p>
<h3>
home-team vs aya tream
</h3>
<p className="league">
<a className="watchlive" href="">
<span className="icon" />
<span>Watch live</span>
</a>
<span>{sport.name} - league cc - league name</span>
</p>
</div>
</div>
);
})
) : (
<p>Loading...</p>
)}
</React.Fragment>
);
}
}
Just destructure it - load sports in one component then render some <EventsLoadingComponent /> passing sport id as prop ...
HINT: Use if(isLoading) return <p>Loading...</p> in render before 'main return' - no need to use ternary operator in return JSX.
UPDATE:
render() {
const { sports, isLoading } = this.state;
if(isLoading) return <p>Loading...</p>
return (
<React.Fragment>
{sports.map(sport => <EventsLoadingComponent sport={sport}/>}
</React.Fragment>
);
}
Move getEvents into <EventsLoadingComponent/> - you'll be fething for events related to this.props.sport.id and render them. This way each of them can be separately updated.
Remember to use key in the topmost html element.
UPDATE #2:
can you please give your code comparison with my code ?
Your code - linear, procedural, 'flat template-driven', forcing async to be sync, all-in-one-component ... while html is a (flatten view of) tree structure.
React thinking (generally, not my code only) - more OO, building tree of objects closer related to data and view structure, giving them own responsibility (data handling, view). Easier to read, expand (destructure further details to components - even one-liners), suitable to decorating, easy to manage ... and reuse.
Often object in structure renders only passed children (or nothing) only providing functionality. Available level of complexity is greater, communication within this structure is far easier (and less dependent) than (it could be done) in html.
Something like this:
getEvents({ id }) {
axios
.get(`/api/v1/events?sport_id=${id}`)
...
}
componentDidMount() {
this.getSports()
.then(() => {
return Promise
.all(this.state.sports.map(this.getEvents))
});
...
}
Note:
You need to refine the way you save the data because you need to know which events are for which sport.

Displaying Data from One Component in Another

I'm learning react at the moment and I'm trying to have two components interact with each other. The hierarchy is as follows:
App
--SearchForm
--Results
There's a data object that will be filtered through a string I enter in the SearchForm component. The filtered result should be displayed in the Results component.
My logic was to have all the functions needed in the App component, and pass the data to the individual components.
I want to be able to display the filtered data in the results component.
Can anyone help me with this please?
Please find the App.js file's code below, as well as a sample of the object I'm using.
App.js
import React, { Component } from "react";
import styled from "styled-components";
import Header from "./Header";
import SearchForm from "./SearchForm";
import Results from "./Results";
import Map from "./Map";
const Outer = styled.div`
text-align:center;
`;
class App extends Component {
constructor(props) {
super(props);
this.state = {
query: "",
data: [],
refinedData: [],
};
// this.handleSearchChange = this.handleSearchChange.bind(this);
}
handleSearchChange = (event) => {
this.setState({
query: event.target.value,
});
}
getData = async () => {
const response = await fetch("http://localhost:4200/bookings");
const json = await response.json();
this.setState({
data: json,
})
console.log(this.state.data);
}
filterData = () => {
const filtered = this.state.data.filter(element => {
return element.toLowerCase().includes(this.state.query.toLowerCase());
});
this.setState({
refinedData: filtered,
});
console.log(this.state.refinedData);
}
componentDidMount() {
this.getData();
}
render() {
return (
<Outer>
<Header/>
<SearchForm triggeredUpdate={this.handleSearchChange}/>
<Results searchQuery={this.state.filterData}/>
<Map/>
</Outer>
);
}
}
export default App;
Object
[
{
"id": 50000,
"car": {
"id": 1000,
"licence_plate": "SKK5050Q"
},
"book_start": 1543271643,
"book_end": 1543340723,
"pickup": {
"id": 87,
"code": "WDL",
"lat": 1.434,
"lng": 103.78
},
"dropoff": {
"id": 85,
"code": "TPY",
"lat": 1.33,
"lng": 103.851
},
"user": {
"id": 51498,
"name": "Count Dooku"
}
}
]
This is a simple logic actually in React. You want to show filtered results in your Results component, then you pass the filtered state to it. You can trigger the search with a button, then maybe the suitable place for this can be Search component. For this, you will pass your filterData method to it as a prop as you think.
I said a few times "it is an array not object" in my comments since the last data you show in your question says Object as bold but it is an array :) So, I got confused but you are doing it right.
You should filter your data with a prop in your object. As you think again, like user.name, car.license_late etc. You need a target here.
Here is a simple working example:
class App extends React.Component {
state = {
query: "",
data: [
{
"id": 50000,
"car": {
"id": 1000,
"licence_plate": "SKK5050Q"
},
"book_start": 1543271643,
"book_end": 1543340723,
"pickup": {
"id": 87,
"code": "WDL",
"lat": 1.434,
"lng": 103.78
},
"dropoff": {
"id": 85,
"code": "TPY",
"lat": 1.33,
"lng": 103.851
},
"user": {
"id": 51498,
"name": "Count Dooku"
}
}
],
refinedData: [],
};
handleSearchChange = event => this.setState({
query: event.target.value,
});
filterData = () => {
const { data, query } = this.state;
const filtered = !query ? [] : data.filter(element =>
element.car.licence_plate.toLowerCase().includes(this.state.query.toLowerCase())
);
this.setState({
refinedData: filtered,
});
}
render() {
return (
<div>
<SearchForm filterData={this.filterData} triggeredUpdate={this.handleSearchChange} />
<Results refinedData={this.state.refinedData} />
</div>
);
}
}
const Results = props => (
<div>
{
props.refinedData.map( el =>
<div key={el.id}>
<p>ID: {el.id}</p>
<p>User name: {el.user.name}</p>
</div>
)
}
</div>
)
const SearchForm = props => (
<div>
<input onChange={props.triggeredUpdate} />
<br />
<button onClick={props.filterData}>Search</button>
</div>
)
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Update after discussion on chat
You can do the search without a button while you typing. We don't have filterData method anymore since we moved the filter logic into handleSearchChange method. Also, we don't need any query state right now.
filterData array created with a ternary operator. If there is no search value we are returning an empty array since we don't want to list all of our data if there is not any search. By the way, I've updated my previous solution according to that, too. It was returning all the data if we hit the Search button with an empty input.
class App extends React.Component {
state = {
data: [
{
"id": 50000,
"car": {
"id": 1000,
"licence_plate": "SKK5050Q"
},
"book_start": 1543271643,
"book_end": 1543340723,
"pickup": {
"id": 87,
"code": "WDL",
"lat": 1.434,
"lng": 103.78
},
"dropoff": {
"id": 85,
"code": "TPY",
"lat": 1.33,
"lng": 103.851
},
"user": {
"id": 51498,
"name": "Count Dooku"
}
}
],
refinedData: [],
};
handleSearchChange = event => {
const { value: query } = event.target;
this.setState(prevState => {
const filteredData = !query ? [] : prevState.data.filter(element =>
element.car.licence_plate.toLowerCase().includes(query.toLowerCase())
);
return {
refinedData: filteredData
};
});
}
render() {
return (
<div>
<SearchForm triggeredUpdate={this.handleSearchChange} />
<Results refinedData={this.state.refinedData} />
</div>
);
}
}
const Results = props => (
<div>
{
props.refinedData.map(el =>
<div key={el.id}>
<p>ID: {el.id}</p>
<p>User name: {el.user.name}</p>
</div>
)
}
</div>
)
const SearchForm = props => (
<div>
<input onChange={props.triggeredUpdate} />
</div>
)
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

TypeError: this.state.data.map is not a function despite setting data: [ ]

I'm trying to store the data in ItemLists component for which I need to first map over. But getting TypeError: this.state.data.map is not a function
constructor(props) {
super(props);
this.state = {
data: []
}
}
componentDidMount() {
fetch('items.json')
.then(function(response) {
if (response.status >= 400) {
throw new Error("Bad response");
}
return response.json();
})
.then(data => {
console.log(data)
this.setState({data: data});
});
}
render() {
return (
<div className="App">
{this.state.data.map(function(object){
return (
<ItemLists key={object.id} data={object}/>
)
})}
</div>
);
}
}
if I change the code to:
render() {
return (
<div className="App">
return (
<ItemLists data={this.state.data}/>
)
</div>
)
}
error I get is: Objects are not valid as a React child (found: object with keys {artist, title, level, released, rating}). If you meant to render a collection of children, use an array instead pointing towards this.setState({data: data});
json data:
{
"data": [
{
"name": "cnt",
"level": "12"
},
{
"name": "stewart",
"level": "6"
},
{
"name": "nic",
"level": "7"
}
]
}
After the fetch request is completed you set the data array in your state to the parsed JSON object. Since regular objects don't have a map method like arrays have, you get the error.
You could set the data in state to be the data array in the parsed JSON object instead.
componentDidMount() {
fetch('items.json')
.then(response => {
if (response.status >= 400) {
throw new Error("Bad response");
}
return response.json();
})
.then(response => {
this.setState({ data: response.data });
});
}
You should use Arrow function when you use the function in React Component
let JSONDATA =
{
data: [
{
name: "cnt",
level: "12"
},
{
name: "stewart",
level: "6"
},
{
name: "nic",
level: "7"
}
]
}
let { data } = JSONDATA;
data.map((obj)=>{
console.log(obj)
})

Resources