I'm using this react modal plugin: and I need to show an array of objects in the modal on page load. When the first item shows user clicks a button so isOpen prop is set to false for Modal. Each item has a prop showModal that feeds the value to isOpen for the Modal. As the user keeps clicking I keep setting the value on the current object to false and then set it true for the next object. This is all working fine but the problem is that the overlay and dialog window stays on screen and only content within the modal is updated. I would like the modal to fully close and open to show content of the next object in array. I had to strip out my code to a simplified version below:
class ProductsModal extends React.Component {
constructor(props) {
super(props);
this.remindMeHandler = this.remindMeHandler.bind(this);
this.state = {
products: [],
modalId: 0
};
}
showModals() {
let products = this.state.products;
//A different function saves the array of product objects in the state so
//I can show them one by one
let currentProduct = products[this.state.popUpId];
if (products.length > 0) {
return <ProductItemModal
product={currentProduct}
showNextPopUp={() => this.showNextPopUp(currentProduct.productId)}
showPopUp={currentProduct['showModal']}
/>;
//showModal is a boolean for each product that sets the value of isOpen
}
}
showNextPopUp() {
//All this does is sets the "showModal" property to false for current
//product and sets it to true for next product so it shows in the Modal
}
render() {
return(
<div>
{this.showModals()}
</div>
);
}
}
class ProductItemModal extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
<Modal
isOpen={this.props.showModal}
contentLabel="Product"
id={this.props.product.productId}
>
<div>
Product Data......
</div>
</Modal>
);
}
}
I'm using this react modal plugin: https://github./reactjs/react-modal and I need to show an array of objects in the modal on page load. When the first item shows user clicks a button so isOpen prop is set to false for Modal. Each item has a prop showModal that feeds the value to isOpen for the Modal. As the user keeps clicking I keep setting the value on the current object to false and then set it true for the next object. This is all working fine but the problem is that the overlay and dialog window stays on screen and only content within the modal is updated. I would like the modal to fully close and open to show content of the next object in array. I had to strip out my code to a simplified version below:
class ProductsModal extends React.Component {
constructor(props) {
super(props);
this.remindMeHandler = this.remindMeHandler.bind(this);
this.state = {
products: [],
modalId: 0
};
}
showModals() {
let products = this.state.products;
//A different function saves the array of product objects in the state so
//I can show them one by one
let currentProduct = products[this.state.popUpId];
if (products.length > 0) {
return <ProductItemModal
product={currentProduct}
showNextPopUp={() => this.showNextPopUp(currentProduct.productId)}
showPopUp={currentProduct['showModal']}
/>;
//showModal is a boolean for each product that sets the value of isOpen
}
}
showNextPopUp() {
//All this does is sets the "showModal" property to false for current
//product and sets it to true for next product so it shows in the Modal
}
render() {
return(
<div>
{this.showModals()}
</div>
);
}
}
class ProductItemModal extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
<Modal
isOpen={this.props.showModal}
contentLabel="Product"
id={this.props.product.productId}
>
<div>
Product Data......
</div>
</Modal>
);
}
}
Share
Improve this question
edited Jul 21, 2017 at 4:47
sayayin
asked Jul 21, 2017 at 4:03
sayayinsayayin
1,0416 gold badges25 silver badges43 bronze badges
5
- Your question is unclear. Can you explain what you are trying to acplish better? – Alexander Higgins Commented Jul 21, 2017 at 4:13
- @AlexanderHiggins Ok so you know how the modal opens and there is an ovelay? I want this to close everytime isOpen is set to false as I'm going thru the products. Right now it does not happen, Modal with the overlay stays open as I'm going through products. Does it makes sense? – sayayin Commented Jul 21, 2017 at 4:42
-
Your modal's
isOpen
has a prop ofshowModal
but in yourProductItemModal
I don't see you setting showModal at all. Did you mean forshowPopUp
to beshowModal
? – Simon Commented Jul 21, 2017 at 5:25 - @Simon that is ing from this.props.showModal, in the parent ponent showPopUp={currentProduct['showModal']} is what is being sent from the parent. All these changes in the state are handled in showNextPopUp() – sayayin Commented Jul 21, 2017 at 5:40
- I have troubleshooted the value for isOpen, everything is good there. There is something else I need to do here I guess. Modal opens fine and when the last product's showModal property is set to false, it closes. But in between it stays open and just refreshes the product content. – sayayin Commented Jul 21, 2017 at 5:42
4 Answers
Reset to default 4 +50Had a workaround for all your problems and created this codepen link. It would be like this,
class ProductItemModal extends React.Component {
render() {
const { showModal, product, showNextModal, onClose } = this.props;
return(
<ReactModal
isOpen={showModal}
contentLabel="Product"
onRequestClose={() => onClose()}
>
<p>
<b>Product Id</b> - {product.id}, <b>Product Name</b> - {product.name}
</p>
<button onClick={() => showNextModal()}>Next</button>
</ReactModal>
);
}
}
class ProductsModal extends React.Component {
constructor() {
super();
this.state = {
products: [
{id: 1, name: "Mac", showModal: true},
{id: 2, name: "iPhone", showModal: false},
{id: 3, name: "iPod", showModal: false},
],
modalId: 0
};
}
handleProductItemModalClose(product) {
//backdrop click or escape click handling here
console.log(`Modal closing from Product - ${product.name}`);
}
showModals() {
const { products, modalId } = this.state;
//A different function saves the array of product objects in the state so
//I can show them one by one
let currentProduct = products[modalId];
if(currentProduct) {
return <ProductItemModal
product={currentProduct}
showNextModal={() => this.showNextModal(currentProduct.id)}
showModal={currentProduct["showModal"]}
onClose={() => this.handleProductItemModalClose(currentProduct)}
/>;
//showModal is a boolean for each product that sets the value of isOpen
}
}
showNextModal(currentProductId) {
const { products, modalId } = this.state;
var isLastModal = false;
if(modalId === products.length - 1) {
isLastModal = true;
}
var clonedProducts = [...products];
var currentIndex = clonedProducts.findIndex(product => product.id === currentProductId);
var newIndex = currentIndex + 1;
clonedProducts[currentIndex].showModal = false;
if(!isLastModal) {
clonedProducts[newIndex].showModal = true;
} else {
//ment the following lines if you don't wanna show modal again from the start
newIndex = 0;
clonedProducts[0].showModal = true;
}
//All this does is sets the "showModal" property to false for current
//product and sets it to true for next product so it shows in the Modal
this.setState({
products: clonedProducts
}, () => {
this.setState({
modalId: newIndex
});
});
}
render() {
return(
<div>
{this.showModals()}
</div>
);
}
}
ReactDOM.render(<ProductsModal />, document.getElementById("main"));
Let me know if it helps.
Updated codepen: https://codepen.io/anon/pen/rzVQrw?editors=0110
You need to call setState() of ProductItemModal to close Model. Otherwise, though isOpen is changed, the UI is not re-rendered.
As you probably know that react maintain a virtual DOM and on every time state or props change it pares the difference between browser's DOM(actual dom) and virtual DOM(the one that React maintain) and in your code every time you change the isOpen property all you are doing is only changing the props of the Model ponent that's why React only update the internal content of the actual Model
to pletely close and re-open the model you need to do a small change in your code
instead of returning only one model ponent in your ProductsModal
you need to do something like this so that react know that this modal has been close and otherone has been open Key property is important for performance reason read more
class ProductsModal extends React.Component {
.
.
.
showModals() {
let products = this.state.products;
//A different function saves the array of product objects in the state so
if (products.length > 0) {
return (
//return list of all modal ponent
products.map((product) =>
<ProductItemModal
product={product}
showNextPopUp={() => this.showNextPopUp(product.productId)}
showPopUp={product['showModal']}
key={product.productId}
/>
)
);
//showModal is a boolean for each product that sets the value of isOpen
}
}
.
.
.
}
all you are doing here is just returning multiple modal and when one model gets the isOpen props as false
is close and the other witch gets true
is open and now react know that there are two different modals because of key props
Another work around is to use setTimeout
. Implementation is as follows-
class ProductItemModal extends React.Component {
render() {
const { showModal, product, selectNextProductFunc, onClose } = this.props;
return(
<ReactModal
isOpen={showModal}
contentLabel="Product"
onRequestClose={() => onClose()}
>
<p>
<b>Product Id</b> - {product.id}, <b>Product Name</b> - {product.name}
</p>
<button onClick={() => selectNextProductFunc(product)}>Next</button>
</ReactModal>
);
}
}
class ProductsModal extends React.Component {
constructor() {
super();
this.state = {
products: [
{id: 1, name: "Mac"},
{id: 2, name: "iPhone"},
{id: 3, name: "iPod"},
],
productId: null,
showModal: true,
};
}
handleProductItemModalClose(product) {
//backdrop click or escape click handling here
console.log(`Modal closing from Product - ${product.name}`);
}
showModals() {
const { products, productId, showModal} = this.state;
//A different function saves the array of product objects in the state so
//I can show them one by one
const getProduct = function(){
if(productId){
return products.find((i) => i.id === productId);
}else{
return products[0]; // first element
}
}
return <ProductItemModal
product={getProduct()}
selectNextProductFunc={this.selectNextProductFunc.bind(this)}
showModal={showModal}
onClose={() => this.handleProductItemModalClose()}
/>;
//showModal is a boolean for each product that sets the value of isOpen
}
selectNextProductFunc(currentProduct) {
const { products} = this.state;
this.setState({
showModal: false
});
const currentProductIndex = products.findIndex((i) => i.id === currentProduct.id);
const modifiedIndex = 0;
if(products[currentProductIndex + 1]){
this.setState({
productId : products[currentProductIndex + 1].id,
});
}else{
this.setState({
productId : modifiedIndex,
});
}
setTimeout(() => {
this.setState({
showModal: true
})
}, 1000);
}
render() {
return(
<div>
{this.showModals()}
</div>
);
}
}
ReactDOM.render(<ProductsModal />, document.getElementById("main"));
jsbin