Bootstrap v4 popover not updating on state change in React - reactjs

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;

Maybe using ref could help ... but why not use reactstrap and more important why not react-popper ...? It's well known (https://github.com/FezVrasta/popper.js/#react-vuejs-angular-angularjs-emberjs-etc-integration) that many libraries doesn't work well with react or any other (virtual) DOM managers.
Do you really need jQuery?
Using react portals you can remove all theese dependencies.

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;

Related

Open bootstrap modal after axios http call in react

I made an HTTP call in react using Axios. It works perfectly fine. But when I try to open a bootstrap 4 modal after HTTP call success. It shows me an error 'modal is not a function'. I try a number of ways to solve this but unable to solve the problem. I didn't upload the whole code as it is quite long. Let me know in the comments if you want any additional code sample. Please help.
import $ from 'jquery';
import '../assets/css/signup.css';
import { Link } from 'react-router-dom';
import axios from 'axios';
import SuccessMessage from './dashboard/SuccessMessage';
class SignUp extends React.Component{
constructor()
{
super()
this.state={
firstName:'',
lastName:'',
email:'',
phoneNumber:'',
password:'',
confirmPassword:'',
isSignUp:false
}
}
componentDidUpdate()
{
if(this.state.isSignUp === true)
{
let user = {
firstName: this.state.firstName,
lastName: this.state.lastName,
email:this.state.email,
phoneNumber:this.state.phoneNumber,
password:this.state.password
}
console.log(user);
var first_name = user.firstName;
var last_name=user.lastName;
var email=user.email;
var phone_no=user.phoneNumber;
var password = user.password;
axios.post("http://ec2-14-2a9-69-0b6.us-west-2.compute.amazonaws.com:4000/dashboard/register", {
first_name,
last_name,
email,
phone_no,
password
}, {
headers: header
})
.then(res => {
console.log(res);
if(res.status === 200 && res.data.success === true)
{
setTimeout(() =>
{
$('#signup-success').modal('show');
},200)
}
})
}
}
handleSubmit=(e) =>
{
e.preventDefault();
this.setState({isSignUp:true});
}
render()
{
return(
<SuccessMessage heading="Sign Up Successfully!" description="Please login in to access your account" iconClass="fa fa-check bg-golden flex all-center border-radius-50" modalId="signup-success"/>
)
}
Success Message component
<div className="modal" id={this.props.modalId}>
<div className="modal-dialog modal-dialog-centered">
<div className="modal-content">
<div className="modal-header">
<h4 className="modal-title">Modal Heading</h4>
<button type="button" className="close" data-dismiss="modal">×</button>
</div>
<div className="modal-body align-center" style={style}>
<i style={icon} className={this.props.iconClass} ></i>
<h3 className="heading color-black">{this.props.heading}</h3>
<p className="paragraph color-black">{this.props.description}</p>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-danger" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
Try not to use jquery and react together. You could achieve what you are saying using the react state:
class SignUp extends React.Component{
constructor()
{
super()
this.state={
firstName:'',
lastName:'',
email:'',
phoneNumber:'',
password:'',
confirmPassword:'',
isSignUp:false,
showModal: false
}
}
componentDidUpdate()
{
if(this.state.isSignUp === true)
{
let user = {
firstName: this.state.firstName,
lastName: this.state.lastName,
email:this.state.email,
phoneNumber:this.state.phoneNumber,
password:this.state.password
}
console.log(user);
var first_name = user.firstName;
var last_name=user.lastName;
var email=user.email;
var phone_no=user.phoneNumber;
var password = user.password;
axios.post("http://ec2-14-2a9-69-0b6.us-west-2.compute.amazonaws.com:4000/dashboard/register", {
first_name,
last_name,
email,
phone_no,
password
}, {
headers: header
})
.then(res => {
console.log(res);
if(res.status === 200 && res.data.success === true)
{
setTimeout(() =>
{
this.setState({ showModal: true });
},200)
}
})
}
}
handleSubmit=(e) =>
{
e.preventDefault();
this.setState({isSignUp:true});
}
render()
{
return(
<div>
{
this.state.showModal &&
<SuccessMessage heading="Sign Up Successfully!" description="Please login in to access your account" iconClass="fa fa-check bg-golden flex all-center border-radius-50" modalId="signup-success"/>
</div>
)
}
Also, I guess you got a display: none or something in the modal as you are doing a .show using jquery. Put that to display always as it will be only shown if the state is true.
Actually getting the Bootstrap Modal to display using React (without jQuery) requires DOM manipulation. Bootstrap 4 uses jQuery to add a modal backdrop element, adds the modal-open class to the body, and finally adds display:block to the .modal wrapper.
This is why it's preferable to using reactstrap, react-bootstrap, etc... since they've already componentized the Bootstrap Modal.
If you must show (toggle) the Bootstrap Modal in React without jQuery (or other component framework), here's an example:
class SuccessMessage extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
modalClasses: ['modal','fade']
}
}
toggle() {
document.body.className += ' modal-open'
let modalClasses = this.state.modalClasses
if (modalClasses.indexOf('show') > -1) {
modalClasses.pop()
//hide backdrop
let backdrop = document.querySelector('.modal-backdrop')
document.body.removeChild(backdrop)
}
else {
modalClasses.push('show')
//show backdrop
let backdrop = document.createElement('div')
backdrop.classList = "modal-backdrop fade show"
document.body.appendChild(backdrop)
}
this.setState({
modalClasses
})
}
render() {
return (
<div
id="messageModal"
className={this.state.modalClasses.join(' ')}
tabIndex="-1"
role="dialog"
aria-hidden="true"
ref="messageModal"
>
<div className="modal-dialog modal-dialog-centered modal-lg">
<div className="modal-content">
<div className="modal-header">
<h4>
Success
</h4>
...
</div>
<div className="modal-body">
...
</div>
</div>
</div>
</div>
)
}
}
Working Demo: https://codeply.com/p/4EV36QjwCB

