Ad

Cannot Read Property 'modalIsOpen' Of Undefined

I want to iterate over few data with react-modal then populate those data into a modal when a button is clicked, It pops up different values based on which button is clicked. Hope this make sense? If not:

I'm iterating over few data so in each loop, I'll have a button. When I click on button one, I get some value. When I click on button two, I get different value but not same as button one's value.

var React = require('react');
var Modal = require('react-modal');

Cards = React.createClass({

getInitialState:function(){
      return{
          filteredData: this.props.data, // Im using react-rails to get this 'data'
          modalIsOpen: false
      }
  },

  openModal: function() {
    this.setState({modalIsOpen: true});
  },

  closeModal: function() {
    this.setState({modalIsOpen: false});
  },

  handleModalCloseRequest: function() {
    // opportunity to validate something and keep the modal open even if it
    // requested to be closed
    this.setState({modalIsOpen: false});
  },

  handleInputChange: function() {
    this.setState({foo: 'bar'});
  },

render(){
 var cards=[];
 this.props.data.slice(0, 25).forEach(function(s) {
 cards.push(
  <div key={s.id}>
    <button className="button tiny radius" onClick={this.openModal}>view</button>
    <Modal
      closeTimeoutMS={150}
       isOpen={this.state.modalIsOpen}
       onRequestClose={this.handleModalCloseRequest}>
       <h1>{s.title}</h1>
       <button onClick={this.closeModal}>close</button>
        <div>{s.description}</div>
         <form>
          <input onChange={this.handleInputChange} />
          <input />
          <br/>              
          <button>{s.agree}</button>
         </form>
     </Modal>
  </div>
 )
 });

 return(
  {cards}
 )
}

});
module.exports = Cards;

If I move <Modal></Modal> into the render() all works well but I need the loop of the modal. Is there a way to rewrite this?

Ad

Answer

I will first create a state currentData so I can move <Modal /> away from the loop.

getInitialState:function(){
    return{
        filteredData: this.props.data,
        modalIsOpen: false,
        currentData: {}
    }
},

I will then create a separate function to create the content of the modal, I will call it renderModalContent.

renderModalContent: function(s) {
    return (
      <div>
          <h1>{s.title}</h1>
          <div>{s.description}</div>
          <form>
              <input onChange={this.handleInputChange} />
              <button>{s.agree}</button>
          </form>
      </div>
    )
},

this will be the loop inside your render method

    var cards = this.props.data.slice(0,25).map( (s, i) => {
        return (
            <div key={s.id}>
                <button className="button tiny radius" onClick={this.openModal.bind(this,s)}>View</button>
            </div>
        );
    });

You can see that I'm binding this.openModal with this. You openModal is

openModal: function(s) {
    this.setState({modalIsOpen: true, currentData: s});
},

and it's using this.setState hence the binding. The s is the argument I'm passing to the renderModalContent function so we can pass it to this.setState({currentData}).

This will be the return value of your render method

    var currentData = this.state.currentData;
    return(
        <div>
            { cards }                
            <Modal
                closeTimeoutMS={150}
                isOpen={this.state.modalIsOpen}
                onRequestClose={this.handleModalCloseRequest}>
                {
                    Object.keys(currentData).length > 0 ?
                      this.renderModalContent(currentData) :
                      null;
                }
                <button onClick={this.closeModal}>close</button>
            </Modal>
        </div>
        })
    )

Edit: Removed bind as @Alexander and @Sylar suggested in the comments

Ad
source: stackoverflow.com
Ad