javascript - 如果数组为空,则在 React 中进行条件渲染
问题描述
我正在建立一个电影院列表项目。有一个关于每部电影的信息块,然后是它下面的电影时间。
我有 2 个下拉菜单 - 一个选择电影,一个选择日期。我正在使用三元运算符来渲染结果,但无法得到它,因此如果电影在特定日期没有任何放映,则在选择该日期时隐藏有关电影的信息块。
我将仅发布其中一部电影的示例。
这是从列表信息中获取的 json 文件 -
[
{
"id": "film1",
"filmTitle": "Knives Out",
"paragraphText": "Lorem ipsum dolor sit amet",
"mon": ["12:00", "15:00", "19:00"],
"tue": ["13:10", "16:30", "19:00", "21:00"]
}
]
这是带有电影清单之一的 js 文件的一部分-
constructor(props){
super(props);
this.state = {
filmListings: [],
selectedFilm: "allFilms",
selectedDate: "allDates"
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event){
const {name, value} = event.target
this.setState({
[name]: value
});
}
componentDidMount() {
const FilmListings = require("./components/booking/filmTimesData.json");
this.setState({ filmListings: FilmListings })
}
render() {
const filmsArray = require("./components/booking/filmTimesData.json");
const selectedFilm = this.state.selectedFilm
const selectedDate = this.state.selectedDate
return (
<form id="searchForm">
<div id="filmDateContainer">
<div className="searchOption">
<h2>Film:</h2>
<img src={FilmSearch} alt="film icon"/>
<select
name="selectedFilm"
value={this.state.selectedFilm}
onChange={this.handleChange}
className="dropdown"
>
<option value="allFilms">All Films</option>
<option value="film1">Knives Out</option>
<option value="film2">Judy and Punch</option>
<option value="film3">Harriet</option>
</select>
</div>
<h2 id="or">OR</h2>
<div className="searchOption">
<h2>Date:</h2>
<img src={DateSearch} alt="date icon"/>
<select
name="selectedDate"
value={this.state.selectedDate}
onChange={this.handleChange}
className="dropdown"
>
<option value="mon">Monday 2nd December</option>
<option value="tue">Tuesday 3rd December</option>
<option value="wed">Wednesday 4th December</option>
</select>
</div>
</div>
<div>
{(selectedFilm === "film1" || selectedFilm === "allFilms") ?
<FilmInfo filmTitle={filmsArray[0].filmTitle} paragraphText={filmsArray[0].paragraphText}/> : " "}
{(selectedDate === "mon" || selectedDate === "allDates")
&& (selectedFilm === "film1" || selectedFilm === "allFilms") ?
<Mon day={filmsArray[0]}/> : " "
}
{(selectedDate === "tue" || selectedDate === "allDates")
&& (selectedFilm === "film1" || selectedFilm === "allFilms") ?
<Tue day={filmsArray[0]}/> : " "
}
{(selectedDate === "wed" || selectedDate === "allDates")
&& (selectedFilm === "film1" || selectedFilm === "allFilms") ?
<Wed day={filmsArray[0]}/> : " "
}
</div>
</form>
);
}
}
在此示例中,星期三没有放映电影,那么当从下拉列表中选择星期三时,如何获取不放映影片的信息块?
解决方案
我认为,不幸的是,试图将其限制在一个电影项目上而不是循环播放多个电影项目,这使事情变得更加复杂,而不是更少。似乎您正在从“某些电影有Wed
放映,因此我们需要有条件地渲染Wed
组件”的结论中倒退。换句话说,从数据点之间的差异而不是它们的共同点开始。
我们可以编写一些复杂的条件来检查特定属性是否存在,但它会非常脆弱,一旦您继续映射所有内容,您可能最终还是将其丢弃。不知道每个电影对象具有哪些特定属性并允许数据更自然地流过您的组件更有意义。
从您的 JSON 文件开始,将放映数据分组为一组更离散的属性。您现在可以轻松地访问和循环播放节目,而不是尝试按名称单独访问每个节目。
[
{
"id": "film1",
"filmTitle": "Knives Out",
"paragraphText": "Lorem ipsum dolor sit amet",
"showings": [
{"date": "mon", "times": ["12:00", "15:00", "19:00"]},
{"date": "tue", "times": ["13:10", "16:30", "19:00", "21:00"]}
]
}
]
到组件上。让我们从摆脱不必要的生命周期方法和一些渲染变量开始。由于您的数据来自静态的本地文件,因此您只需将其导入组件顶部并直接包含在constructor
. componentDidMount
只有在处理挂载时无法立即访问的数据时使用它才有意义(例如,它来自远程资源,或等待树中的某些其他组件)。我在import
这里使用语法,因为它应该在样板 React 环境中可供您使用。
import filmData from "./components/booking/filmTimesData.json";
class FilmListings extends React.Component {
constructor(props){
super(props);
this.state = {
filmListings: filmData,
selectedFilm: "allFilms",
selectedDate: "allDates"
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event){
const {name, value} = event.target
this.setState({
[name]: value
});
}
render() {
const { filmListings, selectedFilm, selectedDate } = this.state;
return (
...
);
}
}
现在渲染函数。我将不理会filmDateContainer
它,因为它本质上很好,尽管您当然希望映射选项filmListings
而不是硬编码,这在下一点之后应该会变得更加清晰。
我们将用这个映射函数替换整个div
after filmDateContainer
,它首先循环遍历每个电影对象以确保它不与selectedFilm
. 然后它循环播放该电影对象的放映,以确保每个放映对象都不会与selectedDate
. 本质上,我们创建了一个交错过滤器,它按重要性顺序嵌套数据(至关重要的是,您现在可能会注意到,按照与 JSON 文件中的结构相同的顺序)。
{filmListings.map(film =>
selectedFilm === "allFilms" || selectedFilm === film.id ? (
<div key={film.id}>
<FilmInfo filmTitle={film.filmTitle} paragraphText={film.paragraphText}/>
{film.showings.map(showing =>
selectedDate === "allDates" || selectedDate === showing.date ? (
<Day showing={showing} key={film.id + showing.date}/>
) : null)}
</div>
) : null)}
请记住,每次渲染映射函数时都要分配适当的唯一键,以便 React 可以跟踪所有内容。嵌套也已经非常密集,因此您可能需要考虑将每个映射分成自己的变量,该变量返回它负责的 JSX 小片段,特别是如果您有任何额外的条件要考虑或要映射的属性。
如果您对此有任何疑问,请发表评论,我会尽力解释。
编辑:
我认为处理您所追求的更复杂的条件逻辑的最佳方法是编写一些选择器。当您继续进行更复杂的状态管理时,您会更加熟悉这个概念,但在这里可以通过在渲染返回之前添加一些简单的函数来有效地使用它。作为奖励,它还将清理渲染返回,这非常好,因为它已经开始看起来有点难看。通过组合选择器,您可以开始以更合乎逻辑、更易读的方式推理您的状态,并定义产生特定结果的常见操作。
我也摆脱了三元组并进行了纯粹&&
的评估,因为它有点干净。
render() {
const { filmListings, selectedFilm, selectedDate } = this.state;
const allFilms = selectedFilm === 'allFilms';
const allDates = selectedDate === 'allDates';
const validDate = date => allDates || selectedDate === date;
const filmHasDate = showings => showings.some(showing => validDate(showing.date));
const validFilm = (id, showings) => (allFilms || selectedFilm === id) && filmHasDate(showings);
return (
<form>
...
{filmListings.map(film => validFilm(film.id, film.showings) && (
<div key={film.id}>
<FilmInfo filmTitle={film.filmTitle} paragraphText={film.paragraphText}/>
{film.showings.map(showing => validDate(showing.date) && (
<Day showing={showing} key={film.id + showing.date}/>
))}
</div>
))}
</form>
);
}
这种方法的一个缺点是每次组件重新渲染时都要进行所有这些计算。根据您拥有的应用程序类型、组件重新呈现的频率以及输入数组的大小,这可能会限制性能,但在绝大多数情况下,这不会成为问题。
另一种方法是只在handleChange
调用时进行一次这些计算,并将结果存储在一个新的状态变量(例如filteredListings
)中,然后您可以直接在您的渲染函数中映射该变量。但是,当您的数据集变得相当大时,您的手上会出现重复状态,并且在多个位置使用相同的数据进行同步。只是想一想!