反应交互性:编辑、过滤、条件渲染

当我们接近React之旅的终点时(至少现在是这样),我们将在Todo列表应用程序的主要功能领域中添加收尾工作。这包括允许您编辑现有任务,并在所有任务、已完成任务和未完成任务之间筛选任务列表。我们将在此过程中研究条件UI呈现。

前提条件:

熟悉核心HTML格式,CSS公司、和JavaScript脚本语言,终端/命令行.

目标: 了解React中的条件渲染和实现列表我们的应用程序中的过滤和编辑UI。

编辑任务名称

我们还没有用于编辑任务名称的用户界面。我们马上就要谈到这一点。首先,我们至少可以实现编辑任务()中的函数附录.jsx。它将类似于删除任务()因为这需要一个身份证件找到它的目标对象,但它还需要一个新名称包含要将任务更新到的名称的属性。我们将使用Array.prototype.map()而不是Array.prototype.filter()因为我们想返回一个带有一些更改的新数组,而不是从数组中删除一些内容。

添加编辑任务()内部功能<应用程序/>组件,与其他功能位于同一位置:

jsx格式
功能 编辑任务(身份证件,新名称) {
  常数编辑的任务列表=任务.地图((任务) => {
    //如果此任务与编辑的任务具有相同的ID
    如果 (身份证件===任务.身份证件) {
      //复制任务并更新其名称
      返回 { ...任务, 名称:新名称};
    }
    //如果不是编辑的任务,则返回原始任务
    返回任务;
  });
  设置任务(编辑的任务列表);
}

通过编辑任务到我们的<托多/>组件作为道具删除任务:

jsx格式
常数任务列表=任务.地图((任务) => (
  <托多
    身份证件={任务.身份证件}
    名称={任务.名称}
    完整的={任务.完整的}
    钥匙={任务.身份证件}
    切换任务已完成={切换任务完成}
    删除任务={删除任务}
    编辑任务={编辑任务}
  />
));

现在打开Todo.jsx公司。我们将进行一些重构。

用于编辑的UI

为了允许用户编辑任务,我们必须为他们提供一个用户界面使用状态进入<托多/>组件,就像我们之前对<应用程序/>组件:

jsx格式
进口 {使用状态}  “反应”;

我们将使用此设置正在编辑默认值为的状态。在顶部添加以下行<托多/>组件定义:

jsx格式
常数 [正在编辑,集合编辑] = 使用状态();

接下来,我们将重新考虑<托多/>组件。从现在开始,我们希望它显示两个可能的“模板”中的一个,而不是迄今为止使用的单个模板:

  • “视图”模板,当我们只是查看待办事项时;到目前为止,这是我们在教程中使用的。
  • 编辑待办事项时的“编辑”模板。我们即将创建此。

将此代码块复制到托多()功能,在您的使用状态()挂钩,但高于返回声明:

jsx格式
常数编辑模板= (
  <形式 类名="堆叠小">
    <div公司 类名="表格组">
      <标签 类名="待办事项标签" 用于={道具.身份证件}>的新名称{道具.名称}
      </标签>
      <输入 身份证件={道具.身份证件} 类名="待办事项文本" 类型="文本" />
    </div公司>
    <div公司 类名="btn-群">
      <按钮 类型="按钮" 类名="btn待取消">取消<跨度 类名="视觉上的隐藏">重命名{道具.名称}</跨度>
      </按钮>
      <按钮 类型="提交" 类名="btn btn_primary待编辑">保存<跨度 类名="视觉上的隐藏">的新名称{道具.名称}</跨度>
      </按钮>
    </div公司>
  </形式>
);
常数视图模板= (
  <div公司 类名="小堆">
    <div公司 类名="c-cb型">
      <输入
        身份证件={道具.身份证件}
        类型="复选框"
        默认选中={道具.完整的}
        onChange(更改时)={() =>道具.切换任务完成(道具.身份证件)}
      />
      <标签 类名="待办事项标签" html用于={道具.身份证件}>
        {道具.名称}
      </标签>
    </div公司>
    <div公司 类名="btn-群">
      <按钮 类型="按钮" 类名="英国电信公司">编辑<跨度 类名="视觉上的隐藏">{道具.名称}</跨度>
      </按钮>
      <按钮
        类型="按钮"
        类名="btn btn危险"
        onClick(单击)={() =>支柱.删除任务(支柱.身份证件)}>删除<跨度 类名="视觉上的隐藏">{道具.名称}</跨度>
      </按钮>
    </div公司>
  </div公司>
);