React setstate does not work in IE11 after render is called

I am new to react development and I have a react app where on the componentDidMount am setting the state of value as "add" and it renders the div content for "add" and once button click on the add div am calling an addstate method
where am setting the state of the value as "edit" and it renders the div content with respect to "edit" and where i call again the addstate method through done method call.
In this case the fetch call from addstate method is happening to the backend but the state is not setting back to edit..it fails only in IE11. It works on chrome, firefox and mobile devices.
If i remove the piece of code "Value:edit" in addstate method its working good. But my requirement needs to render based upon different scenarios. so basically am able to set the state of the result only once in IE11. it does not work repeatedly.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "test",
items: []
}
};
addState() {
fetch("/local/addThings").then(res => res.json())
.then(
(result) => {
this.setState({
value: "edit",
items: result
});
}
)
.catch(error => console.error('Error:', error));
}
done() {
this.setState({
value: "add"
});
}
componentDidMount() {
fetch("/local/getThings")
.then(
(result) => {
this.setState({
value: "add"
});
}
)
.catch(error => console.error('Error:', error));
}
render() {
const { value, items } = this.state;
if (value === "add") {
return <div >
<div >
<ul >
<li onClick={() => this.addState()}>
<div>
<img src="Add.png" />
<center><p>AddButton</p></center>
</div>
</li>
</ul>
</div>
</div>
;
}
if (value === "edit") {
return (<div>
<div >
<ul >
<li onClick={() => this.done()}>
<div >
<img src="Save.png" />
<center><p>SaveButton</p></center>
</div>
</li>
{items.map(item => (
<center><p>{item.name}</p></center>
</li>
))}
</ul>
</div>
</div>
);
}
}
}
ReactDOM.render(React.createElement(App, null), document.getElementById("details")); ```
Try binding your method and refer to it directly in your onClick event:
lass App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "test",
items: []
}
this.addState = this.addState.bind(this);
};
addState() {
fetch("/local/addThings").then(res => res.json())
.then(
(result) => {
this.setState({
value: "edit",
items: result
});
}
)
.catch(error => console.error('Error:', error));
}
done() {
this.setState({
value: "add"
});
}
componentDidMount() {
fetch("/local/getThings")
.then(
(result) => {
this.setState({
value: "add"
});
}
)
.catch(error => console.error('Error:', error));
}
render() {
const { value, items } = this.state;
if (value === "add") {
return <div >
<div >
<ul >
<li onClick={this.addState}>
<div>
<img src="Add.png" />
<center><p>AddButton</p></center>
</div>
</li>
</ul>
</div>
</div>
;
}
if (value === "edit") {
return (<div>
<div >
<ul >
<li onClick={() => this.done()}>
<div >
<img src="Save.png" />
<center><p>SaveButton</p></center>
</div>
</li>
{items.map(item => (
<center><p>{item.name}</p></center>
</li>
))}
</ul>
</div>
</div>
);
}
}
}
I added settimeout in my methods and it worked in IE. It seems like the response was slower in IE for the API calls. Not sure if there is a workaround or this is the right approach.
fetch("/local/addThings").then(res => res.json())
.then(
(result) => {
setTimeout(() => {
this.setState({
value: "edit",
items: result
});
}, 200);
}
)
.catch(error => console.error('Error:', error));
}```

