Reputation: 2470
I have three election contestant that I want to update their vote counts by making axios call to server backend.
Everything is working fine.
Here is an example of json response from axios call.
[{ "id": "1", "name": "contestant 1", "vote": "300" }]
My Issue:
My only issue is the loading event. When I click on first contestant to update its vote result via axios call. Instead of the application to display Content Loading... only for the click contestant button, it will display Content Loading... for all the three contestant button.
What I want:
Please how do I display Content Loading... only for the clicked contestant buttons
I guess this has to do with the Id of the clicked contestant. I have also tried this but no way
if (person.id === person_id){
this.setState({ loading: true });
}
Here is the code
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
loading: false,
isLoading: true
};
}
componentDidMount() {
this.setState({
data: [
{ id: "1", name: "contestant 1", vote: "3" },
{ id: "2", name: "contestant 2", vote: "6" },
{ id: "3", name: "contestant 3", vote: "2" }
]
});
}
handleVote(person_id, person_vote) {
const data_vote = {
person_id: person_id,
person_vote: person_vote
};
this.setState({ loading: true }, () => {
axios.get("http://localhost/apidb_react/result.json", { 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,
loading: false,
}));
})
.catch(error => {
console.log(error);
});
});// close loading
}
render() {
let data_loading;
if (this.state.loading) {
data_loading = "Content Loading......"
}
return (
<span>
<label>
<ul>
{this.state.data.map(person => (
<li key={person.id}>
{person.name} --(vote count: {person.vote})
<br />
{data_loading}
<button
type="button"
onClick={() => this.handleVote(person.id, person.vote)}
> Get Vote</button>
</li>
))}
</ul>
</label>
</span>
);
}
}
Upvotes: 0
Views: 709
Reputation: 16726
You will need a separate loading
state for every contestant. So the logical next step would be to create Contestant component having it's own state and own UI for reflecting that state to the user.
Something like this:
class Contestant extends React.Component {
static defaultProps = {
onVote: () => {},
onError: () => {}
};
constructor() {
super();
this.state = {
isLoading: false
};
}
handleVote(person_id, person_vote) {
const data_vote = {
person_id: person_id,
person_vote: person_vote
};
this.setState({ isLoading: true });
axios
.get("http://localhost/apidb_react/result.json", { data_vote })
.then(response => {
this.props.onVote(person_id, response.data[0].vote);
this.setState({ isLoading: false });
})
.catch(error => {
this.props.onError(error);
});
}
render() {
const { person } = this.props;
const { isLoading } = this.state;
return (
<li key={person.id}>
{isLoading
? "Content Loading......"
: `${person.name} --(vote count: ${person.vote})`}
<br />
<button
type="button"
onClick={() => this.handleVote(person.id, person.vote)}
>
Get Vote
</button>
</li>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
componentDidMount() {
this.setState({
data: [
{ id: "1", name: "contestant 1", vote: "3" },
{ id: "2", name: "contestant 2", vote: "6" },
{ id: "3", name: "contestant 3", vote: "2" }
]
});
}
onVote = (person_id, vote) => {
const { data } = this.state;
this.setState({
data: data.map(person => {
if (person.id !== person_id) return person;
return { ...person, vote };
})
});
};
render() {
return (
<span>
<label>
<ul>
{this.state.data.map(person => (
<Contestant
key={person.id}
person={person}
onVote={this.onVote}
/>
))}
</ul>
</label>
</span>
);
}
}
Somewhat functional sandbox: https://codesandbox.io/s/p7q3qylwj0. Obviously you will need to hit a real endpoint there, instead of:http://localhost/apidb_react/result.json
Upvotes: 1