我们现在已经在两个独立的常量中定义了两种不同的模板结构——“编辑”和“视图”。这意味着返回的声明<托多/>现在是重复的-它还包含“视图”模板的定义。我们可以使用条件渲染以确定组件返回的模板,并因此在UI中呈现。

有条件渲染

在JSX中,我们可以使用一个条件来更改浏览器呈现的内容。要在JSX中编写条件,可以使用三元运算符.

在我们的情况下<托多/>组件,我们的条件是“此任务正在编辑吗?”更改返回内部声明托多()所以它看起来是这样的:

jsx格式
返回 < 类名="待办事项">{正在编辑?编辑模板:视图模板}</>;

浏览器应该像以前一样呈现所有任务。要查看编辑模板,必须更改默认值正在编辑状态来自真的现在在您的代码中;我们将在下一节中查看如何使用编辑按钮切换此项!

切换<托多/>模板

最终,我们已经准备好让我们的最后一个核心功能具有交互性。首先,我们想打电话给设置编辑()值为真的当用户在我们的视图模板,以便我们可以切换模板。

更新中的“编辑”按钮视图模板如此:

jsx格式
<按钮 类型="按钮" 类名="英国电信公司" onClick(单击)={() => 集合编辑(真的)}>编辑<跨度 类名="视觉上的隐藏">{道具.名称}</跨度>
</按钮>

现在我们将添加相同的onClick(单击)处理程序中的“取消”按钮编辑模板,但这次我们要正在编辑这样它会将我们切换回视图模板。

更新中的“取消”按钮编辑模板如此:

jsx格式
<按钮
  类型="按钮"
  类名="btn待取消"
  onClick(单击)={() => 集合编辑()}>取消<跨度 类名="视觉上的隐藏">重命名{道具.名称}</跨度>
</按钮>

有了这段代码,您应该能够按todo项中的“编辑”和“取消”按钮在模板之间切换。

eat todo项显示视图模板,并提供编辑和删除按钮

显示编辑模板的eat todo项,带有输入字段以输入新名称,以及可用的取消和保存按钮

下一步是实际实现编辑功能。

从UI编辑

我们将要做的很多事情都将反映我们在其中所做的工作表单.jsx:当用户在我们的新输入字段中键入时,我们需要跟踪他们输入的文本;一旦他们提交表单,我们需要使用回调属性用任务的新名称更新状态。

我们将从制作一个新的挂钩开始,用于存储和设置新名称。还在Todo.jsx公司,将以下内容放在现有挂钩下方:

jsx格式
常数 [新名称,设置新名称] = 使用状态("");

接下来,创建一个手柄更改()设置新名称的函数;把这个放在钩子下面,但放在模板之前:

jsx格式
功能 处理更改(e(电子)) {
  设置新名称(e(电子).目标.价值);
}

现在我们将更新我们的编辑模板<输入/>字段,设置价值的属性新名称并将我们的handleChange()函数到其onChange(更改时)事件。更新如下:

jsx格式
<输入
  身份证件={道具.身份证件}
  类名="待办事项文本"
  类型="文本"
  价值={新名称}
  onChange(更改时)={处理更改}
/>

最后,我们需要创建一个函数来处理编辑表单的onSubmit(提交时)事件。在下面添加以下内容handleChange():

jsx格式
功能 handleSubmit(处理提交)(e(电子)) {e(电子).预防违约();道具.编辑任务(道具.身份证件,新名称);
  设置新名称("");
  集合编辑();
}

记住我们的编辑任务()回调属性需要我们正在编辑的任务的ID及其新名称。

将此函数绑定到窗体的提交事件,添加以下内容on提交处理程序到编辑模板<表格>:

jsx格式
<形式 类名="小堆" onSubmit(提交时)={手柄提交}>

现在您应该可以在浏览器中编辑任务了。此时,您的Todo.jsx公司文件应如下所示:

