如果数组为空,则在 React 中进行条件渲染

Conditional rendering in React if an array is empty

我正在构建一个电影列表项目。有一个块包含有关每部电影的信息,然后是它下面的电影时间。

我有 2 个下拉菜单 - 一个 select 一部电影,一个 select 约会。我正在使用三元运算符来呈现结果,但无法获取结果,因此如果某部电影在特定日期没有放映,则当该日期为 select 时,有关该电影的信息块将被隐藏编辑

我只是post其中一部电影的例子。

这是列表信息来自的 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>
        );
    }
}

在这个例子中,星期三没有放映电影,那么如何从下拉列表中 select 获取星期三不放映的信息块?

我认为,不幸的是,试图将它限制在一个电影项目而不是循环多个电影项目会使事情变得更复杂,而不是更少。看起来您是在倒推“有些电影有 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 on 装载才有意义(例如,它来自远程资源,或等待树中的其他组件).我在这里使用 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 中的选项而不是硬编码,这在下一段之后应该会变得更清楚。

我们将用这个映射函数替换 filmDateContainer 之后的整个 div,它首先循环每个电影对象以确保它不会与 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 可以跟踪所有内容。嵌套也已经非常密集,所以你可能想考虑将每个映射分离到它自己的变量中,return 是它负责的 JSX 小片段,特别是如果你有任何额外的条件要考虑或属性要映射.

如果您对此有任何疑问,请发表评论,我会尽力解释。

编辑:

我认为处理您所追求的更复杂的条件逻辑的最佳方法是编写一些选择器。当您继续进行更复杂的状态管理时,您会更加熟悉这个概念,但是在这里通过在渲染之前添加一些简单的函数也可以同样有效地使用它 return。作为奖励,它还将清理渲染 return 这非常好,因为它已经开始看起来有点丑了。通过组合选择器,您可以开始以更合乎逻辑、更易读的方式推理您的状态,并定义产生特定结果的常见操作。

我也摆脱了三元组并进行了纯 && 评估,因为它更干净一些。

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)中,然后您可以直接将其映射到你的渲染功能。但是,您手头上有多个相同数据的重复状态,当您的数据集变得相当大时,这可能会令人头疼地进行推理和同步。只是想一想!

在你的例子中,你可以简单地做:

{((selectedDate === "wed" && filmsArray[0][selectedDate]) || selectedDate === "allDates") 
    && (selectedFilm === "film1" || selectedFilm === "allFilms")
    &&
        <Wed day={filmsArray[0]}/>
}

它将检查您的 film 对象是否确实具有“wed”键并有条件地呈现 Wed 组件。

请注意,我放弃了 ?,因为如果

的结果
((selectedDate === "wed" && filmsArray[0][selectedDate]) || selectedDate === "allDates") 
    && (selectedFilm === "film1" || selectedFilm === "allFilms")

为false,组件不会被渲染。我只是觉得它更干净。

您可以针对每个案例执行此操作。

您的代码将如下所示:

<div>
    {(selectedFilm === "film1" || selectedFilm === "allFilms") &&
        <FilmInfo filmTitle={filmsArray[0].filmTitle} paragraphText={filmsArray[0].paragraphText}/>
    }

    {((selectedDate === "mon" && filmsArray[0][selectedDate]) || selectedDate === "allDates") 
        && (selectedFilm === "film1" || selectedFilm === "allFilms")
        &&
            <Mon day={filmsArray[0]}/>
    }

    {((selectedDate === "tue" && filmsArray[0][selectedDate]) || selectedDate === "allDates") 
        && (selectedFilm === "film1" || selectedFilm === "allFilms")
        &&
            <Tue day={filmsArray[0]}/>
    }

    {((selectedDate === "wed" && filmsArray[0][selectedDate]) || selectedDate === "allDates") 
        && (selectedFilm === "film1" || selectedFilm === "allFilms")
        &&
            <Wed day={filmsArray[0]}/>
    }
</div>

但我确实认为您应该重新构建您的解决方案,以使用更简单、更可靠的方式来显示所需的组件。符合@lawrencee-witt 的建议。

最终解决方案将受到您对该 JSON 文件的控制的严重影响。

如果你需要更多信息,我可以创建一个小的 CodePen 示例。