Is it possible to make a state array from this JSON object? - reactjs

A newbie in React here. I'm using axios to retrieve this object requested to my Django server rest framework Api:
{
"count": 3,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"url": "http://localhost:8000/blog/api/categories/1/",
"title": "Django",
"slug": "django"
},
{
"id": 2,
"url": "http://localhost:8000/blog/api/categories/2/",
"title": "Git",
"slug": "git"
},
{
"id": 3,
"url": "http://localhost:8000/blog/api/categories/3/",
"title": "Introduction to docker",
"slug": "introduction-to-docker"
}
]
}
So far I've been able to store those variables in separate arrays. This is the implementation of my FetchDemo.js:
import React, {Component} from 'react';
import axios from 'axios';
{/* https://daveceddia.com/ajax-requests-in-react/ */}
class FetchDemo extends Component {
state = {
urls: []
}
state = {
titles: []
}
state = {
slugs: []
}
componentDidMount() {
axios.get(`${this.props.url}${this.props.path}`).then(res => {
const urls = res.data.results.map(num => num.url);
const titles = res.data.results.map(num => num.title);
const slugs = res.data.results.map(num => num.slug);
this.setState( {urls} );
this.setState( {titles} );
this.setState( {slugs} );
});
}
render() {
return (
<div>
<h1>{`${this.props.url}${this.props.path}`}</h1>
<ul>
/// How to generate the JSX objects? ///
</ul>
</div>
);
}
}
export default FetchDemo;
Is it possible to build in the axios request a state array from this JSON object like the next one instead? How do I generate JSX with this new state categories?
this.setState({
categories: [
{url: "http://localhost:8000/blog/api/categories/1/", title: "Django", slug: "django"},
{url: "http://localhost:8000/blog/api/categories/2/", title: "Git", slug: "git"},
{url: "http://localhost:8000/blog/api/categories/3/", title: "Introduction to Docker", slug: "introduction-to-docker"},
]
}
)
I wish I knew more React and JavaScript. Any help is appreciated, thanks in advance.

There are a few things to note here.
state is a single object with multiple keys.
setState() is a single async transaction. When you are updating your state, you should update it in one go.
You can do things in a simpler way if I understand your question right. You don't have to store the values in multiple keys. It can be stored in a single key categories.
import React, { Component } from 'react';
import axios from 'axios';
class FetchDemo extends Component {
constructor(props) {
super(props);
this.state = {
categories = []
}
}
componentDidMount() {
axios.get(`${this.props.url}${this.props.path}`).then((res) => {
const urls = res.data.results.map((num) => num.url);
const titles = res.data.results.map((num) => num.title);
const slugs = res.data.results.map((num) => num.slug);
this.setState({
categories: res.data.results
});
});
}
render() {
const { categories } = this.state;
return (
<div>
<h1>{`${this.props.url}${this.props.path}`}</h1>
<ul>
{categories.length ?
categories.map(cat => (
<li key={cat.id}>
<div>{cat.title}</div>
</li>
))
:
(<li>categories not loaded yet</li>)}
</ul>
</div>
);
}
}
export default FetchDemo;

Related

Nested JSON Objects in React JS

I have a API Response which has two Arrays - Incident and Services.
I have two Buttons "Incident" & "Service Req.", Once we click on each button it should get a list of Incident or Service Req with toggle or disabled MUI button functionality.
I'm trying to create two buttons - Incident & Service Req
Once we click on Incident/Service Req button we need - Inc/SR list on same Container DIV
Im trying to add MUI Disabled button or Toggle Functionality but my basic Functionality is not working.
{
"totalIncident": 20,
"totalRequests": 20,
"incidents": [
{
"createdOn": "2022-10-20 07:14:17",
"description": "This is Incident .",
"number": "REQ9XXXX"
}
],
"requests": [
{
"createdOn": "2022-10-20 07:14:17",
"description": "This is Request.",
"number": "REQ9XXXX"
}
}
]
}
import React from "react";
import "./App.css";
class API_APP extends React.Component {
constructor() {
super();
this.state = { data: [] };
}
componentDidMount() {
fetch(
"API_LINK"
)
.then((res) => {
return res.json();
})
.then((resp) => {
this.setState({ data: resp });
console.log(resp);
});
}
onClickIncidents = () => {
console.log(this.state.data.incidents);
return (
<div>
<h1>Incidents</h1>
{this.state.data.incidents?.map((item) => {
return (
<div key={item.sysId} className="part">
<li> {item.number}</li>
</div>
);
})}
</div>
);
};
render() {
console.log(this.state.data);
const incidents = this.onClickIncidents;
console.log(incidents);
return (
<div className="container">
<h1>Incidents</h1>
<button onClick={this.onClickIncidents}>incidents</button>
<button onClick={this.onClickRequests}>requests</button>
</div>
);
}
}
export default API_APP;