How to make create/edit popup form component

I'm trying to make simple CRUD example using react.js as frontend.
I already have add/edit functionality done in a component,
but I want to call this component dynamically on click and show it as a popup or modal window on the same page without redirecting to another route.
Does anyone have experience with doing this using react.js?
This is my parent component code where I show a grid of items displaying cities:
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import { Link, NavLink } from 'react-router-dom';
interface FetchNaseljeDataState {
nasList: NaseljeData[];
loading: boolean;
}
export class FetchNaselje extends React.Component<RouteComponentProps<{}>, FetchNaseljeDataState> {
constructor() {
super();
this.state = { nasList: [], loading: true };
fetch('api/Naselje/Index')
.then(response => response.json() as Promise<NaseljeData[]>)
.then(data => {
this.setState({ nasList: data, loading: false });
});
// This binding is necessary to make "this" work in the callback
this.handleDelete = this.handleDelete.bind(this);
this.handleEdit = this.handleEdit.bind(this);
}
public render() {
let contents = this.state.loading
? <p><em>Loading...</em></p>
: this.renderNaseljeTable(this.state.nasList);
return <div>
<h1>Naselje Data</h1>
<p>This component demonstrates fetching Naselje data from the server.</p>
<p>
<Link to="/addnaselje">Create New</Link>
</p>
{contents}
</div>;
}
// Handle Delete request for an naselje
private handleDelete(id: number) {
if (!confirm("Do you want to delete naselje with Id: " + id))
return;
else {
fetch('api/Naselje/Delete/' + id, {
method: 'delete'
}).then(data => {
this.setState(
{
nasList: this.state.nasList.filter((rec) => {
return (rec.idnaselje != id);
})
});
});
}
}
private handleEdit(id: number) {
this.props.history.push("/naselje/edit/" + id);
}
// Returns the HTML table to the render() method.
private renderNaseljeTable(naseljeList: NaseljeData[]) {
return <table className='table'>
<thead>
<tr>
<th></th>
<th>ID Naselje</th>
<th>Naziv</th>
<th>Postanski Broj</th>
<th>Drzava</th>
</tr>
</thead>
<tbody>
{naseljeList.map(nas =>
<tr key={nas.idnaselje}>
<td></td>
<td>{nas.idnaselje}</td>
<td>{nas.naziv}</td>
<td>{nas.postanskiBroj}</td>
<td>{nas.drzava && nas.drzava.naziv}</td>
<td>
<a className="action" onClick={(id) => this.handleEdit(nas.idnaselje)}>Edit</a> |
<a className="action" onClick={(id) => this.handleDelete(nas.idnaselje)}>Delete</a>
</td>
</tr>
)}
</tbody>
</table>;
}
}
export class NaseljeData {
idnaselje: number = 0;
naziv: string = "";
postanskiBroj: string = "";
drzava: DrzavaData = { iddrzava: 0, naziv: ""};
drzavaid: number = 0;
}
export class DrzavaData {
iddrzava: number = 0;
naziv: string = "";
}
This is my child component that I want to dynamically show on create new link click:
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import { Link, NavLink } from 'react-router-dom';
import { NaseljeData } from './FetchNaselje';
import { DrzavaData } from './FetchNaselje';
interface AddNaseljeDataState {
title: string;
loading: boolean;
drzavaList: Array<any>;
nasData: NaseljeData;
drzavaId: number;
}
export class AddNaselje extends React.Component<RouteComponentProps<{}>, AddNaseljeDataState> {
constructor(props) {
super(props);
this.state = { title: "", loading: true, drzavaList: [], nasData: new NaseljeData, drzavaId: -1 };
fetch('api/Naselje/GetDrzavaList')
.then(response => response.json() as Promise<Array<any>>)
.then(data => {
this.setState({ drzavaList: data });
});
var nasid = this.props.match.params["nasid"];
// This will set state for Edit naselje
if (nasid > 0) {
fetch('api/Naselje/Details/' + nasid)
.then(response => response.json() as Promise<NaseljeData>)
.then(data => {
this.setState({ title: "Edit", loading: false, nasData: data });
});
}
// This will set state for Add naselje
else {
this.state = { title: "Create", loading: false, drzavaList: [], nasData: new NaseljeData, drzavaId: -1 };
}
// This binding is necessary to make "this" work in the callback
this.handleSave = this.handleSave.bind(this);
this.handleCancel = this.handleCancel.bind(this);
}
public render() {
let contents = this.state.loading
? <p><em>Loading...</em></p>
: this.renderCreateForm(this.state.drzavaList);
return <div>
<h1>{this.state.title}</h1>
<h3>Naselje</h3>
<hr />
{contents}
</div>;
}
// This will handle the submit form event.
private handleSave(event) {
event.preventDefault();
const data = new FormData(event.target);
// PUT request for Edit naselje.
if (this.state.nasData.idnaselje) {
fetch('api/Naselje/Edit', {
method: 'PUT',
body: data,
}).then((response) => response.json())
.then((responseJson) => {
this.props.history.push("/fetchnaselje");
})
}
// POST request for Add naselje.
else {
fetch('api/Naselje/Create', {
method: 'POST',
body: data,
}).then((response) => response.json())
.then((responseJson) => {
this.props.history.push("/fetchnaselje");
})
}
}
// This will handle Cancel button click event.
private handleCancel(e) {
e.preventDefault();
this.props.history.push("/fetchnaselje");
}
// Returns the HTML Form to the render() method.
private renderCreateForm(drzavaList: Array<any>) {
return (
<form onSubmit={this.handleSave} >
<div className="form-group row" >
<input type="hidden" name="idnaselje" value={this.state.nasData.idnaselje} />
</div>
< div className="form-group row" >
<label className=" control-label col-md-12" htmlFor="Naziv">Naziv</label>
<div className="col-md-4">
<input className="form-control" type="text" name="naziv" defaultValue={this.state.nasData.naziv} required />
</div>
</div >
<div className="form-group row">
<label className="control-label col-md-12" htmlFor="PostanskiBroj" >Postanski broj</label>
<div className="col-md-4">
<input className="form-control" name="PostanskiBroj" defaultValue={this.state.nasData.postanskiBroj} required />
</div>
</div>
<div className="form-group row">
<label className="control-label col-md-12" htmlFor="Drzava">Država</label>
<div className="col-md-4">
<select className="form-control" data-val="true" name="drzavaid" defaultValue={this.state.nasData.drzava ? this.state.nasData.drzava.naziv : ""} required>
<option value="">-- Odaberite Državu --</option>
{drzavaList.map(drzava =>
<option key={drzava.iddrzava} value={drzava.iddrzava}>{drzava.naziv}</option>
)}
</select>
</div>
</div >
<div className="form-group">
<button type="submit" className="btn btn-default">Save</button>
<button className="btn" onClick={this.handleCancel}>Cancel</button>
</div >
</form >
)
}
}
I'm assuming I'll have to make css for the create/edit component to make it look like a popup...
EDIT: I would appreciate if someone could make code example using my classes, thanks...
In the parent component set a state on click functionality, say for eg:
this.setState({display: true})
In the parent component render based on condition display child component, say for eg:
<div>{(this.state.display) ? <div><childComponent /></div> : ''}</div>
To display the child component in a modal/popup, put the component inside say a bootstrap or react-responsive-modal. For that, you have to install and import react-responsive-modal and then
In the render method,
return (
<div>
{this.state.toggleModal ? <div className="container">
<Modal open={this.state.toggleModal} onClose={this.onCloseModal} center>
<div className="header">
<h4>{Title}</h4>
</div>
<div className="body">
<div>
{this.state.toggleModal ? <someComponent /> : ''}
</div>
</div>
</Modal>
</div>
: null}
</div>
)
Have your popup component receive a prop from the parent that will tell it if it should be displayed or not, a simple boolean will do the trick. Then, when you want something to show the popup, just change that state in the parent.

