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>
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 setState
s 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!