jsx格式
功能 托多(道具) {
  常数 [正在编辑,集合编辑] = 使用状态();
  常数 [新名称,设置新名称] = 使用状态("");

  功能 处理更改(e(电子)) {
    设置新名称(e(电子).目标.价值);
  }

  功能 handleSubmit(处理提交)(e(电子)) {e(电子).预防违约();道具.编辑任务(道具.身份证件,新名称);
    设置新名称("");
    集合编辑();
  }

  常数编辑模板= (
    <形式 类名="小堆" onSubmit(提交时)={handleSubmit(处理提交)}>
      <div公司 类名="表格组">
        <标签 类名="待办事项标签" html用于={道具.身份证件}>的新名称{道具.名称}
        </标签>
        <输入
          身份证件={支柱.身份证件}
          类名="待办事项文本"
          类型="文本"
          价值={新名称}
          onChange(更改时)={处理更改}
        />
      </div公司>
      <div公司 类名="btn组">
        <按钮
          类型="按钮"
          类名="btn待取消"
          onClick(单击)={() => 集合编辑()}>取消<跨度 类名="视觉上的隐藏">重命名{道具.名称}</跨度>
        </按钮>
        <按钮 类型="提交" 类名="btn btn_primary待编辑">保存<跨度 类名="视觉上的隐藏">的新名称{道具.名称}</跨度>
        </按钮>
      </div公司>
    </形式>
  );

  常数视图模板= (
    <div公司 类名="小堆">
      <div公司 类名="c-cb型">
        <输入
          身份证件={道具.身份证件}
          类型="复选框"
          默认选中={道具.完整的}
          onChange(更改时)={() =>道具.切换任务完成(道具.身份证件)}
        />
        <标签 类名="待办事项标签" html用于={道具.身份证件}>
          {道具.名称}
        </标签>
      </div公司>
      <div公司 类名="btn-群">
        <按钮
          类型="按钮"
          类名="英国电信公司"
          onClick(单击)={() => {
            集合编辑(真的);
          }}>编辑<跨度 类名="视觉上的隐藏">{道具.名称}</跨度>
        </按钮>
        <按钮
          类型="按钮"
          类名="btn btn危险"
          onClick(单击)={() =>道具.删除任务(道具.身份证件)}>删除<跨度 类名="视觉上的隐藏">{支柱.名称}</跨度>
        </按钮>
      </div公司>
    </div公司>
  );

  返回 < 类名="待办事项">{is编辑?编辑模板:视图模板}</>;
}

出口 违约托多;

返回过滤器按钮

现在我们的主要功能已经完成,我们可以考虑我们的过滤器按钮了。目前,他们重复“All”标签,没有任何功能!我们将重新应用我们在<托多/>组件到:

  • 创建用于存储活动过滤器的挂钩。
  • 渲染数组<过滤器按钮/>元素,允许用户在all、completed和incomplete之间更改活动筛选器。

添加过滤器挂钩

将新挂钩添加到您的应用程序()读取和设置过滤器的函数。我们希望默认过滤器为全部因为我们所有的任务最初都应该显示:

jsx格式
常数 [滤波器,设置过滤器] = 使用状态(“全部”);

定义过滤器

我们现在的目标是双重的:

  • 每个过滤器应具有唯一的名称。
  • 每个过滤器应该具有唯一的行为。

JavaScript对象是将名称与行为关联起来的好方法:每个键都是过滤器的名称;每个属性都是与该名称关联的行为。

在的顶部附录.jsx低于我们的进口但高于我们的应用程序()函数,让我们添加一个名为过滤器_MAP:

jsx格式
常数 过滤器_MAP = {
  全部: () => 真的,
  活动: (任务) => !任务.完整的,
  完整的: (任务) =>任务.完整的,
};

的值过滤器_MAP是我们将用于筛选任务数据数组:

  • 这个全部筛选器显示所有任务,因此我们返回真的用于所有任务。
  • 这个活动筛选器显示其任务完整的道具是.
  • 这个完整的筛选器显示其任务完整的道具是真的.

在前面添加的内容下面,添加以下内容-这里我们使用Object.keys()方法来收集数组过滤器名称:

jsx格式
常数 过滤器名称 =对象.钥匙(过滤器_MAP);

注:我们在我们的外部定义这些常量应用程序()因为如果在函数中定义了它们,则每次<应用程序/>组件重新读取,我们不希望这样。无论我们的应用程序做什么,此信息都不会更改。

渲染过滤器

现在我们有了过滤器名称数组中,我们可以使用它来渲染所有三个过滤器。在内部应用程序()函数,我们可以创建一个名为过滤器列表,我们将使用它映射名称数组并返回<过滤器按钮/>组件。记住,我们这里也需要钥匙。

