javascript/ReactJS: Show results from backend in a list - javascript

I am sending a GET request on a Node API with a MongoDB server. I am getting the response as JSON in an array of object format. I want to show all those results in a list. Right now i am making a function like this
class VendorDashboard extends React.Component {
constructor() {
super();
this.state = {
paginationValue: '86',
title: ""
}
this.handleLogout = this.handleLogout.bind(this);
this.gotoCourse = this.gotoCourse.bind(this);
}
componentDidMount() {
axios.get('/vendor/showcourses') //the api to hit request
.then((response) => {
console.log(response);
let course = [];
course = response.data.map((courseres) => {
this.setState({
title: courseres.title
});
})
});
Right now what is happening is it is showing just one result. I want to show all results on that api. How can i do it?

This segment here is overriding the title per course.
course = response.data.map((courseres) => {
this.setState({
title: courseres.title
});
})
You can keep the state as an array of titles and do;
course = response.data.map((courseres) => {
return courseres.title;
})
this.setState({titles: course});
And then you can repeat on the array of titles in your component.
Like so in the render method;
const { titles } = this.state;
return <div>{titles.map((title, index) => <div key={index}>{title}</div>)}</div>

You need to collect all the server response and set that as an array of data to the state and use this state data to render:
class VendorDashboard extends React.Component {
constructor() {
super();
this.state = {
paginationValue: '86',
course: []
}
this.handleLogout = this.handleLogout.bind(this);
this.gotoCourse = this.gotoCourse.bind(this);
}
componentDidMount() {
axios.get('/vendor/showcourses') //the api to hit request
.then((response) => {
const course = response.data.map((courseres) => ({
id: courseres.id,
title: courseres.title
}));
this.setState({
course
});
});
}
render() {
return (
<ul>
{
this.state.course.map((eachCourse) => {
return <li key={eachCourse.id}>{eachCourse.title}</li>
})
}
</ul>
)
}
}

In each map iteration you rewrite your piece of state, it is wrong.
Just put courses in your state:
console.log(response);
this.setState({ courses: response.data });
In render method go through your state.courses:
render(){
return(
<div>
{this.state.courses.map(course => <h2>{course.title}</h2>)}
</div>
);
}

Related

React.js + Socket.io updating state changes position of list items

export default class extends React.Component {
constructor(props) {
super(props)
this.state = {
status: [],
services: []
}
getAppData((err,opt, data) => {
function Exists(list, id) {
return list.some(function(el) {
return el.data.id == id;
});
}
if (opt == "sysinfo"){
var filtered = this.state.status;
if (Exists(filtered, data.id)){
filtered = this.state.status.filter(function(el) { return el.data.id != data.id; });
}
filtered.push({ data })
this.setState({status: filtered})
} else if (opt == "init_services"){
this.setState({services: data})
}
});
}
render() {
const timestampforuse = this.state.status
const totalList = this.state.services
console.log(totalList)
const mainList = totalList.map((link) =>
<ListGroup.Item key={link.id} keyProp={link.id}>Name: {link.name} Node: {link.node}</ListGroup.Item>
);
console.log(totalList)
const listItems = timestampforuse.map((link) =>
<ListGroup.Item ><p key={link.data.id}>ID: {link.data.pid} Node: {link.data.node} <br/>Ram usage: {link.data.p_ram.toFixed(2)} / 100% Cpu usage: {link.data.p_cpu.toFixed(2)} / 100%</p></ListGroup.Item>
);
return (
<div>
<ListGroup>
{mainList}
</ListGroup>
</div>
);
}
}
Data from sysinfo:
{
cores: 16,
cpu: 0,
id: "00ffab6ca93243f08eb10670d9c491d54cf674173d13c24a0a663ebb3f5e54d042ae",
node: "1",
p_cpu: 0,
p_ram: 0.18230482881430612,
pid: 29216,
ram: 28.78515625,
threads: 5,
time: 1609179904302,
time_from_startup: 1609179876.271594,
time_since_boot: 1608562209.0201786
}
Data for init:
add_game: true
description: "a test script"
id: "00ffab6ca93243f08eb10670d9c491d54a0a663ebb3f5e54d042ae"
name: "test331112321"
node: "1"
Socket script:
import openSocket from 'socket.io-client';
const socket = openSocket('http://localhost:3000');
function getAppData(cb) {
socket.on('update_system', data => cb(null,"sysinfo", data));
socket.on('init_services', data => cb(null,"init_services", data));
socket.emit('updated', 1000);
}
export { getAppData };
I have tried using a map and using it as a list but when it updates every second it updates too fast to even read. How would I make the name appear, then once data gets sent have that update the list but not update the entire list? At the moment, it allows it to update and change, and no issues if it's 1 item being updated but if there are 2 or more it updates too fast to see. How do I get around this?
I have fixed this by updating an array of objects on the server-side. Updating a service on that list and returning the entire list. This tackled the issue of having it update too fast.
End code front end code:
export default class extends React.Component {
constructor(props) {
super(props)
this.state = {
services: []
}
getAppData((err,opt, data) => {
if (opt == "sysinfo"){
this.setState({services: data})
}
});
}
componentDidMount() {
fetch("http://localhost:3000/api/v1/bot/me/getservices").then(res => res.json()).then(data =>{
console.log(data)
this.setState({services: data})
})
}
render() {
const totalList = this.state.services
const listItems = totalList.map((link) =>
<ListGroup.Item key={link.id}>Name: {link.name} Node: {link.node} <br/>Ram usage: {link.p_ram.toFixed(2)} / 100% Cpu usage: {link.p_cpu.toFixed(2)} / 100%</ListGroup.Item>
);
return (
<div>
<ListGroup>
{listItems}
</ListGroup>
</div>
);
}
}

Any efficient way to render nested json list in React?

I am able to fetch REST API where I can get nested json output, and I want them to display in React component. Now I only can render them in the console which is not my goal actually. I am wondering if there is an efficient way to do this for rendering nested json list in React. can anyone give me a possible idea to make this work?
here is what I did:
import React, { Component } from "react";
class JsonItem extends Component {
render() {
return <li>
{ this.props.name }
{ this.props.children }
</li>
}
}
export default class List extends Component {
constructor(props){
super(props)
this.state = {
data: []
}
};
componentDidMount() {
fetch("/students")
.then(res => res.json())
.then(json => {
this.setState({
data: json
});
});
}
list(data) {
const children = (items) => {
if (items) {
return <ul>{ this.list(items) }</ul>
}
}
return data.map((node, index) => {
return <JsonItem key={ node.id } name={ node.name }>
{ children(node.items) }
</JsonItem>
});
}
render() {
return <ul>
{ this.list(this.props.data) }
</ul>
}
}
<script src="https://facebook.github.io/react/js/jsfiddle-integration-babel.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
my current output:
in my above component, I could render nested list on the console like this:
[![enter image description here][1]][1]
desired output:
how can I properly render out nested json output on React? Any idea to make this happen? any thought? Thanks
As you knew .map() is the common solution for this. But you can make this much better like below.
export default class List extends Component {
constructor(props){
super(props)
this.state = {
data: [],
isLoaded: false, //initally the loading state is false.
}
};
componentDidMount() {
fetch("/students")
.then(res => res.json())
.then(json => {
//updating the loading state and data.
this.setState({data: json, isLoaded:true});
});
}
render() {
//Waiting ajax response or ajax not yet triggered.
if(!this.state.isLoaded){
return(<div>Loading...</div>);
}else{
//Rendering the data from state.
let studenDetails = this.state.data.map((student, i) => {
let uin = student.uin;
let studentInfo = Object.keys(student.studentInfo).map((label, i) => {
return (
<div key={i}>
<span>
<strong>{label}: </strong>{`${student.studentInfo[label]}`}
</span>
</div>
);
});
return (
<div key={i}>
<h3>{uin}</h3>
<p>{studentInfo}</p>
</div>
);
});
return (<div>{studenDetails}</div>);
}
}
}
Hope it will help you.
To render a list in react use the .map() function to build a list of jsx elements.
render() {
let myRenderedData = this.state.data.map((x, index) => {
return <p key={index}>{x.uin}</p>
})
return (<div>{myRenderedData}</div>)
}

How to show information from API when using search box in ReactJS?

I'm using the Star Wars API to build a React JS project. The aim of my app is to be able to search for characters.
Here is my code for the search component in the my app.
At the moment I'm able to retrieve data the API and show the information on the page but I can't work out how to show this information when it's searched for.
Any ideas?
import React, { Component } from 'react';
class Search extends Component {
constructor(props){
super(props)
this.state = {
query:'',
peoples: [],
}
}
onChange (e){
this.setState({
query: e.target.value
})
if(this.state.query && this.state.query.length > 1) {
if(this.state.query.length % 2 === 0){
this.componentDidMount()
}
}
}
componentDidMount(){
const url = "https://swapi.co/api/people/";
fetch (url,{
method:'GET'
}).then(results => {
return results.json();
}).then(data => {
let peoples = data.results.map((people) => {
return(
<ul key={people.name}>
<li>{people.name}</li>
</ul>
)
})
this.setState({peoples: peoples});
console.log("state", peoples)
})
}
render() {
return (
<form>
<input
type="text"
className="search-box"
placeholder="Search for..."
onChange={this.onChange.bind(this)}
/>
{this.state.peoples}
</form>
)
}
}
export default Search
You could put your fetch in a separate function instead of in componentDidMount and call that when the component mounts and when your query changes.
Since you might be creating multiple requests if the user types quickly, you could use a debounce to only send one request, or use something that verifies that you always use the result of the latest request, like e.g. a token.
Example
class Search extends Component {
token = null;
state = {
query: "",
people: []
};
onChange = e => {
const { value } = e.target;
this.setState({
query: value
});
this.search(value);
};
search = query => {
const url = `https://swapi.co/api/people?search=${query}`;
const token = {};
this.token = token;
fetch(url)
.then(results => results.json())
.then(data => {
if (this.token === token) {
this.setState({ people: data.results });
}
});
};
componentDidMount() {
this.search("");
}
render() {
return (
<form>
<input
type="text"
className="search-box"
placeholder="Search for..."
onChange={this.onChange}
/>
{this.state.people.map(person => (
<ul key={person.name}>
<li>{person.name}</li>
</ul>
))}
</form>
);
}
}
You have to define it in diff function to manage easy.
import React, { Component } from 'react';
class Search extends Component {
constructor(props) {
super(props)
this.state = {
query: null,
peoples: [],
}
}
componentDidMount() {
this.serachPeople(this.state.query);
}
onChange(e) {
this.setState({ query: e.target.value }, () => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query.length % 2 === 0) {
this.serachPeople(this.state.query);
}
} else {
this.serachPeople(this.state.query);
}
})
}
serachPeople(query) {
const url = "https://swapi.co/api/people/";
if (query) {
// if get value ion query so filter the data based on the query.
fetch(url, {
method: 'GET'
}).then(results => {
return results.json();
}).then(data => {
let peoples = data.results.filter(people => people.name === query).map((people) => {
return (
<ul key={people.name}>
<li>{people.name}</li>
</ul>
)
})
this.setState({ peoples: peoples });
console.log("state", peoples)
})
} else {
fetch(url, {
method: 'GET'
}).then(results => {
return results.json();
}).then(data => {
let peoples = data.results.map((people) => {
return (
<ul key={people.name}>
<li>{people.name}</li>
</ul>
)
})
this.setState({ peoples: peoples });
console.log("state", peoples)
})
}
}
render() {
return (
<form>
<input
type="text"
className="search-box"
placeholder="Search for..."
onChange={this.onChange.bind(this)}
/>
{this.state.peoples}
</form>
)
}
}
export default Search;
I hope this will help for u. Let me know if u have any query.

