valheru
valheru

Reputation: 143

Reactjs component modal onclick div

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">&times;</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;

Upvotes: 0

Views: 2783

Answers (3)

Napoli
Napoli

Reputation: 1403

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

  1. Check is ESC key is pressed
  2. Bind a Cancel/Close button (in addition to a header x button).
  3. 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.

Upvotes: 1

Amir
Amir

Reputation: 21

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.

Upvotes: 2

Stretch0
Stretch0

Reputation: 9241

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
    });
}

Upvotes: 2

Related Questions