在您的下面添加以下内容任务列表常量声明:

jsx格式
常数过滤器列表= 过滤器名称.地图((名称) => (
  <过滤器按钮 钥匙={名称} 名称={名称} />
));

现在我们将替换重复的三个<过滤器按钮/>中的附录.jsx用这个过滤器列表.更换以下部件:

jsx格式
<过滤器按钮 />
<过滤器按钮 />
<过滤器按钮 />

有了这个:

jsx格式
{过滤器列表}

这还不行。我们首先还有一点工作要做。

交互式过滤器

为了使我们的过滤按钮具有交互性,我们应该考虑它们需要使用什么道具。

  • 我们知道<过滤器按钮/>应该报告它当前是否被按下,如果它的名称与过滤器状态的当前值匹配,则应该按下。
  • 我们知道<过滤器按钮/>需要回调才能设置活动筛选器。我们可以直接利用我们的设置过滤器挂钩。

更新您的过滤器列表常数如下:

jsx格式
常数过滤器列表= 过滤器名称.地图((名称) => (
  <过滤器按钮
    钥匙={名称}
    名称={名称}
    已按下={名称===滤波器}
    设置过滤器={设置过滤器}
  />
));

就像我们之前对我们的<托多/>组件,我们现在必须更新过滤器按钮.jsx使用我们提供的道具。执行以下每一项操作,并记住使用花括号来读取这些变量!

  • 更换全部的具有{props.name}.
  • 设置的值咏叹调{props.isPressed}.
  • 添加onClick(单击)调用的处理程序props.setFilter()使用过滤器的名称。

所有这些都完成了,你的过滤器按钮.jsx文件应如下所示:

jsx格式
功能 过滤器按钮(道具) {
  返回 (
    <按钮
      类型="按钮"
      类名="btn切换-btn"
      咏叹调={道具.已按下}
      onClick(单击)={() =>道具.设置过滤器(道具.名称)}>
      <跨度 类名="视觉上的隐藏">显示</跨度>
      <跨度>{道具.名称}</跨度>
      <跨度 类名="视觉上的隐藏">任务</跨度>
    </按钮>
  );
}

出口 违约过滤器按钮;

再次访问浏览器。您应该看到,不同的按钮被赋予了各自的名称。当你按下过滤按钮时,你会看到它的文本呈现在一个新的大纲上——这告诉你它已经被选中了。如果您在单击按钮时查看DevTool的页面检查器,您将看到咏叹调属性值会相应更改。

应用程序的三个筛选按钮-全部、活动和已完成-围绕已完成的重点突出显示

然而,我们的按钮实际上并没有过滤UI中的todo!让我们完成这件事。

在UI中筛选任务

现在,我们的任务列表常数in应用程序()映射任务状态并返回新的<托多/>组件。这不是我们想要的!只有在应用选定过滤器的结果中包含任务时,才应渲染该任务。在映射任务状态之前,我们应该对其进行筛选(使用Array.protype.filter())消除我们不想渲染的对象。

更新您的任务列表如此:

jsx格式
常数任务列表=任务.滤波器(过滤器_MAP[滤波器])
  .地图((任务) => (
    <托多
      身份证件={任务.身份证件}
      名称={任务.名称}
      完整的={任务.完整的}
      钥匙={任务.身份证件}
      切换任务完成={切换任务完成}
      删除任务={删除任务}
      编辑任务={编辑任务}
    />
  ));

为了决定在中使用哪个回调函数Array.prototype.filter(),我们访问中的值过滤器_MAP它对应于我们的过滤器状态的键。当过滤器全部例如,FILTER_MAP[过滤器]将评估为()=>真.

现在,在浏览器中选择筛选器将删除不符合其条件的任务。列表上方标题中的计数也将更改以反映列表!

带有过滤器按钮的应用程序。“活动”将高亮显示,因此仅显示活动的待办事项。

总结

就这样,我们的应用程序现在功能齐全。然而,现在我们已经实现了所有的功能,我们可以做一些改进,以确保更广泛的用户可以使用我们的应用程序。我们的下一篇文章通过在React中包含焦点管理来完善我们的React教程,这可以提高可用性并减少键盘用户和屏幕阅读器用户的混淆。