How to access variable inside React Component? Initialized it outside Component

I'm currently a beginner working on a project in React/Redux. I'm trying to call JSON from an API file, save it as an array of objects, and then pass it into another file to start pulling data out of it. I recently got stuck in one place
Below is my class, which is accessing the JSON data and pulling it out to put into an array. I initialized the array outside of the class, but it's not being written to. I'm not really sure how to 'throw' the array that I need out of my class.
numberendpoint.json (an array of objects)
[
{
color: "red",
value: "#f00"
},
{
color: "green",
value: "#0f0"
},
{
color: "blue",
value: "#00f"
},
{
color: "cyan",
value: "#0ff"
},
{
color: "magenta",
value: "#f0f"
},
{
color: "yellow",
value: "#ff0"
},
{
color: "black",
value: "#000"
}
]
In index.js
let productJSON = [] //initialize productJSON array here
class Hue extends React.Component {
constructor() {
super();
this.state = {
elements: [],
productJSON: []
};
}
componentWillMount() {
fetch('numberendpoint.json')
.then(results => {
return results.json();
}).then(data => {
let colorArray = [] //initialize array to receive json data
for (let i =0; i < data.length; i++) {
colorArray.push(data[i])
}
productJSON = JSON.stringify(productArray) //here is where I try to assign the productJSON array
let elements = data.map((rainbow) => {
return (
<div key={rainbow.results}>
<p>{raindow.color}</p>
<p>{rainbow.value}</p>
</div>
)
})
this.setState({elements: elements});
console.log("state", this.state.elements[0]);
})
}
render() {
return (
<div>
<div className="container2">
{this.state.elements}
</div>
</div>
)}
}
How can I access the JSONproduct array? or alternatively, how do I 'pop' it out of this class so I can use it?
Update: used the solution suggested by Rahamin. Now I have this code below, all contained within the the "Hue" class. But I'm still getting errors.
import React from 'react'
const TIMEOUT = 100
let productJSON;
class Hue extends React.Component {
constructor() {
super();
this.state = {
products: [],
};
this.getColors = this.getColors.bind(this)
}
componentDidMount() {
fetch('http://tech.work.co/shopping-cart/products.json')
.then(results => {
return results.json();
}).then(data => {
let colorArray = []
for (let i =0; i < data.length; i++) {
colorArray.push(data[i])
}
console.log("jsonproduct=" + JSON.stringify(productArray))
productJSON = JSON.stringify(productArray)
this.setState({productJSON: productJSON});
});
}
render() {
return (
<div>
<div className="container2">
{this.state.productJSON}
</div>
</div>
)
}
}
export default {
getProducts: (cb, timeout) => setTimeout(() => cb(({ productJSON: value})), timeout || TIMEOUT), // here is where I am getting an error -- "value" is undefined. I'm not sure I was meant to put "value" there or something else...very new to React so its conventions are still foreign to me.
buyProducts: (payload, cb, timeout) => setTimeout(() => cb(), timeout || TIMEOUT)
}
let productJSON = [] //initialize productJSON array here
class Hue extends React.Component {
constructor() {
super();
this.state = {
elements: [],
productJSON: []
};
}
componentDidMount() {
fetch('numberendpoint.json')
.then(res => {
this.setState({elements: res.data});
})
}
render() {
if(this.state.elements.length > 0){ //once the data is fetched
return (
<div>
<div className="container2">
{this.state.elements.map((rainbow) => {
return (
<div key={rainbow.results}>
<p>{raindow.color}</p>
<p>{rainbow.value}</p>
</div>
)
})}
</div>
</div>
)
}
else{ // initial render
return null;
}
}
I don't really understand why you are trying to put array OUTSIDE of a class but I think you need to understand when each event gets called in React.
componentDidMount is an event that gets called when all the components have mounted in the class. So at this stage, render() function has already run. Which means your productJSON is undefined at this stage. What you really wanna do is that make sure your component changes when the state gets updated to something other than undefined.
Try the following code.
let productJSON = [] //initialize productJSON array here
class Hue extends React.Component {
constructor() {
super();
this.state = {
elements: [],
};
}
componentWillMount() {
fetch('numberendpoint.json')
.then(results => {
return results.json();
}).then(data => {
let colorArray = [] //initialize array to receive json data
for (let i =0; i < data.length; i++) {
colorArray.push(data[i])
}
this.setState({productJSON:colorArray});
let elements = data.map((rainbow) => {
return (
<div key={rainbow.results}>
<p>{raindow.color}</p>
<p>{rainbow.value}</p>
</div>
)
})
this.setState({elements: elements});
console.log("state", this.state.elements[0]);
})
}
render() {
return (
<div>
<div className="container2">
{this.state.productJSON ? 'state not ready' : this.state.productJSON} //this is the important part. It will render this.state.productJSON only when it is a truthy value.
</div>
</div>
)}
}
Given that you do get a valid colorArray from your call, this will work.

React-Flux Load initial state

I'm trying to make an Ajax request in al React Flux app with axios and I get data after state is set.
I have this code in root app:
InitialData.getInitialPosts();
The API request it looks like this:
let PostsApi = {
getAllPosts(){
return axios.get('https://jsonplaceholder.typicode.com/posts')
.then( (response) => {
console.log('All posts: ', response.data)
return response.data;
});
}
}
export default PostsApi;
In actions/initialData.js i have this:
let LoadInitialData = {
getInitialPosts(){
Dispatcher.dispatch({
actionType: 'LOAD_INITIAL_POSTS',
initialPosts: {
posts: PostsApi.getAllPosts()
}
})
}
}
export default LoadInitialData;
In store:
let _posts = [];
const PostsStore = Object.assign({}, EventEmitter.prototype, {
addChangeListener(callback){
this.on('change', callback)
},
removeChangeListener(callback){
this.removeChangeListener('change', callback)
},
emitChange(callback){
this.emit('change', callback)
},
getAllPosts(){
return _posts;
}
});
Dispatcher.register(function(action){
switch(action.actionType){
case 'LOAD_INITIAL_POSTS':
_posts = action.initialPosts.posts;
PostsStore.emitChange();
break;
default:
}
});
In View:
export default class PostsPage extends React.Component {
constructor(){
super();
this.state = {
posts: []
}
}
componentDidMount(){
this.setState({
posts: PostsStore.getAllPosts()
});
}
render(){
const { posts } = this.state;
return(
<div>
{posts.map( post => {
return <h3 key={post.id}>{post.title}</h3>
})}
</div>
)
}
}
On console.log:
state: Object {posts: Array[0]}
state: Object {posts: Promise}
postsApi.js:7 All posts: [Object, Object, Object, Object, Object, Object...]
And the problem is the ajax request is after componentDidMount.
Your PostsPage component is not set up correctly to listen to changes from the store. The code you have will only grab the list of posts once when it first mounts. You want it to update whenever the Store gets new data.
To accomplish this, you need to utilize the add/remove Change Listener functions that you setup in the Store. It should look something like this;
export default class PostsPage extends React.Component {
constructor(){
super();
this.state = {
posts: []
}
}
_calculateState(){
this.setState({
posts: PostsStore.getAllPosts()
});
}
componentDidMount(){
PostsStore.addChangeListener(this._calculateState);
},
componentWillUnmount(){
PostsStore.removeChangeListener(this._calculateState);
},
render(){
const { posts } = this.state.posts;
return(
<div>
{posts.map( post => {
return <h3 key={post.id}>{post.title}</h3>
})}
</div>
)
}
}

Categories

Resources