django-react cannot fetch data properly

models.py
class Book(models.Model):
name = models.CharField(max_length=100, unique=True)
author = models.CharField(max_length=64)
passcode = models.CharField(max_length=80)
serializers.py
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__'
views.py
class GetBooks(APIView):
def get(self, request):
books = models.Book.objects.all()
data = serializers.BookSerializer(books, many=True).data
return Response(data, status=status.HTTP_200_OK)
urls.py
urlpatterns = [
path('get-books', views.GetBooks.as_view(), name='get-books')
]
Main.js
import React, { Component } from 'react'
export default class Main extends Component {
constructor(props){
super(props)
this.state = {
books: ''
}
this.getBooks()
}
getBooks() {
fetch('http://127.0.0.1:8000/api/get-books') // go down to see the data
.then((response) => {
const books = response.json()
console.log(books) // go down to see the output <---- !!
this.setState({
books: books
})
})
}
render() {
return (
<div>
// for book in books show book.name, book.author ...
</div>
)
}
}
Books data from (http://localhost/api/get-books)
[
{
"id": 1,
"name": "book1",
"author": "author1",
"passcode": "123"
},
{
"id": 2,
"name": "book2",
"author": "auhthor2",
"passcode": "123"
}
]
console.log(data)
Promise { <state>: "pending" }
​
<state>: "fulfilled"
​
<value>: Array [ {…}, {…} ]
​​
0: Object { id: 1, name: "book1", author: "author1", … }
​​
1: Object { id: 2, name: "book2", author: "author2", … }
​​
length: 2
​​
<prototype>: Array []
I want to iterate through all the books and display it to the user, the fetched data return as an array but I cant access it from this.state.books (i just recently started using react and django so please have mercy...)
I FOUND A SOLUTION
Main.js
import axios from 'axios'
import React, { Component } from 'react'
import Books from './Books'
export default class Main extends Component {
constructor(props){
super(props)
this.state = {
books: []
}
}
componentDidMount() {
axios.get('http://localhost:8000/api/get-books')
.then(res => {
this.setState({
books: res.data
})
})
}
render() {
return (
<div>
<Books books={this.state.books} />
</div>
)
}
}
Books.js
import React from 'react'
const Books = ({books}) => {
return (
<div>
{books.map(book => (
<div>
<h1>{book.name}</h1>
<small>{book.author}</small>
</div>
))}
</div>
)
}
export default Books
I simply used axios, put the data in an array and sent it to Book.js, then used the .map() to iterate through them!

React parsing an array of objects coming from an API

I'm quite new with react, I'm trying to hit an API and I'm getting this response. I need to go over the array and show the elements in a table:
{
"people": [
{
"id": "1",
"name": "philip",
"age": 25,
"timestamp": "2020-10-17T21:59:50.151"
},
{
"id": "2",
"name": "philip2",
"age": 26,
"timestamp": "2020-10-17T21:59:50.152"
},
{
"id": "3",
"name": "philip3",
"age": 27,
"timestamp": "2020-10-17T21:59:50.153"
},
]
}
I'm hitting and getting response from the api correctly but I have some issues trying to parse it.
import React, { Component } from 'react';
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
people: []
}
}
componentDidMount() {
fetch('/local/api/people')
.then(res => res.json())
.then(json => json.people)
.then(people => this.setState({'people': people}))
}
render() {
return (
<div className="App">
{this.state.people}
Here I'd need to go over the array and show all the elements
</div>
);
}
}
export default App;
Error: Objects are not valid as a React child (found: object with keys ....... If you meant to render a collection of children, use an array instead.
I tried a lot of things but nothing worked so far
you have to map over the array inside the return like the code below and I am passing a key that's a react way to identify the element
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity:
and also am checking when the component renders that I will only show the list when the state people array length is true means not 0
import React, { Component } from "react";
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
people: [],
};
}
componentDidMount() {
fetch("/local/api/people")
.then((res) => res.json())
.then((json) => json.people)
.then((people) => this.setState({ people: people }));
}
render() {
return (
<div className="App">
{this.state.people.length && this.state.people.map((element, key) => {
return (
<div key={key}>
<span>{element.id}</span>
<span>{element.name}</span>
<span>{element.age}</span>
<span>{element.timestamp}</span>
</div>
);
})}
</div>
);
}
}
export default App;
You can create a member function that maps over the array and creates and returns jsx for each table row. You can then call this function inside a table body tag.
renderTableData() {
return this.state.people.map((person, index) => {
const { id, name, age } = person //destructuring
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{age}</td>
</tr>
)
})
}
render() {
return (
<div className="App">
<table id='people'>
<tbody>
{this.renderTableData()}
</tbody>
</table>
</div>
)
}

