(React) 如何将模态内容与被点击的元素进行匹配?
(React) How to match the modal content with the clicked element?
我正在尝试将显示的模态与被点击的元素相匹配,现在我正在通过点击渲染所有模态,我一直试图使它们与索引相匹配但没有成功,请帮忙。
这是我的构造函数
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})
}
在这里我获取数据并呈现两个列表组件和模态
componentDidMount() {
axios.get(`data.json`)
.then(res => {
const portfolioData = [res.data.portfolio.projects.film];
this.setState({ portfolioData });
})
};
组件
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}
/>
))
和 return
<section id="portfolio">
{ portfolioList }
{ this.state.modalPost !== false ? modalList : null }
</section>
您的代码将创建与 this.state.portfolioData
保存的数据一样多的模态,这是不必要的、低效的并且可能导致渲染浪费。如果你退一步从这个角度思考,你将只渲染一个模态,但用所选项目的数据渲染它
让我们看一个例子,
我们可以从拥有一个额外的状态值 selectedValue
开始,它将保存被点击项目的值
this.state = {
portfolioData: [],
newModal:[],
modalPost: false,
isShown: false,
selectedValue: {} //<---
};
然后,当用户单击该项目时,我们可以在状态中设置该特定项目的值;具体在 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)
}
最后,您可以只渲染一个模态,而不是映射数据,它获取并显示来自 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}
/>
这只是您可以遵循的想法。之后您应该 organize/optimize 根据您的代码库编写您的代码。如果您不熟悉 setState
的第二个参数,它会将回调作为第二个参数,在更新状态后执行。参考:https://reactjs.org/docs/react-component.html#setstate
希望这对你有帮助,干杯!
编辑:我刚刚注意到您没有在任何地方使用 isShown
。这是模态应该根据的值打开。模式应该有一个道具,通过传递 true/false 使其成为 show/hide,检查文档。你应该将 this.state.isShown
传递给那个特定的 prop 来让所有东西一起工作!
问题是当您在 portfolioData 上循环时,您正在创建多个模态实例。我认为您不需要多个实例,因为您一次只会打开一个实例。
当您将模式状态设置为 isShown 时。您实际上是在生成的模态的所有实例上设置状态。因此,您最终会打开多个模式。理想情况下,您应该只有一个模态并将数据传递给模态。
你可以这样做:
首先,将模式移出循环,这样您就只有它的一个实例。
将数据传递给:
<a>onClick={() => this.toggleModal(provider.data)} key={index}>Portfolio Item</a>
最后,在toggleModal函数中先设置数据再打开模态
这样,您所有的 PortfolioItem 链接最终都会调用相同的模态实例。但是有不同的数据。设置数据后,您可以使用现有的 isShown 状态重新渲染组件。
这是一个小例子:
这样,您所有的 PortfolioItem 链接最终将调用相同的模态实例。但是有不同的数据。设置数据后,您可以使用已经存在的 isShown 状态重新渲染组件。
这是一个小例子:
class App extends React.Component {
constructor(props){
super(props);
this.state = {
isShown: false,
data: ''
}
this.list = [{data: 'data1'}, {data: 'data2'}];
}
onAnchorClick({data},event){
this.setState({data, isShown: true});
}
render(){
return(
<div>
{this.list.map((obj, idx) => <div key={idx}>
<a onClick={this.onAnchorClick.bind(this, obj)}>Portfolio Item</a>
</div>)}
<div style={{display: !this.state.isShown ? 'none' : 'block'}}>
<h3>Data: {this.state.data}</h3>
</div>
</div>
)
}
}
window.onload = () => {
ReactDOM.render(<App />, document.getElementById("app"));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
我正在尝试将显示的模态与被点击的元素相匹配,现在我正在通过点击渲染所有模态,我一直试图使它们与索引相匹配但没有成功,请帮忙。
这是我的构造函数
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})
}
在这里我获取数据并呈现两个列表组件和模态
componentDidMount() {
axios.get(`data.json`)
.then(res => {
const portfolioData = [res.data.portfolio.projects.film];
this.setState({ portfolioData });
})
};
组件
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}
/>
))
和 return
<section id="portfolio">
{ portfolioList }
{ this.state.modalPost !== false ? modalList : null }
</section>
您的代码将创建与 this.state.portfolioData
保存的数据一样多的模态,这是不必要的、低效的并且可能导致渲染浪费。如果你退一步从这个角度思考,你将只渲染一个模态,但用所选项目的数据渲染它
让我们看一个例子,
我们可以从拥有一个额外的状态值 selectedValue
开始,它将保存被点击项目的值
this.state = {
portfolioData: [],
newModal:[],
modalPost: false,
isShown: false,
selectedValue: {} //<---
};
然后,当用户单击该项目时,我们可以在状态中设置该特定项目的值;具体在 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)
}
最后,您可以只渲染一个模态,而不是映射数据,它获取并显示来自 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}
/>
这只是您可以遵循的想法。之后您应该 organize/optimize 根据您的代码库编写您的代码。如果您不熟悉 setState
的第二个参数,它会将回调作为第二个参数,在更新状态后执行。参考:https://reactjs.org/docs/react-component.html#setstate
希望这对你有帮助,干杯!
编辑:我刚刚注意到您没有在任何地方使用 isShown
。这是模态应该根据的值打开。模式应该有一个道具,通过传递 true/false 使其成为 show/hide,检查文档。你应该将 this.state.isShown
传递给那个特定的 prop 来让所有东西一起工作!
问题是当您在 portfolioData 上循环时,您正在创建多个模态实例。我认为您不需要多个实例,因为您一次只会打开一个实例。
当您将模式状态设置为 isShown 时。您实际上是在生成的模态的所有实例上设置状态。因此,您最终会打开多个模式。理想情况下,您应该只有一个模态并将数据传递给模态。
你可以这样做:
首先,将模式移出循环,这样您就只有它的一个实例。
将数据传递给:
<a>onClick={() => this.toggleModal(provider.data)} key={index}>Portfolio Item</a>
最后,在toggleModal函数中先设置数据再打开模态
这样,您所有的 PortfolioItem 链接最终都会调用相同的模态实例。但是有不同的数据。设置数据后,您可以使用现有的 isShown 状态重新渲染组件。
这是一个小例子: 这样,您所有的 PortfolioItem 链接最终将调用相同的模态实例。但是有不同的数据。设置数据后,您可以使用已经存在的 isShown 状态重新渲染组件。
这是一个小例子:
class App extends React.Component {
constructor(props){
super(props);
this.state = {
isShown: false,
data: ''
}
this.list = [{data: 'data1'}, {data: 'data2'}];
}
onAnchorClick({data},event){
this.setState({data, isShown: true});
}
render(){
return(
<div>
{this.list.map((obj, idx) => <div key={idx}>
<a onClick={this.onAnchorClick.bind(this, obj)}>Portfolio Item</a>
</div>)}
<div style={{display: !this.state.isShown ? 'none' : 'block'}}>
<h3>Data: {this.state.data}</h3>
</div>
</div>
)
}
}
window.onload = () => {
ReactDOM.render(<App />, document.getElementById("app"));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>