React - Map content inside a div

Good Morning! Why does my map content stay outside the "blog--div" div?
It's getting loose on Body and I do not know why. Help-me, please!
I try to put a border around the contents of the "blog--div" but the content becomes loose, making it impossible to apply styles.
imports[...]
class Blog extends Component {
constructor(props) {
super(props)
this.state = {
post: [],
}
}
componentDidMount() {
this.setState({ isLoading: true })
fetch(`${API}`)
.then(res => res.json())
.then(res => {
this.setState({
post: [res],
isLoading: false,
})
})
}
render() {
const { isLoading } = this.state
if (isLoading) {
return <Loading />
}
return (
<div className="blog">
<p className="blog__title">Blog</p>
{this.renderBlog()}
</div>
)
}
renderBlog() {
const page = this.state.post.map((post, key) => {
return (
<div className="blog--div" key={key}>
<div className="blog__post">
<div className="blog__post--title">
<p><a target="_blank" rel="noopener noreferrer" href={post[0].link}>{post[0].title.rendered.replace('Visit.Rio', 'Projeto 1')}</a></p>
</div>
<div className="blog__post--description">
<p>{post[0].excerpt.rendered.replace('Visit.Rio', 'Projeto 1')}</p>
</div>
</div>
</div>
)
})
return page
}
}
export default Blog