How to pass data to props from state?

I'm learning React and have some troubles with using state and props. There is two files: App.js and component. In App.js i use axios to get JSON data from IP and store in a state. But I cannot pass the data to props through the state.
Here is App.js:
import React from 'react';
import axios from 'axios';
import Shutruk from './Shutruk';
const qwerty = {
"data": [
{
"_id": "5d1cb18e4af03042df6267c5",
"title": "Shutruk",
"description": "Shhhhhhhhhhhhh",
"date": "2019-07-03T13:45:50.850Z",
"__v": 0
},
{
"_id": "5d1cc27b37c9751001f5c12f",
"title": "Shilkhak",
"description": "Shilkhak-Inshushinak",
"date": "2019-07-03T14:58:03.797Z",
"__v": 0
},
{
"_id": "5d1cc45655780f11112a023f",
"title": "Унташ",
"description": "Untash-Napirisha",
"date": "2019-07-03T15:05:58.699Z",
"__v": 0
},
{
"_id": "5d1ef36c503601183b5f856f",
"title": "dgfdgfdhgf",
"description": "bbbbbbbbbbbbbbbbb",
"date": "2019-07-05T06:51:24.873Z",
"__v": 0
},
{
"_id": "5d1ef381503601183b5f8570",
"title": "qewytuytruytru",
"description": "jhfgasjdfgasjdfgjhsdf",
"date": "2019-07-05T06:51:45.761Z",
"__v": 0
}
]
};
class App extends React.Component {
state = {
data: []
}
componentDidMount() {
axios.get('http://localhost:5555/posts')
.then(res => {
const data = res.data;
this.setState({ data });
})
}
render() {
return (
<div>
<Shutruk name={ this.state.data.data[0].title }/>
</div>
)
}
}
export default App;
Here is the component:
import React from 'react';
class Shutruk extends React.Component {
render() {
return (
<div>
<h1>This is is {this.props.name}!</h1>
</div>
)
}
}
export default Shutruk;
I use axios to get data from backend, but when I insert it to props, it does'nt work. I create an array qwerty[] with the same data, and when I replace with:
return (
<div>
<Shutruk name={ qwerty.data[0].title }/>
</div>
)
it works correctly. What is the problem, if there is no difference between 'this.state.data' and 'qwerty'?
I checked with console.log and the result is same!
Thanks to everyone for any help!
This is because, axios and setState are asynchronous and when the component loads in componentDidMount, it takes a while to load data into state and since data.data[0] is still empty, it doesn't work. But when you use a const to declare it, it works as it is already present.
Instead of
<Shutruk name={ this.state.data.data[0].title }/>
Do:
renderShutruk = () => {
if (this.state.data.data !== undefined) {
return <Shutruk name={this.state.data.data[0].title} />
} else {
return null;
}
};
render() {
return (
<div>
{this.renderShutruk()}
</div>
);
}
Your App component is probably crashing when mounting while you're accessing an undefined state because your try to get this.state.data.data[0].title when data state equals []
Try replacing your App component like this to prevent access to an undefined state (I recommend doing this for all your asynchronous operations in the components):
class App extends React.Component {
state = {
data: [],
loading: true,
error: false,
}
componentDidMount() {
axios.get('http://localhost:5555/posts')
.then(res => {
const data = res.data.data; // get the data array instead of object
this.setState({ data, loading: false });
})
.catch(err => { // log request error and prevent access to undefined state
this.setState({ loading: false, error: true });
console.error(err);
}
render() {
if (this.state.loading) {
return(
<div>
<p> Loading... </p>
</div>
)
}
if (this.state.error || !this.state.data[0]) { // if request failed or data is empty don't try to access it either
return(
<div>
<p> An error occurred </p>
</div>
)
}
return (
<div>
<Shutruk name={ this.state.data[0].title }/> // access to data array state
</div>
)
}
}
export default App;
Just change this,
<Shutruk name={ this.state.data.data[0].title }/>
with,
{this.state.data ? <Shutruk name={ this.state.data[0].title }/> : null}
Update
If you are not getting data, then you must use async/await,
async componentDidMount() {
await axios.get('http://localhost:5555/posts')
.then(res => {
//console.log(res) => if `res` is {data:[...]} , then do this,
const data = res.data;
//console.log(res) => if `res` is {data: data: [...]} , then do this,
const data = res.data.data;
this.setState({ data });
})
}

Undefined getting value from fetch request in a JSON file using REACT.JS

I'm coming from a Vue environment I'm a bit confused with this,
I read some other question similar to this but I couldn't make it work,
why I can't echo out the value of a nested object getting from a fetch request?
I console.log after setState and I got the values but in the render is undefined,
import React, { Component } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends Component {
constructor() {
super();
this.state = {
isLoading: true,
articles: {}
};
}
componentDidMount() {
this.setState({ loading: true });
fetch("./articles.json")
.then(response => response.json())
.then(result => {
this.setState({
isLoading: false,
article: result.blog.article
});
console.log(
"componentDidMount__this.state.article=",
this.state.article.link.title
); //this gets the value
})
.catch(error => {
console.error(error);
});
}
render() {
//let articleTitle;
// this gets error ----> console.log(this.state.article.link.title);
// because .link is undefined
// console.log(this.state.article);
// if (this.state.article !== "undefined") {
// console.log("wait what?..");
// if I changed the state in fetch why this stil
// articleTitle = this.state.article.link.title;
// } else {
// articleTitle = "";
// }
// I assign "this.state.article.link.title" to a variable so I can avoid the error,
//
return (
<div className="App">
{/*<h1>{articleTitle}</h1> */}
<h1>{this.state.article.link.title}</h1>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
articles.json
{
"blog": {
"article": {
"id": 1,
"title": " 1 lorem ipsum",
"description": "lorem ipsum",
"image": {
"desktop": "https://via.placeholder.com/900x500?text=desktop",
"tablet": "https://via.placeholder.com/600x400?text=tablet",
"mobile": "https://via.placeholder.com/320x320?text=mobile"
},
"link": {
"title": "lorem link",
"url": "#"
},
"author": {
"avatar": "https://via.placeholder.com/125x125?text=125x125",
"name": "lorem ipsum"
}
}
}
}
https://codesandbox.io/s/wo65w21kl5
You have to put check before using dynamic states in render as it is called on both component mount and update.
This should work fine:
{this.state.isLoading ? '' : this.state.article.link.title}
It appears that this.state.article.link.title is being referenced when this.state.article === undefined.
The solution is to retrieve this.state.article.link.title in a safer manner.
This is typically achieved by leveraging short-circuit evaluation. I've also used destructuring assignment and default parameters in the example below.
Assigning default values to this.state is also recommended, especially when dealing with indeterminate data.
// Default `this.state`.
this.state = {
article: {link: {title: ''}},
articles: {},
isLoading: true,
}
// Safe retrieval of `title`.
const {article = {}} = this.state
const {link = {}} = article.link
const title = link.title || ''
TRY this
import React, { Component } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends Component {
constructor() {
super();
}
state = {
isLoading: true,
articles: {}
};
componentDidMount() {
this.setState({ loading: true });
fetch("./articles.json")
.then(response => response.json())
.then(result => {
this.setState({
isLoading: false,
articles: result.blog.article
});
})
.catch(error => {
console.error(error);
});
}
render() {
let Test = this.state.articles ? (
<div className="App">
<h1>{this.state.articles.title}</h1>
</div>
) : null;
console.log(this.state.articles.title);
return <div>{Test}</div>;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
It seems that render method is invoked three times before fetch method finish, so in render method this.state.articles is empty object. You also wonders why guy from the tutorial does not have this issue, well in tutorial you mentioned object: this.state.character.name was used and in your code this.state.articles.link.title. This is the difference, because it is acceptable to use this.state.character.name (it refers to property from empty object so it will return undefined, whereas your this.state.article.link.title (it tries to access to property on object that does not exist). You can check it in console:
const obj = {};
console.log(obj.property); //undefined
console.log(obj.link.title); // Uncaught TypeError: Cannot read property 'title' of undefined

Resources