0

I'm trying to create a modal in React that displays some information when a button in App.js is clicked.

When I put the modal code in App.js, it successfully populated the data (albeit for every li at once).

I moved the modal to it's own component, but I'm getting Cannot read property 'name' of undefined.

Here's my App.js code:

const APIURL = 'https://api.openbrewerydb.org/breweries?';
const citySearch = 'by_city=boston';

class App extends Component {
  constructor(props) {
    super(props)

    this.handleShow = this.handleShow.bind(this);
    this.handleClose = this.handleClose.bind(this);

    this.state = {
      error: null,
      isLoaded: false,
      breweries: [],
      show: false
    };
  }

  handleClose() {
    this.setState({ show: false });
  }

  handleShow() {
    this.setState({ show: true });
  }

  componentDidMount() {
    fetch(APIURL + citySearch)
      .then(res => res.json())
      .then(
        (result) => {
          this.setState({
            isLoaded: true,
            breweries: result
          });
        },
        (error) => {
          this.setState({
            isLoaded: true,
            error
          });
        }
      )
  }

  render() {
    const { error, isLoaded, breweries } = this.state;
    if (error) {
      return <div>Error: {error.message}</div>;
    } else if (!isLoaded) {
      return <div>Loading...</div>;
    } else {
    return (
      <div>
        <ul>
        {breweries.map(brewery => (
          <div className="brewery-card">
          <li key={brewery.id}>
          <p id="brewery-name">{brewery.name}</p>
          <p id="brewery-type">{brewery.brewery_type}</p> 
          <p id="brewery-address">{brewery.street} {brewery.city}, {brewery.state} {breweries.postal_code} </p>
          <button onClick={this.handleShow}>Details</button>
          <a href={brewery.website_url} target="_blank" rel="noopener noreferrer">Visit Site</a>
          </li>
          </div>
        ))}
      </ul>
      <DetailsModal props={this.props.brewery} show={this.state.modalShow} />
      </div>
     );
    }
  }
}

export default App;

And here's my component code, which isn't receiving props:

import React, { Component } from 'react';
import { Modal, Button } from 'react-bootstrap'

class DetailsModal extends Component {
    constructor(props) {
        super(props);

        this.state = {
            show: false
        };
    }
    render() {

        const brewery = this.props.brewery;

        return (
            <Modal {...this.props}>
            <Modal.Header closeButton>
              <Modal.Title>{brewery.name}</Modal.Title>
            </Modal.Header>
            <Modal.Body>{brewery.street} {brewery.city}, {brewery.state} {brewery.postal_code}</Modal.Body>
            <Modal.Footer>
              <Button variant="secondary" onClick={this.handleClose}>
                Close
              </Button>
            </Modal.Footer>
          </Modal>
        )
    }
}

  export default DetailsModal;

What am I missing that is preventing props from App.js from being used in DetailsModal.js?

James
  • 411
  • 1
  • 4
  • 18

1 Answers1

1

You are passing props with the wrong name (or accessing it wrong)

This

<DetailsModal props={this.props.brewery} show={this.state.modalShow} />

Should be

//              \/ here
<DetailsModal brewery={this.props.brewery} show={this.state.modalShow} />

When you do const brewery = this.props.brewery; you are trying to access brewery from props but you didn't provided brewery in the props, only props and show.

Here is an example to clarify what is happening.

Here you are passing someProp to ComponentX

Inside ComponentX this.props will be an object with the passed props, and in this case, it will be

{
    someProp: "something"
}

So this.props.someProp return "something"

If you try to access this.props.someOtherProp it will return undefined. And if you try to access this.props.someOtherProp.X you will get an error of annot read property 'X' of undefined

If you are having problems to understand how props works, please look at the examples from the docs.

Edit:

The other reason why you are getting the error is that in App this.props.brewery doesn't exist.

Only thing related to brewery or breweries is in your state.

So that is another error. And you need to specify what you want to display in DetailsModal

Edit 2:

To display the brewery when you click it, you need to keep in the state the value of the clicked brewery and pass that to the modal

  1. Add to the App state currentBrewery

    this.state = {
        error: null,
        isLoaded: false,
        breweries: [],
        show: false,
        currentBrewery: {}
    };
    
  2. Add to the onClick to set currentBrewery as the brewery from the breweries

    <button onClick={() => this.handleShow(brewery)}>Details</button>
    

    And

    handleShow(brewery) {
        this.setState({ show: true, currentBrewery: brewery });
    }
    
  3. Give DetailsModal the currentBrewery that is in your state

    <DetailsModal brewery={this.state.currentBrewery} show={this.state.modalShow} />
    
Vencovsky
  • 28,550
  • 17
  • 109
  • 176
  • Thanks. I'm still getting `can't read property 'name' of undefined` though, I'm reading the docs now, but I'm guessing the error now lies in my `DetailsModal.js`? – James May 09 '19 at 17:33
  • 1
    `this.state.breweries` is an array. You want display only the clicked brewery ? I know how to solve the problem, but don't know what is the result you want – Vencovsky May 09 '19 at 17:40
  • `DetailsModal` for each individual brewery. Thanks! – James May 09 '19 at 17:41
  • I thought that `constructor(props) super(props)` made `this.props.brewery` valid? I'm not sure what goes on under the hood there and will look it up. – James May 09 '19 at 17:43
  • 1
    I think [this](https://stackoverflow.com/a/34995257/9119186) will help you – Vencovsky May 09 '19 at 17:44
  • 1
    You want to display the modal only when you click at an brewery? – Vencovsky May 09 '19 at 17:45
  • Yes, there's a `details` button that's generated for each brewery, and I'd like to set it up so that once its clicked, the modal appears. – James May 09 '19 at 17:46
  • 1
    @James I think now it's working for you, just edited – Vencovsky May 09 '19 at 17:55
  • Thanks @Vencovsky, while the page is loading now, the modal doesn't actually show. I'm looking to see if there's a mismatch in method calls or something – James May 09 '19 at 17:57
  • 1
    Change `show={this.state.showModal}` to `show={this.state.show}` – Vencovsky May 09 '19 at 18:05
  • Thank you very much. The modal isn't overlaying as it should, but that'll be another post if I can't figure it out – James May 09 '19 at 18:08