Home > Back-end >  Trouble displaying data in React modal
Trouble displaying data in React modal

Time:02-18

I'm new to React and I am currently working on a project where I am building a single page app using React. The goal of the project is to have a map of an area that will be hosting a winery/wine vendor tour, and on the map have buttons the user can click/tap to display info about each vendor/vineyard.

I am currently stuck trying to get the modal to display the info of the specified vendor/vineyard button that the user clicks, i.e. the user clicks the button for Vendor 1, and the modal pops up and displays the relevant info for Vendor 1. The same for Vendor 2 and so on. Currently the modal is not displaying anything except for a button to close the modal. My question is how do I get the info for each vendor/vineyard to display for the corresponding button on click?

I've tried including .map method in the return in Vendor.js, but that displays all the vendor/vineyard info for every one in the modal.

My current file structure is:

App
  |_src
    |_components
      |_Button
        |_Popup
          |_Vendor

Vendor.js:

import React from './react';

const Vendor = () => {

    let vendors = [
        {
            id: 1,
            title: 'Vendor 1 Name',
        },
        {
            id: 2,
            title: 'Vendor 2 Name'
        }
    ]

    const [vendorsList, setVendorsList] = useState([]);

    useEffect(() => {
        setVendorsList([...vendors]);
    }, []);

    return (
        <div className='card' key={vendors.id}>
            <h3>{vendors.title}</h3>
            <h3>{vendors.hours}</h3>
        </div>
    )
}
    
export default Vendor;

App.js:

function App() {
    return(
        <div className='App'>
            <Map />
            <Info />
        </div>
        <div className='app__button'>
            <Button />
        </div>
    );
}

Button.js:

const Button = () => {
    const [buttonPopup, setButtonPopup] = useState(false);

    return(
        <div className='popup'>
            <button onClick={() => setButtonPopup(true)}>Button</button>
            <Popup
                trigger={buttonPopup}
                setTrigger={setButtonPopup}
            />
        </div>
    )
}

Popup.js:

const Popup = (props) => {
    return (props.trigger) ? (
        <div className='popup-inner'>
            <button className='close-button' onClick={() => props.setTrigger(false)}>X</button>
            {props.children}
            <Vendor />
        </div>
    ) : "";
};

If there is anything else that I can include to help, I'd be happy to. TIA!

CodePudding user response:

So you have a list of vendors with ids associated, the best way to get the data into the modal would be to pass the vendor Id to the popup, then get the vendor from the list with the matching id from inside the popup.

You would do this by making the vendor list that is currently in the Vendor component live outside the component and be exported so but can use it everywhere. Then, the vendor component takes in an id and gets the vendor with that id by doing:

const vendor = Vendors.find(vendor => vendor.Id === Id);

Then you render … vendor.title … … vendor.hours … In your Vendor component.

I hope this helps, I write it on my phone so please lmk if you have questions!

CodePudding user response:

The current problem is that in the Vendors component, the component's render method given an array of vendors and tries to get data from on that array, rather than on a single object. <h3>{vendors.title}</h3> is not going to return the title, because vendors is an array, not a single object with a property for title.

That is why nothing renders. However, the issue is that you can't just iterate on that list in the Vendor component, because right now that component does not know which vendor to display. A parent ought to pass in a vendor object into this child component. Here is an example!

If each Button opens a modal for a specific vendor, VendorList could live higher in the component structure to pass down the necessary information, like so:

function App() {
    let vendors = [
        {
            id: 1,
            title: 'Vendor 1 Name',
        },
        {
            id: 2,
            title: 'Vendor 2 Name'
        }
    ]

    return(
        <div className='App'>
            <Map />
            <Info />
        </div>
        
        {/* create 1 button per vendor */}
        {vendors.map(vendor => (
          <div className='app__button'>
            <Button vendor={vendor} />
          </div>
        ))}
    );
}

// button now receives a single vendor on the props obj
const Button = (props) => {
    const [buttonPopup, setButtonPopup] = useState(false);

    return(
        <div className='popup'>
            <button onClick={() => setButtonPopup(true)}>Button</button>
            <Popup
                trigger={buttonPopup}
                setTrigger={setButtonPopup}
                vendor={props.vendor}
            />
        </div>
    )
}

// keep passing the vendor in the props...
const Popup = (props) => {
    return (props.trigger) ? (
        <div className='popup-inner'>
            <button className='close-button' onClick={() =>   
               props.setTrigger(false)}>X</button>
            {props.children}
            <Vendor vendor={props.vendor}/>
        </div>
    ) : "";
};

// until finally the Vendor component receives the individual vendor it should display
const Vendor = (props) => {
    return (
        <div className='card' key={props.vendor.id}>
            <h3>{props.vendor.title}</h3>
            <h3>{props.vendor.hours}</h3>
        </div>
    )
}

This much prop passing isn't always ideal, but it's a good way to learn how to move data across React components! Hope this helps!

CodePudding user response:

It look like you made a mistake in your Vendor.js file, you have an Array of vendors but you are displaying it like if it was a single object. You have to make a map over it like this :

end of Vendor.js:

    return (
        <>
          {vendors.map((vendor) => (
            <div className="card" key={vendor.id}>
              <h3>{vendor.title}</h3>
              <h3>{vendor.hours}</h3>
            </div>
          ))}
        </>
    )

But if you want to get one button by vendor and display on popup the info from that vendor, you should do :

Vendor.js:

import React from './react';

const Vendor = ({vendor}) => {

    return (
        <div className='card' key={vendor.id}>
            <h3>{vendor.title}</h3>
            <h3>{vendor.hours}</h3>
        </div>
    )
}
    
export default Vendor;

App.js:

function App() {
    let vendors = [
        {
            id: 1,
            title: 'Vendor 1 Name',
        },
        {
            id: 2,
            title: 'Vendor 2 Name'
        }
    ]

    const [vendorsList, setVendorsList] = useState([]);

    useEffect(() => {
        setVendorsList([...vendors]);
    }, []);

    return(
        <div className='App'>
            <Map />
            <Info />
        </div>
        <div className='app__button'>
            {vendors.map((vendor) => (<Button vendor={ vendor } />)}
        </div>
    );
}

Button.js:

const Button = ({vendor}) => {
    const [buttonPopup, setButtonPopup] = useState(false);

    return(
        <div className='popup'>
            <button onClick={() => setButtonPopup(true)}>Button</button>
            <Popup
                trigger={buttonPopup}
                setTrigger={setButtonPopup}
                vendor={ vendor }
            />
        </div>
    )
}

Popup.js:

const Popup = (props) => {
    return (props.trigger) ? (
        <div className='popup-inner'>
            <button className='close-button' onClick={() => props.setTrigger(false)}>X</button>
            {props.children}
            <Vendor vendor={ props.vendor }/>
        </div>
    ) : "";
};
  • Related