React: change order list when button clicked

I am making my first app with Javascript and React and started with a page which views a shopping list. It gets the items from an api call.
If the user clicks on the button 'done' (or should I use an checkbox?) This product should go to the bottom of the list (and be grayed out with css but thats not the problem).
The problem is, I have no clue how to do this. Can anyone help me out a bit?
This is my code:
import React from 'react';
//import image from '../images/header.png';
//import Collapsible from './Collapsible';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
orders: []
}
}
componentWillMount() {
localStorage.getItem('orders') && this.setState({
orders: JSON.parse(localStorage.getItem('orders')),
isLoading: false
})
}
componentDidMount() {
if (!localStorage.getItem('orders')){
this.fetchData();
} else {
console.log('Using data from localstorage');
}
}
fetchData() {
fetch('http://localhost:54408/api/orders/all/15-03-2018')
.then(response => response.json())
.then(parsedJSON => parsedJSON.map(product => (
{
productname: `${product.ProductName}`,
image: `${product.Image}`,
quantity: `${product.Quantity}`,
isconfirmed: `${product.IsConfirmed}`,
orderid: `${product.OrderId}`
}
)))
.then(orders => this.setState({
orders,
isLoading: false
}))
.catch(error => console.log('parsing failed', error))
}
componentWillUpdate(nextProps, nextState) {
localStorage.setItem('orders', JSON.stringify(nextState.orders));
localStorage.setItem('ordersDate', Date.now());
}
render() {
const {isLoading, orders} = this.state;
return (
<div>
<header>
<img src="/images/header.jpg"/>
<h1>Boodschappenlijstje <button className="btn btn-sm btn-danger">Reload</button></h1>
</header>
<div className={`content ${isLoading ? 'is-loading' : ''}`}>
<div className="panel">
{
!isLoading && orders.length > 0 ? orders.map(order => {
const {productname, image, quantity, orderid} = order;
return<div className="product" key={orderid}>
<div className="plaatjediv">
<img className="plaatje" src={image} />
</div>
<div className="productInfo">
<p>{productname}</p>
<p>Aantal: {quantity}</p>
<p>ID: {orderid}</p>
</div>
<div className="bdone">
<button className="btn btn-sm btn-default btndone">Done</button>
</div>
</div>
}) : null
}
</div>
<div className="loader">
<div className="icon"></div>
</div>
</div>
</div>
);
}
}
export default App;
You can achieve by using this :
this.handleDoneAction = event = > {
let itemIndex = event.target.getAttribute("data-itemIndex");
let prevOrders = [...this.state.orders];
var itemToMoveAtLast = prevOrders.splice(itemIndex, 1);
var updatedOrderList = prevOrders.concat(itemToMoveAtLast);
this.setState({order: updatedOrderList})
}
I have attach an event handler on the button handleDoneAction.
<button className="btn btn-sm btn-default btndone" data-itemIndex={index} onClick={this.handleDoneAction}>Done</button>
the attribute data-itemIndex is the index of the object in orders array.
And your map function will be like this:
orders.map((order, index) => {
//content
})
ANd for the different style effects on the done products, I will suggest you to use different array for all done products.

Resources