I am having a Component which is a Modal alert. For animating the Modal, I am using the Transition from semantic-ui-react. But only a few animations(pulse,bounce, flash) are working and that too only while mounting of Component, and not working while I close the Modal. Also the duration property is also not working.
class Alert extends React.Component {
state = {
open: true
};
close = () => {
this.setState({
open: false
});
};
render() {
const { open } = this.state;
return (
<Transition animation="fade" duration={1000} visible={open}>
<Modal
size="mini"
closeOnDimmerClick={false}
closeOnEscape={false}
open={this.state.open}
onClose={this.close}>
<Modal.Content>
<p>
Hello user
</p>
</Modal.Content>
<Modal.Actions>
<Button color="blue" onClick={this.close}>
Ok
</Button>
</Modal.Actions>
</Modal>
</Transition>
);
}
}
Why such weird behavior? What am I doing wrong?
Modal is unmounting way before Transition can animate it, one way would be to let Transition unmount the Modal:
class Alert extends React.Component {
state = {
open: true
};
close = () => {
this.setState({
open: false
});
};
render() {
const { open } = this.state;
return (
<Transition
animation="fade"
duration={1000}
unmountOnHide={true}
visible={open}
>
<Modal
size="mini"
closeOnDimmerClick={false}
closeOnEscape={false}
open={true}
onClose={this.close}
>
<Modal.Content>
<p>Hello user</p>
</Modal.Content>
<Modal.Actions>
<Button color="blue" onClick={this.close}>
Ok
</Button>
</Modal.Actions>
</Modal>
</Transition>
);
}
}
I hope it helps!
Related
I have added a click handler so that when a user clicks outside of my modal, it closes the modal box. I am using 'react-outside-click-handler' in my Gatsby.js project to achieve this.
This click handler is working perfectly and closes the modal when cliked outside of the box. However, it also toggles the modal to activate if clicked anywhere on the page (when the modal is not active).
Could someone point me in the right direction as to how to stop the activation of the modal whilst keeping the deactivation feature?
Perhaps I could write an if statement specifying that when the state is false, the outside clicks do not toggle the modal?
The page:
export default class Contact extends Component {
state = {
modal: false,
}
modalToggle = () => {
this.setState({ modal: !this.state.modal })
}
render = () => {
return (
<div className="temp_background">
<div className="work_boxes">
<OutsideClickHandler onOutsideClick={this.modalToggle}>
<button
className="place-order"
style={{ backgroundImage: `url(${work_screenshot_2})` }}
onClick={this.modalToggle}
/>
<Modal onClick={this.modalToggle} status={this.state.modal} />
</OutsideClickHandler>
</div>
</div>
)
}
}
The modal component:
export default class Modal extends Component {
render() {
return (
<div>
<div className="modal" data-status={this.props.status}>
<div
className="modal-left"
style={{ backgroundImage: `url(${work_screenshot_2})` }}
/>
<div className="modal-right">
<h2>{this.props.title}</h2>
<p>{this.props.description}</p>
<button onClick={this.props.onClick} className="close">
<span className="fa fa-close">x</span>
</button>
</div>
</div>
</div>
)
}
}
You could conditionally render it but you should also look at being more explicit with your function calls here. Using a toggleModal call may be more concise, but being less explicit comes at a cost.
By having two separate methods: openModal and closeModal, you can more clearly see what your code is doing. Now, your OutsideClickHandler component explicitly closes the modal if you click outside of it. The methods additionally check to make sure the modal is opened/closed before making changes.
export default class Contact extends Component {
state = {
modal: false,
}
openModal = () => {
const { modal } = this.state;
if (!modal) {
this.setState({ modal: true })
}
}
closeModal = () => {
const { modal } = this.state;
if (modal) {
this.setState({ modal: false })
}
}
render = () => {
return (
<div className="temp_background">
<div className="work_boxes">
<OutsideClickHandler onOutsideClick={this.closeModal}>
<button
className="place-order"
style={{ backgroundImage: `url(${work_screenshot_2})` }}
onClick={this.openModal}
/>
<Modal onClick={this.closeModal} status={this.state.modal} />
</OutsideClickHandler>
</div>
</div>
)
}
}
You could conditionally render it:
render = () => {
return (
{this.state.modal ? (<div className="temp_background">
<div className="work_boxes">
<OutsideClickHandler onOutsideClick={this.modalToggle}>
<button
className="place-order"
style={{ backgroundImage: `url(${work_screenshot_2})` }}
onClick={this.modalToggle}
/>
<Modal onClick={this.modalToggle} status={this.state.modal} />
</OutsideClickHandler>
</div>
</div>) : null }
)
}
As #LMulvey points out, the proper way would be to have separate open and close handlers, and it should definitely be done that way in more complicated situations. But for this simple case, a toggle is fine I think.
I struck in the code that trying to display the products in modal body but not displaying. when i click on image it showing in console but not getting an idea how to display products in modal body. For to display in modal i used react-bootstrap. modal pop up is displaying when i click on image and it displaying header and footer. But in modal body i want to display the product details which i clicked. can any one help ..
import React, { Component } from "react";
import { Grid, Row, Col, Image, Button, Modal } from "react-bootstrap";
import AddToCartView from "./AddToCartView";
// import ProductView from './ProductView';
class ProductList extends Component {
constructor(props){
super(props);
this.state = {
show : false
}
}
handleShow = (item) => {
this.getProductDetails(item);
this.setState({
show : true
})
}
handleClose = () => {
this.setState({
show : false
})
}
getProductDetails = (prod) => {
// console.log(id,'clicked');
console.log(prod);
console.log(prod.title);
console.log(prod.id);
console.log(prod.price);
// let click = document.getElementsByTagName(Image.id);
// console.log(click);
// const { viewProducts } = this.props
// console.log(viewProducts);
// viewProducts.map((prod,id) => {
// console.log(prod.ptype);
// })
}
// console.log(viewProducts,'viewProducts');
render(){
const { viewProducts } = this.props;
return (
<div className="list-container">
<div className="mobile-list">
<h3> Showing { viewProducts.length } mobiles </h3>
</div>
<Grid>
<Row>
{viewProducts.map((item, key) => (
<Col xs={8} md={4} lg={4} key={item.id}>
<figure>
<Image onClick={() => this.handleShow(item) } src={item.image}
thumbnail />
<figcaption>{item.title}</figcaption>
<figcaption>
<label>Rs. </label>
{item.price}
</figcaption>
</figure>
<Button bsStyle="primary" onClick={this.props.onChange}>
<i className="fa fa-shopping-cart" />
Add
</Button>
<hr />
</Col>
))}
</Row>
</Grid>
<Modal show={this.state.show} onHide={() => this.handleClose()}>
<Modal.Header closeButton>
<Modal.Title>Product Details</Modal.Title>
</Modal.Header>
<Modal.Body>
{/* <ProductView displayProductView= { (item) =>
this.props.displayProductDetails(item) }/> */}
</Modal.Body>
<Modal.Footer>
<Button onClick={() => this.handleClose()}>Close</Button>
</Modal.Footer>
</Modal>
</div>
);
};
}
export default ProductList;
You are aware the ProductView component is commented out within the Modal Body right?
I'm not seeing where you pass the item to ProductView. Most likely your ProductView is getting nothing to display. Maybe you could change render of ProductView to just render a 'Hello' to see if that is the case.
I am trying to make a modal component which I can reuse, but I don't get what I am doing wrong here. The Modal is not appearing. Can anyone help me out?
Little explanation about my app.
This app is loading a JSON url and shows a list of products, which can be marked as done. If you click the div plaatjediv you should get a popup (the modal) with details info over the clicked product.
EDIT: Edited the code as suggested here. I can see the state change to true and false if I click the div, but the Modal is still not appearing.
my code
App.js
import React from 'react';
import ProductModal from './ProductModal.js';
class App extends React.Component {
constructor(props) {
super(props);
this.toggleModal = this.toggleModal.bind(this);
this.state = {
isLoading: true,
orders: [],
dealtOrders: [],
open: false
}
}
toggleModal() {
this.setState({
open: !this.state.open
});
}
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/26-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))
}
render() {
this.handleDoneAction = event =>
{
let itemIndex = event.target.getAttribute("data-itemIndex");
let prevOrders = [...this.state.orders];
let dealtOrders = [...this.state.dealtOrders];
const itemToMoveAtLast = prevOrders.splice(itemIndex, 1);
const addToDealtOrders = dealtOrders.concat(itemToMoveAtLast);
this.setState({dealtOrders: addToDealtOrders});
this.setState({orders: prevOrders});
};
this.handleUndoAction = event =>
{
let itemIndex = event.target.getAttribute("data-itemIndex");
let orders = [...this.state.orders];
let dealtOrders = [...this.state.dealtOrders];
const undoDealtOrder = dealtOrders.splice(itemIndex, 1);
const addToOrders = orders.concat(undoDealtOrder);
this.setState({orders: addToOrders});
this.setState({dealtOrders: dealtOrders});
};
const {isLoading, orders, dealtOrders,open} = this.state;
return (
<div>
<header>
<img src="/images/header.jpg"/>
<h1>Boodschappenlijstje <button className="btn btn-sm btn-danger">Reload</button></h1>
</header>
<ProductModal open={open} />
<div className={`content ${isLoading ? 'is-loading' : ''}`}>
<div className="panel">
{
!isLoading && orders.length > 0 ? orders.map((order, index) => {
const {productname, image, quantity, orderid} = order;
return<div className="product" key={orderid}>
<div className="plaatjediv" onClick={this.toggleModal}>
<img className="img-responsive" src={image} />
</div>
<div className="productInfo">
<p>{productname}</p>
<p>Aantal: {quantity}</p>
</div>
<div className="bdone">
<button className="btn btn-lg btn-default btndone" data-itemIndex={index} onClick={this.handleDoneAction}>Done</button>
</div>
</div>
}) : null
}
</div>
<h2>Mandje</h2>
<div className="panel">
{
!isLoading && dealtOrders.length > 0 ? dealtOrders.map((dorder, index) => {
const {productname, image, quantity, orderid} = dorder;
return<div className="productDone" key={index}>
<div className="plaatjediv">
<img className="img-responsive" src={image} />
</div>
<div className="productInfo">
<p>{productname}</p>
<p>Aantal: {quantity}</p>
</div>
<div className="bdone">
<button className="btn btn-lg btn-default btndone" data-itemIndex={index} onClick={this.handleUndoAction}>Undo</button>
</div>
</div>
}) : null
}
</div>
<div className="loader">
<div className="icon"></div>
</div>
</div>
</div>
);
}
} export default App;
ProductModal.js
import React from 'react';
class ProductModal extends React.Component {
constructor() {
super();
}
render() {
const open = this.props.open;
return (
<div className={'modal fade'+(open ? '' : 'hide')} tabindex="-1" role="dialog">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 className="modal-title">test</h4>
</div>
<div className="modal-body">
test
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" className="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
)
}
}
export default ProductModal;
I am unsure what your issue is from your question but I am guessing your model doesn't open?
When you set state, you need to set it to the opposite of this.state.open
You can do it like this:
toggleModal() {
this.setState({
open: !this.state.open
});
}
I can't see where the modal is supposed to be rendered. You have to add it to render function of your "App" class. like this:
render() {
...
return(
<ProductModal open={true} />
...
):
}
and also, in your toggleModal function, do something like this:
this.setState({ open: !this.state.open});
Hope this solves the issue.
The issue is that you do not have your <ProductModal /> as a component in your <App /> In addition to setting your open state, once shown, it will (or should) never hide because you will not be able to toggle it again using your button, and you also do not have any keybindings within your <ProductModal /> itself.
I would suggest you bind an event listener within <ProductModal /> to
Check is ESC key is pressed
Bind a Cancel/Close button (in addition to a header x button).
Listen for if anywhere outside of your dialog is clicked, dismiss the modal.
You will also need to pass a handler from <App /> down to <ProductModal /> to notify when the modal has been closed.
In your App.js
handleClose() {
this.setState({
open: false
});
}
render() {
return (
...
<ProductModal open={this.state.open} handleClose={this.handleClose.bind(this)} />
)
}
Then in your ProductModal.js
handleClose() {
this.props.handleClose();
}
Observe the following using my sandbox:
https://stackblitz.com/edit/react-98m4cr
You'll see that I've implemented the handleClose event to control the state back up to the parent. In addition, you may want to add listeners as mentioned above, all triggering handleClose in the end; just remember to unbind them in ProductModal.js componentWillUnmount.
class Posts extends Component {
constructor(){
super();
this.state = {
modalIsOpen:false
};
this.openModal = this.openModal.bind(this);
this.afterOpenModal = this.afterOpenModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
openModal() {
console.log("got here?")
this.setState({modalIsOpen: true});
}
afterOpenModal() {
// references are now sync'd and can be accessed.
this.subtitle.style.color = '#f00';
}
closeModal() {
this.setState({modalIsOpen: false});
}
render() {
const keys = generateKey(new Date().getTime())
var dictionary = this.props.posts
const postItemsArr = Object.keys(dictionary).map(post=>dictionary[post])
const number = 0
const postItems = postItemsArr.map(
post=>(
<Jumbotron key={generateKey(post.positiontitle) + generateKey(post.businessId)} >
<div className="position">{post.positiontitle}</div><br></br>
<BusinessName businessnameType={post.businessname} /><br></br>
<JobDescription jobDescription={post.description_sanitized} /><br></br>
<p>
<Modal isOpen={this.state.modalIsOpen}
onAfterOpen={this.afterOpenModal}
onRequestClose={this.closeModal}
style={customStyles}
contentLabel="Example"
>
<h2 ref={subtitle => this.subtitle = subtitle}>Hello</h2>
<button onClick={this.closeModal}>close</button>
<div>I am a modal</div>
<form>
<input />
<button>tab navigation</button>
<button>stays</button>
<button>inside</button>
<button>the modal</button>
</form>
</Modal>
<button onClick={this.openModal}>Open Modal</button>
</p>
</Jumbotron>
)
)
return (
<div>
<h1> Jobs Listings </h1>
{postItems }
</div>
);
}
}
Inside my Jumbotron, my modal doesn't seem to be opening, why?
It looks like it gets to the state openModal()
but doesn't actually open the modal when the user clicks on
<button onClick={this.openModal}>Open Modal</button>
Also; should I create a separate component called Modal; what would be best practices?
Overall I'm trying to trigger the modal object for a respective list item in the list.
I would create a new presentational component called modal, then use that when you need it, rather than do what you're trying to do now, which is pass an open/close state. The Modal can be a simple presentational component that just accepts props. By doing it this way, you create reusability.
function Modal(props) {
return (
<div className={props.css.awesomeLayout}>
{props.aProp}
</div>
)
}
ReactDOM is not working with refs.
I want to sting html <b>abcd</b> append to body of modal, but it's not working.
when I check console.log() of modal and modalBody, it return null.
My code in under:
class Header extends Component {
constructor(props) {
super(props);
this.state = {
this.state.modalSendResult: false
};
}
_open() {
this.setState({
modal: !this.state.modal
});
if (!this.state.modal) {
this.appendNode();
}
}
appendNode() {
let modal = ReactDOM.findDOMNode(this.refs.modal)
modalBody = ReactDOM.findDOMNode(this.refs.body);
ReactDOM.render(
<b>abcde</b>, modalBody
);
}
render() {
return (
<Button onClick={this._open.bind(this)}
className="btn btn-primary btn-primary-1 mr-1">
<i className="fa fa-paper-plane-o" aria-hidden="true"></i> Open
</Button>
<Modal ref="modal" isOpen={this.state.modal} toggle={this._open}
className={'modal-primary modal_customer'}>
<ModalHeader toggle={this._open}>
Danh sách kết quả gửi
</ModalHeader>
<ModalBody className="modal-send" ref="body">
</ModalBody>
</Modal>
);
}
}
Please help me!
Take a look at ReactDOM. You can avoid having to call findDOMNode by setting up your refs as specified here: Refs and the DOM.
I think in your case it would look something like
<Modal
ref={(modal) => { this.modal = modal; }}
isOpen={this.state.modal}
toggle={this._open}
className={'modal-primary modal_customer'}
>
<ModalHeader toggle={this._open}>
Danh sách kết quả gửi
</ModalHeader>
<ModalBody
className="modal-send"
ref={(body) => { this.body = body; }}
/>
</Modal>
Then you should be able to reference them with
this.modal
this.body
Alternatively, you could pass the content you want for your ModalBody component as a prop.
<ModalBody
className="modal-send"
content="abcde"
ref={(body) => { this.body = body; }}
/>
And render in your ModalBody component:
render() {
return (
<b>{this.props.content}</b>
);