I have a "Nightlife Coordination" app (from the Free Code Camp curriculum) that allows a user to search by city and RSVP to a bar for that night. The app keeps a list of who has RSVP'd and who is going. It is built with React and Bootstrap v4 (and Node on the back end).
I have text under each bar location that, when clicked, allows a user to RSVP or unRSVP. There is also a button that shows how many people have RSVP'd and, if clicked, will display a Bootstrap popover of the list of people who have RSVP'd.
If a user RSVPs (or unRSVPs), I want the list to update. (Currently, the number on the button DOES update, but not the list.)
The following two images show the problem:
Upon initial load, all is correctly functional
When the user RSVPS or unRSVPs, the number on the button correctly updates, but the list does not
Here is my code.
The list is being generated in the data-content
attribute in the second anchor tag in the render method.
Can anyone help?
One other hint is that in my React developer tools Chrome extension, it shows the data-content
attribute correctly updating upon RSVP and unRSVP. Is it that perhaps Bootstrap saves the contents of the data-content
attribute in its JS file upon initial render and does not update it?
const React = require('react');
class Bar extends React.Component {
constructor(props) {
super(props);
this.state = {
countMeIn: false, // coming from Mongo
numberGoing: this.props.user_namesArr.length,
user_id: this.props.twitter_id,
user_name: this.props.user_name,
yelp_id: this.props.yelp_id,
user_namesArr: this.props.user_namesArr
};
}
componentDidMount() { // need the same for DidMount and DidUpdate, in case user is signed in upon load (from previous session), or signs in after load
if (this.state.user_namesArr.includes(this.props.user_name) && !this.state.countMeIn) {
this.setState({
countMeIn: true
});
}
}
componentDidUpdate(prevProps, prevState) { // Need both in case user logs in after initial page load
console.log(this.state.user_namesArr);
if (this.state.user_namesArr.includes(this.props.user_name) && !prevState.countMeIn) {
this.setState({
countMeIn: true
});
}
$('[data-toggle="popover"]').popover();
}
rsvp() {
let url = '/rsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
fetch(url, { method: "POST" })
.then((res) => res.json())
.then((json) => {
let newArr = this.state.user_namesArr;
newArr.push(this.props.user_name);
this.setState({
numberGoing: this.state.numberGoing + 1,
countMeIn: true,
user_namesArr: newArr,
});
})
}
unrsvp() {
let url = '/unrsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
fetch(url, { method: "POST" })
.then((res) => res.json())
.then((json) => {
let ind = this.state.user_namesArr.indexOf(this.props.user_name);
let newArr = this.state.user_namesArr;
newArr.splice(ind, 1);
this.setState({
numberGoing: this.state.numberGoing - 1,
countMeIn: false,
user_namesArr: newArr,
});
})
}
render() {
return (
<div className="col-lg-4 onecomponent">
<a href={ this.props.bar_yelp_url } target="_blank">
<div className="barname text-center">
{ this.props.name }
</div>
<div className="priceline">
<img className="stars" src={ this.state.starsUrl } /> { this.props.review_count } reviews <span className="price">{ this.props.price }</span>
</div>
<div className="image">
<img class="mainimg" src={ this.props.image_url } />
</div>
<div className="address text-center">
{ this.props.loc[0] }., { this.props.loc[1] }
</div>
</a>
<hr/>
<div className="text-center">
<a tabindex="0" role="button" className="btn btn-success" data-toggle={ this.state.user_namesArr.length > 0 ? "popover" : "" } data-trigger="focus" title="Who's In?" data-content={ this.state.user_namesArr }>
{ this.state.numberGoing } going
</a>
{
this.props.loggedIn ?
this.state.countMeIn ?
<span className="going" onClick={ () => this.unrsvp() }>You're going!</span> : // if logged in and already RSVP'd
<span className="rsvpdetails" onClick={ () => this.rsvp() }>Count me in!</span> : // if logged in but not yet RSVP'd
<span> Please log in </span> // if not logged in
}
</div>
</div>
)
}
}
module.exports = Bar;
It works with Reactstrap. I simply added reactstrap
to my package.json file, and used the Reactstrap code.
const React = require('react');
import { Button, Popover, PopoverHeader, PopoverBody } from 'reactstrap';
class Bar extends React.Component {
constructor(props) {
super(props);
this.state = {
countMeIn: false, // coming from Mongo
numberGoing: this.props.user_namesArr.length,
user_id: this.props.twitter_id,
user_name: this.props.user_name,
yelp_id: this.props.yelp_id,
user_namesArr: this.props.user_namesArr,
popover: false
};
this.toggle = this.toggle.bind(this);
}
componentDidMount() { // need the same for DidMount and DidUpdate, in case user is signed in upon load (from previous session), or signs in after load
if (this.state.user_namesArr.includes(this.props.user_name) && !this.state.countMeIn) {
this.setState({
countMeIn: true
});
}
}
componentDidUpdate(prevProps, prevState) {
if (this.state.user_namesArr.includes(this.props.user_name) && !prevState.countMeIn) {
this.setState({
countMeIn: true
});
}
}
rsvp() {
let url = '/rsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
fetch(url, { method: "POST" })
.then((res) => res.json())
.then((json) => {
let newArr = this.state.user_namesArr;
newArr.push(this.props.user_name);
this.setState({
user_namesArr: newArr,
numberGoing: this.state.numberGoing + 1,
countMeIn: true
});
})
}
unrsvp() {
let url = '/unrsvp/?&yelp_id=' + this.props.yelp_id + '&user_id=' + this.props.twitter_id + '&user_name=' + this.props.user_name;
fetch(url, { method: "POST" })
.then((res) => res.json())
.then((json) => {
let ind = this.state.user_namesArr.indexOf(this.props.user_name);
let newArr = this.state.user_namesArr;
newArr.splice(ind, 1);
this.setState({
user_namesArr: newArr,
numberGoing: this.state.numberGoing - 1,
countMeIn: false
});
})
}
toggle() {
this.setState({
popover: !this.state.popover
});
}
render() {
return (
<div className="col-lg-4 onecomponent">
<a href={ this.props.bar_yelp_url } target="_blank">
<div className="barname text-center">
{ this.props.name }
</div>
<div className="priceline">
<img className="stars" src={ this.state.starsUrl } /> { this.props.review_count } reviews <span className="price">{ this.props.price }</span>
</div>
<div className="image">
<img class="mainimg" src={ this.props.image_url } />
</div>
<div className="address text-center">
{ this.props.loc[0] }., { this.props.loc[1] }
</div>
</a>
<hr/>
<div className="text-center">
{ /* For this to work, id must have leading letters, otherwise throws massive errors. See here: https://stackoverflow.com/questions/23898873/failed-to-execute-queryselectorall-on-document-how-to-fix */ }
<Button id={ "abc" + this.props.yelp_id } className="btn btn-success" onClick={ this.toggle }>{ this.state.numberGoing } going</Button>
<Popover placement="right" isOpen={ this.state.popover } target={ "abc" + this.props.yelp_id } toggle={ this.toggle }>
<PopoverHeader>Who's In?</PopoverHeader>
<PopoverBody>{ this.state.user_namesArr }</PopoverBody>
</Popover>
{
this.props.loggedIn ?
this.state.countMeIn ?
<span className="going" onClick={ () => this.unrsvp() }>You're going!</span> : // if logged in and already RSVP'd
<span className="rsvpdetails" onClick={ () => this.rsvp() }>Count me in!</span> : // if logged in but not yet RSVP'd
<span> Please log in </span> // if not logged in
}
</div>
</div>
)
}
}
module.exports = Bar;