Based on this previous questions I made (Show fetch results in render return() in React.js), from which I received json results, I now need to count the number of sofas that each brand has. For example, Brand X has 2 occurences and Brand Y has 3043.
I get the brand from one sofa by calling myUrlApi + /couch-model on fetch and the json is something like what you can see in the picture below.
Has you can see each sofa has associated to itself a brand. What I want to count is how many sofa each brand has.
I'll put my current code here:
export class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
token: {},
isLoaded: false,
models: []
};
}
componentDidMount() {
/*code to generate token, not needed for the purpose of the question*/
fetch(url + "/couch-model/?limit=9", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: "JWT " + JSON.parse(localStorage.getItem("token")).token
}
})
.then(res => {
if (res.ok) {
return res.json();
} else {
throw Error(res.statusText);
}
})
.then(json => {
this.setState(
{
models: json.results
},
() => {}
);
});
}
render() {
const { isLoaded, models } = this.state;
if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div>
{models.map(model => (
<a href="/sofa" key={model.id}>
<div className="Parcelas">
<img src={img_src} className="ParcImage" alt="sofa" />
<h1>Sofá {model.name}</h1>
<h2>
1,200<span>€</span>
</h2>
<p
className="Features"
dangerouslySetInnerHTML={{ __html: model.description }}
/>
<button className="Botao">
<p className="MostraDepois">Ver Detalhes</p>
<span>+</span>
</button>
<img
src="../../img/points.svg"
className="Decoration"
alt="points"
/>
</div>
</a>
))}
</div>
);
}
}
}
Hope I was clear, ask if you have any doubt.
if your results look like this as you said in your post :
{
results: [
{
brand: { name: "Brand-A", image: "", etc: "..." },
category: "A",
code: "AAA",
name: "SofaA",
price: 1200
},
{
brand: { name: "Brand-A", image: "", etc: "..." },
category: "A",
code: "AAA",
name: "SofaB",
price: 1200
},
{
brand: { name: "Brand-B", image: "", etc: "..." },
category: "A",
code: "AAA",
name: "SofaC",
price: 1200
}
]
}
You can add a state property like sofasPerBrand initialized to {}
constructor(props) {
super(props);
this.state = {
token: {},
isLoaded: true,
models: [],
sofasPerBrand: {}
};
}
And add in the setState function in componentDidMount the RIYAJ KHAN reduce function like this :
this.setState(
{
models: json.results,
sofasPerBrand: json.results.reduce((coundData, sofa, index) => {
if (!!coundData[sofa.brand.name]) {
coundData[sofa.brand.name] += 1;
} else {
coundData[sofa.brand.name] = 1;
}
return coundData;
}, {})
},
() => { }
);
then you can declare it in your render function :
const { isLoaded, models, sofasPerBrand } = this.state;
and use it like that any where :
<ul>
{Object.keys(sofasPerBrand).map(brand=>(
<li>{brand} : {sofasPerBrand[brand]}</li>
))}
</ul>
One can use javascript#reducers for it.
models.reduce((coundData,sofa,index)=>{
if(!!coundData[sofa.brand.name]){
coundData[sofa.brand.name] +=1;
}else{
coundData[sofa.brand.name]=1;
}
return coundData;
}, {})
Related
this is my state
this.state = {
notification: [{
from: {
id: someid,
name: somename
},
message: [somemessage]
},
{..},
{..},
]
}
Now if i get a new message from someid, i have to push that new message into message array of someid
I tried to push that message in different ways, but nothing has worked
I tried it in this way, but im not able to push a new message into message array
if (this.state.notification) {
for (let q = 0; q < this.state.notification.length; q++) {
if (
this.state.notification[q] &&
this.state.notification[q].from &&
this.state.notification[q].from.id === r.from.id
) {
this.setState({
notification: [
...this.state.notification[q].messages,
this.state.notification[q].messages.push(r.message),
],
});
return console.log(this.state.notification[q].messages)
}
}
} else {
this.setState({
notification: [{
from: r.from,
messages: [r.message]
}, ]
});
return console.log(JSON.stringify(this.state.notification));
}
First of all, I think structuring your state as a 2d array is not a good idea. But you can try this
const pushMessage = (someId, someMessage) => {
this.setState({
notifications: this.state.notifications.map((notification) => {
if (notification.from.id === someId) {
return {
...notification,
messages: [...notification.messages, someMessage],
};
}
return notification;
}),
});
};
I'm pretty sure you can't do this: this.state.notification[q].messages.push(r.message). You can't mutate your state directly. You should work with a copy o your state, modify it with your code that seems to be ok, and then do the setState(...).
Here is a repro on Stackblitz that works. Here is the code :
import React, { Component } from "react";
import { render } from "react-dom";
import "./style.css";
class App extends Component {
constructor() {
super();
this.state = {
notifications: [
{
from: { id: 0, name: "Quentin" },
message: ["Message 1"]
},
{
from: { id: 1, name: "John" },
message: ["Message 1"]
},
{
from: { id: 2, name: "Henry" },
message: ["Message 1"]
}
]
};
this.pushMessage = this.pushMessage.bind(this);
}
pushMessage (id, message) {
const newState = Object.assign([], this.state);
newState.notifications.forEach(notif => {
if (notif.from.id === id) {
notif.message.push(message);
}
});
this.setState(newState, () => console.log(this.state));
};
render() {
return (
<div>
<button onClick={() => this.pushMessage(1, "Hello World")}>
Push message
</button>
</div>
);
}
}
render(<App />, document.getElementById("root"));
I only handle the push of a message in an existing notification, you already got the other case.
The first argument to setState is an updater function that takes the previous state as an argument. I think you should use this fact to update your state correctly.
Check out this answer https://medium.com/#baphemot/understanding-reactjs-setstate-a4640451865b.
I have a JSON file:
[
{
"id": 1,
"availability": false
},
{
"id": 2,
"availability": true
}
]
What I would like to achieve is to automatically display an image of a tick if availability : true and to display an image of a cross if availability : false.
For example these are the names of the two images:
tick.jpg
cross.jpg
This is my code so far:
import React, { Component } from "react";
import "./styles.css";
class GetOnlinePosts extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
posts: []
};
}
componentDidMount() {
fetch("https://api.myjson.com")
.then(response => response.json())
.then(
result => {
this.setState({
isLoaded: true,
posts: result
});
},
error => {
this.setState({
isLoaded: true,
error
});
}
);
}
render() {
const { error, isLoaded, posts } = this.state;
const orderedPosts = [
...posts.filter(post => post.availability),
...posts.filter(post => !post.availability)
];
if (error) {
return <div>Error in loading</div>;
} else if (!isLoaded) {
return <div>Loading ...</div>;
} else {
return (
<div>
<div className="tiles">
{orderedPosts.map(post => (
<div key={post.id}>
<div className="tile"></div>
</div>
))}
</div>
</div>
);
}
}
}
export default GetOnlinePosts;
Unfortunately I am unable to have the images included in the with JSON. I would like the images to be within the <div className="tile"> </div> so any help on how to do this would be great. Thanks in advance.
<img src={post.availability ? 'tick.jpg' : 'cross.jpg'} />
I developed a list which get the data of my product, I try that with the fetch and I get the data on the console but It not rendering on the list.
My code is :
constructor(props) {
super(props);
this.state = {
products :[],
id:props.match.params.id
}
}
componentWillReceiveProps(nextProps) {
console.log("nextProps", nextProps);
}
componentDidMount() {
fetch('http://172.16.234.24:8000/api/productDetail/'+this.props.match.params.id)
.then(Response => Response.json())
.then(data =>
{console.log(data.productDetails)
this.setState({ products: data.productDetails})})
}
render() {
let {products} = this.state;
console.log(products)
return (
<div><Well><strong>The details of your product: </strong>
<ul>
<li><strong>Product name :</strong></li><br/>
<li><strong>Categorie name :</strong></li><br/>
<li><strong>TaxRate :</strong></li><br/>
<li><strong>Description :</strong></li><br/>
</ul>
<ul>{products && products.length && products.map(product => (
<li key={product.id}>
{console.log(product.id)}
<li>{product.name}</li><br/>
<li>{product.categorie_id}</li><br/>
<li>{product.taxRate}</li><br/>
<li>{product.description}</li><br/>
</li>))}
</ul>
</Well></div>
)
}
}
when I run it, I get the data on the console :
but my list is empty like that :
How can I fix that ?
From what I understood, this is the result you are looking for :
const data = [
{
id: 8,
name: "AAAA",
categorie_id: 45,
taxRate: 78,
description: "Wow, so cool"
},
{
id: 15,
name: "BBBB",
categorie_id: 8,
taxRate: 5,
description: "damn"
},
{
id: 86,
name: "BBBBBBBFFFF",
categorie_id: 876,
taxRate: 0,
description: "hey, you !"
}
]
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
products: [],
//id: props.match.params.id
}
}
componentDidMount() {
this.setState({ products: data })
}
render() {
const { products } = this.state;
return (
<div>
{products && products.length && products.map(({id, name, categorie_id, taxRate, description}) =>
<div key={id}>
<strong>The details of your product: </strong>
<ul>
<li><strong>Product name : </strong>{name}</li><br />
<li><strong>Categorie name : </strong>{categorie_id}</li><br />
<li><strong>TaxRate : </strong>{taxRate}</li><br />
<li><strong>Description : </strong>{description}</li><br />
</ul>
</div>
)}
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'>
You should include the product field name into the map function.
EDIT
From your console output I think you likely forgot to take the right value in your data :
fetch('http://172.16.234.24:8000/api/productDetail/'+this.props.match.params.id)
.then(Response => Response.json())
.then(data =>
{console.log(data)
this.setState({ products: data.['product details'] })})
Just add .['product details']to take the details from your data.
EDIT 2
If your data is not an array, the following code should be enough :
const data = {
id: 8,
name: "AAAA",
categorie_id: 45,
taxRate: 78,
description: "Wow, so cool"
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
products: [],
//id: props.match.params.id
}
}
componentDidMount() {
this.setState({ products: data })
}
render() {
const { products: { name, categorie_id, taxRate, description } } = this.state;
return (
<div>
<strong>The details of your product: </strong>
<ul>
<li><strong>Product name : </strong>{name}</li><br />
<li><strong>Categorie name : </strong>{categorie_id}</li><br />
<li><strong>TaxRate : </strong>{taxRate}</li><br />
<li><strong>Description : </strong>{description}</li><br />
</ul>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'>
Hope your data looks like below:
this.state = {
product_details: {
Categorie: {},
categorie_id: 4,
name: "Testview",
id: 33,
description: "yes it works",
rate: 0,
vat: {},
vat_id: 2
}
};
to render this in list :
import React, { Component } from 'react';
import { render } from 'react-dom';
class App extends Component {
constructor() {
super();
this.state = {
product_details: {
Categorie: {},
categorie_id: 4,
name: "Testview",
id: 33,
description: "yes it works",
rate: 0,
vat: {},
vat_id: 2
}
};
}
render() {
const {name, categorie_id, id, description} = this.state.product_details;
return (
<>
<strong>The details of your product: </strong>
<ul>
<li><strong>Product name : </strong><span>{name}</span></li><br/>
<li><strong>Categorie name :</strong><span>{categorie_id}</span></li><br/>
<li><strong>TaxRate :</strong><span>{id}</span></li><br/>
<li><strong>Description :</strong><span>{description}</span></li>
</ul>
</>
);
}
}
render(<App />, document.getElementById('root'));
Demo link on this available here
Edit :
As you need to render object on the list item, that will be available here
The code below was designed to update a voting system. It works fine by displaying the results as the page loads.
Here is my problem: I need to update each user's vote any time the Get Vote Count button is clicked.
In the backend, I have php code which returns the array data as per below.
Can someone help me with displaying the array values and updating eg (vote to 11) depending on how the user voted?
<?php
// Update user response on a post
$return_arr[]= array("vote"=>"11");
echo json_encode($return_arr);
exit;
?>
Here is the array return by axios API Call
[{"vote":"11"}]
Here is the code
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
import axios from 'axios';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
loading: false
};
}
componentDidMount() {
this.setState({
data: [
{ id: "1", name: "Tony", vote: "3" },
{ id: "2", name: "Mark", vote: "6" },
{ id: "3", name: "Joy", vote: "2" }
]
});
}
handleVote(person_id, person_vote) {
const data_vote = {
person_id: person_id,
person_vote: person_vote
};
axios
.get("http://localhost/vote.php", { data_vote })
.then(response => {
this.setState({ result_vote: response.data });
console.log(this.state.result_vote);
})
.catch(error => {
console.log(error);
});
}
render() {
return (
<span>
<label>
<ul>
{this.state.data.map((person, i) => (
<li key={i}>
{person.name} --(vote count: {person.vote})
<br />
<input
type="button"
value="Get Vote Counts"
onClick={() => this.handleVote(person.id, person.vote)}
/>
</li>
))}
</ul>
</label>
</span>
);
}
}
You should set your data state after getting the vote data from the fetch response. You have person_id in your handler and getting an array including vote value. So, map through your data state find the relevant person and update its vote value.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
loading: false
};
}
componentDidMount() {
this.setState({
data: [
{ id: "1", name: "Tony", vote: "3" },
{ id: "2", name: "Mark", vote: "6" },
{ id: "3", name: "Joy", vote: "2" }
]
});
}
handleVote(person_id, person_vote) {
const data_vote = {
person_id: person_id,
person_vote: person_vote
};
axios
.get("http://localhost/vote.php", { data_vote })
.then(response => {
const newData = this.state.data.map(person => {
if (person.id !== person_id) return person;
return { ...person, vote: response.data[0].vote };
});
this.setState(state => ({
data: newData
}));
})
.catch(error => {
console.log(error);
});
}
render() {
return (
<span>
<label>
<ul>
{this.state.data.map(person => (
<li key={person.id}>
{person.name} --(vote count: {person.vote})
<br />
<input
type="button"
value="Get Vote Counts"
onClick={() => this.handleVote(person.id, person.vote)}
/>
</li>
))}
</ul>
</label>
</span>
);
}
}
Try to avoid using an index as a key. You have a person.id so use it in your map method. Also, as an enhancement, you can refactor your code and create a Person component. You can pass the related data and vote handler then setup the update logic there.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
loading: false,
};
}
componentDidMount() {
this.setState({
data: [
{ id: "1", name: "Tony", vote: "3" },
{ id: "2", name: "Mark", vote: "6" },
{ id: "3", name: "Joy", vote: "2" },
],
});
}
handleVote = (person) => {
const data_vote = {
person_id: person.id,
person_vote: person.vote,
};
axios
.get("http://localhost/vote.php", { data_vote })
.then((response) => {
const newData = this.state.data.map((el) => {
if (el.id !== person.id) return el;
return { ...el, vote: response.data[0].vote };
});
this.setState({ data: newData });
})
.catch((error) => {
console.log(error);
});
};
render() {
return (
<span>
<label>
<ul>
{this.state.data.map(person => (
<Person
key={person.id}
person={person}
handleVote={this.handleVote}
/>
))}
</ul>
</label>
</span>
);
}
}
const Person = (props) => {
const { person, handleVote } = props;
const onVote = () => handleVote(person);
return (
<li>
{person.name} --(vote count: {person.vote})
<br />
<input type="button" value="Get Vote Counts" onClick={onVote} />
</li>
);
};
So, since your handler function is getting the person_id and your call is returning the new vote count, you should update the current person object in your data table in state.
Here is an example:
Updating the vote count for the current user
super noob question. However, I've been stuck with this issue for long enough now. I've been searching and have found the similar issue but have not been able to resolve it. I was hoping someone could point me in the right direction as to what I am doing wrong.
I am fetching data from an API, and I am able to log out the array, however, when I try to print it out I am having no luck. In the beginning, I was passing it through a reducer but then realized that was not ideal. Right now I am trying to get it to work, then I'll refactor the code into a functional component.
fetchOrders = async () => {
await axios.get('http://localhost:4200/ordenes').then( res => {
this.setState({
prescription: res.data
})
})
}
render() {
console.log(this.state.prescription)
return (
<div >
<Paper style={styles.orderCard}id='orderCardContainer' >
<div id="profileCardContainer" style={styles.profileCard}>
<aside className='profileCardContainer'>
<ProfileCard />
</aside>
</div>
{this.renderOrder()}
</Paper>
</div>
)
}
}
The array I receive back from the API looks something like this:
Array(18)
0: {order: Array(3), _id: "5b2af38fb315eb5630a0bc78", physicianName: "Alejandro", patientName: "Alex", createdAt: "2018-06-21T00:38:39.376Z", …}
UPDATE:
class OrderCard extends Component {
constructor(props) {
super(props)
this.state = { prescription: []}
}
fetchOrders = async () => {
const res = await axios.get('http://localhost:4200/ordenes')
this.setState({
prescription: res.data
})
}
componentDidMount() {
this.fetchOrders()
}
renderOrder() {
if (this.state.prescription.length === 0 ) {
return (
<h1>Loading...</h1>
)
} else {
return (
this.state.prescription.map( (x, i) => {
<li>
{console.log(x.patientName)}
<h1>{ x.patientName }</h1>
</li>
})
)}
}
render() {
console.log(this.state.prescription)
return (
<div >
<Paper style={styles.orderCard}id='orderCardContainer' >
<div id="profileCardContainer" style={styles.profileCard}>
<aside className='profileCardContainer'>
<ProfileCard />
</aside>
</div>
<div id='orders' >
{ this.renderOrder() }
</div>
</Paper>
</div>
)
}
}
function mapStateToProps(state) {
return {
auth: state.auth.authenticated,
}
}
export default connect(mapStateToProps, actions)(OrderCard)
BACKEND SCHEMA
const orderObj = new Schema({
name: String,
price: Number,
}, { _id : false })
const orderSchema = new Schema (
{
physicianName: String,
patientName: String,
order: {type: [orderObj]}
},
{
timestamps: true
}
)
POSTMAN API RESULTS
[
{
"order": [
{
"name": "Asteraceae",
"price": 41.24
},
{
"name": "Liliaceae",
"price": 39.24
},
{
"name": "Fabaceae",
"price": 34.91
}
],
"_id": "5b2af38fb315eb5630a0bc78",
"physicianName": "Alejandro",
"patientName": "Alex",
"createdAt": "2018-06-21T00:38:39.376Z",
"updatedAt": "2018-06-21T00:38:39.376Z",
"__v": 0
}
]
Thanks in advance for tips.
Wrong brackets, you're not returning react element, try fixed one.
renderOrder() {
if (this.state.prescription.length === 0 ) {
return (
<h1>Loading...</h1>
)
} else {
return (
this.state.prescription.map((x, i) => (
<li>
{console.log(x.patientName)}
<h1>{ x.patientName }</h1>
</li>
))
)}
}
and fix this
fetchOrders = async () => {
const res = await axios.get('http://localhost:4200/ordenes');
this.setState({
prescription: res.data
});
}