Search code examples
reactjsfrontendreact-modal

(React) How to match the modal content with the clicked element?


I'm trying to match the modal that shows with the clicked element, right now i'm rendering all the modals with the click, I been trying to make them match with the index but no luck , please help.

here are my constructors

constructor(props) {
    super(props);
    this.state = {
        portfolioData: [],
        newModal:[],
        modalPost: false,
        isShown: false
    };
} 

showModal = (i) =>{        
    this.setState({ isShown: true, modalPost: true })
} 

closeModal = () => {
    this.setState({isShown:false, modalPost: false})
}

and here I get the data and render two list component and the modal

componentDidMount() {
    axios.get(`data.json`)
        .then(res => {
            const portfolioData = [res.data.portfolio.projects.film];
            this.setState({ portfolioData });                  
        })
};

the components

const portfolioList = this.state.portfolioData.map((value) => 
    value.map((val, idx) =>             
        <PortfolioItem 
            id={val.title.en.toString().toLowerCase().split(" ").join("-")}
                title={val.title.en}
                imgsrc={val.imgsrc}
                status={val.status}
                profile={val.profile}
                director={val.director}
                production={val.production}
                showModal={this.showModal}
                youtube={val.trailer}
            />
))

const modalList = this.state.portfolioData.map((value) => 
    value.map((val, idx) =>             
        <Modal
            id={val.title.en.toString().toLowerCase().split(" ").join("-")}
            title={val.title.en}
            imgsrc={val.imgsrc}
            status={val.status}
            profile={val.profile}
            director={val.director}
            production={val.production}
            closeModal={this.closeModal}
            youtube={val.trailer}
        />                
    ))     

and the return

<section id="portfolio">                      
    { portfolioList }
    { this.state.modalPost !== false ? modalList : null }                
</section>

Solution

  • Your code will make as many modal as the data held by this.state.portfolioData, which is unnecessary, inefficient and may result into wasted rendering. If you take a step back and think from this way, You are going to render only one modal but render it with the data of the selected item

    Lets see an example,

    We can start by having an additional state value selectedValue which will hold the clicked item's value

    this.state = {
      portfolioData: [],
      newModal:[],
      modalPost: false,
      isShown: false,
      selectedValue: {}  //<---
    };
    

    Then, when the user clicks the item we can set that particular items value in the state; specifically in selectedValue

    const portfolioList = this.state.portfolioData.map((value) => 
        value.map((val, idx) =>             
            <PortfolioItem 
                id={val.title.en.toString().toLowerCase().split(" ").join("-")}
                    title={val.title.en}
                    imgsrc={val.imgsrc}
                    status={val.status}
                    profile={val.profile}
                    director={val.director}
                    production={val.production}
                    showData={() => this.showData(val)}   //<---
                    youtube={val.trailer}
                />
    ))
    
    //new function for one specific task   <---
    const showData = (value) => {
      this.setState({selectedValue: value}, this.showModal)
    }
    

    Finally, instead of mapping over the data you can render only one modal which takes and show the data from the this.state.selectedValue

    <Modal
     id={this.state.selectedValue.title.en.toString().toLowerCase().split(" ").join("-")}
     title={this.state.selectedValue.title.en}
     imgsrc={this.state.selectedValue.imgsrc}
     status={this.state.selectedValue.status}
     profile={this.state.selectedValue.profile}
     director={this.state.selectedValue.director}
     production={this.state.selectedValue.production}
     closeModal={this.closeModal}
     youtube={this.state.selectedValue.trailer}
    /> 
    

    This is merely an idea you can follow. You should organize/optimize your code later per your codebase afterwards. If you are unfamiliar with setStates second parameter it takes a callback as a second parameter which it executes after updating state. Reference: https://reactjs.org/docs/react-component.html#setstate

    Hope this helps you, Cheers!

    Edit: I just noticed you are not using that isShown anywhere. That is the value that the modal should be opened based on. The modal should have a prop which makes it show/hide by passing true/false, check the documentation. And you should pass this.state.isShown to that specific prop to make everything come together and work!