0

I have a link in a modal that goes to a new page, and I can't seem to reset the state of the component with the modal when directing to that page.

The component with the modal seems to be keeping its state after directing to the new page, because when I hit the back button, it automatically opens the modal.

The modal is either opened or closed based on the state of modalIsOpen.

So I have my simplified Listings component:

import React from 'react'
import ListingModalContent from '../ListingModalContent'
import Modal from '../Modal'

export default class Listings extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      modalIsOpen: false,
      modalContent: null
    }
  }

  modalClick = (e, listing) => {
    e.preventDefault()
    this.setState({
      modalContent: <ListingModalContent listing={listing}/>
    }, () => {
      this.setState({modalIsOpen: true})
    })
  }

  modalClose = () => {
    this.setState({modalIsOpen: false})
  }

  componentWillMount() {
    this.setState({modalIsOpen: false})
    console.log('mounting...')
    console.log(this.state.modalIsOpen)
  }

  componentWillUnmount() {
    console.log('unmounting...')

    this.setState({
      modalIsOpen: false
    }, () => {
      console.log('got here...')
      console.log(this.state.modalIsOpen)
    })

    console.log(this.state.modalIsOpen)
  }

  render() {

    const listings = this.props.listings.map(listing => (<div className="listing">
      <a href="#" onClick={e => this.modalClick(e, listing)}>More Details</a>
    </div>))

    return (<div id="listings">
      <section className="listings">
        {listings}
        <Modal visible={this.state.modalIsOpen} onClose={this.modalClose}>
          {this.state.modalContent}
        </Modal>
      </section>
    </div>)
  }
}

And my ListingsModalContent component:

import React from 'react'

export default class ListingModalContent extends React.Component {
  constructor(props) {
    super(props)
  }

  render() {

    const {listing} = this.props

    return (<div className="listing-modal">
      <div className="details">
        <h2 className="address">{listing.address}</h2>
        <p className="description">{listing.description}</p>
      </div>
      <div className="btn-container">
        <a href={`/listings/${listing.slug}`} onClick={this.props.modalClose}>View Full Listing</a>
      </div>
    </div>)
  }
}

The console output is...

// after initially mounting:
mounting...
false

// after clicking the listing link:
unmounting...
true

// after hitting the back button:
mounting...
false

I'm pretty sure I need to fix this by using componentWillUnmount to set the state of modalIsOpen to false before the component unmounts, but it never seems to finish setting the state before unmounting.

I'm using react on rails, which seems to use some hybrid routing rails/react routing system, but I'm not too familiar with it, and don't want to go down that rabbit hole at the moment if I don't have to.

So my question is, if this is expected behavior of the react component lifecycle, is there a way I can ensure the state of modalIsOpen is reset before unmounting? Or is there a way I can make sure my state is reset to its initial state when going back? Or is this more likely a consequence of the routing system I'm using?

Doug
  • 1,517
  • 3
  • 18
  • 40

1 Answers1

0

This is strange, unexpected bahaviour in react and for sure is not caused by react (as @azium stated) but some 'things around', probably react_on_rails issue (or 'feature'). Report a bug/create an issue on github.

As you see in log state has proper value on mounting and there is no reason to render modal. 'Normal' react would work as expected.

There is no sense to set state on unmount - instance of component will be destroyed, its state, too.

HINTS

You shouldn't store modal content in state. It's possible, it works for simple cases, it can be used a kind of cache for parts of content, but you can have issues when conditional rerendering needed (using prop/state changes).

After setting state this.setState({modalIsOpen: true, modalContent:listing}) in click handler you can use conditional rendering (in render):

{this.state.modalIsOpen && <ListingModalContent listing={this.state.modalContent}/>}

To be true even this.setState({modalIsOpen: true}) can be removed (by save only listing idx in state, '-1' for closing) but then code can be less readable (storing additional pointer is cheap).

xadm
  • 8,219
  • 3
  • 14
  • 25