30天入坑React ---------------day10 Interactivity

这篇文章是30天React系列的一部分 。

在本系列中,我们将从非常基础开始,逐步了解您需要了解的所有内容,以便开始使用React。如果您曾经想学习React,那么这里就是您的最佳选择!

下载免费的PDF

30天的React Mini-Ebook

互动

在Github上编辑此页面

今天,我们将介绍如何为我们的应用程序添加交互性,使其具有吸引力和动态性。

通过这一点,我们构建了少数几个组件,而没有增加太多的用户交互。今天,我们要改变这一点。

用户互动

浏览器是一个事件驱动的应用程序。用户在浏览器中执行的所有操作都会触发事件,从单击按钮到甚至只是移动鼠标。在纯的JavaScript中,我们可以监听这些事件并附加的JavaScript函数来与它们进行交互。

例如,可以我们mousemove使用JS 将函数附加到浏览器事件:

export const go = () => {
  const ele = document.getElementById('mousemove');
  ele.innerHTML = 'Move your mouse to see the demo';
  ele.addEventListener('mousemove', function(evt) {
    const { screenX, screenY } = evt;
    ele.innerHTML = '<div>Mouse is at: X: ' +
          screenX + ', Y: ' + screenY +
                    '</div>';
  })
}

这导致以下功能:

移动鼠标以查看演示

在阵营中,我们不必在原始的JavaScript中与浏览器的事件循环交互,因为阵营为我们提供了一种处理事件的方法props

例如,mousemove要从React上面的(相当不起眼的)演示中监听事件,我们将设置prop onMouseMove(注意事件名称的camelcasing)。

<div onMouseMove={(evt) => console.log(evt)}>
  Move the mouse over this text
</div>

反应了提供很多最props我们可以设置来监听不同的浏览器事件,例如点击,触摸,拖动,滚动,选择事件等等(参阅请事件文档以电子杂志所有这些事件的列表)。

要查看其中的一些实际操作,的英文以下props我们可以传递给我们的元素的一些小_ECSHOP演示。列表中的每个文本元素都会设置它列出的道具。尝试使用列表并查看在元素中如何调用和处理事件(所有事件都在文本上设置,而不是列表项):

将我们在onClick整个应用程序中使用支柱相当多,所以熟悉它是个好主意。在我们的活动列表标题中,我们有一个搜索图标,我们还没有连接到显示搜索框。

我们想要的交互的英文<input />在用户点击搜索图标种植时显示搜索。回想一下,的我们Header组件的英文这样实现的:

class Header extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      searchVisible: false
    }
  }

  // toggle visibility when run on the state
  showSearch() {
    this.setState({
      searchVisible: !this.state.searchVisible
    })
  }

  render() {
    // Classes to add to the <input /> element
    let searchInputClasses = ["searchInput"];

    // Update the class array if the state is visible
    if (this.state.searchVisible) {
      searchInputClasses.push("active");
    }

    return (
      <div className="header">
        <div className="menuIcon">
          <div className="dashTop"></div>
          <div className="dashBottom"></div>
          <div className="circle"></div>
        </div>

        <span className="title">
          {this.props.title}
        </span>

        <input
          type="text"
          className={searchInputClasses.join(' ')}
          placeholder="Search ..." />

        {/* Adding an onClick handler to call the showSearch button */}
        <div
          onClick={this.showSearch.bind(this)}
          className="fa fa-search searchIcon"></div>
      </div>
    )
  }
}

用户当单击该<div className="fa fa-search searchIcon"></div>元素时,我们将要运行一个函数来更新组件的状态,以便searchInputClasses更新对象。使用onClick处理程序,这非常简单。

让我们使这个组件有状态(它需要跟踪搜索字段是否应该显示)。我们可以使用constructor()函数将我们的组件转换为有状态:

class Header extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      searchVisible: false
    }
  }
  // ...
}

的英文什么constructor功能?

在JavaScript的中,该constructor函数的英文在创建³³对象时运行的函数。它返回对创建实例的对象函数的引用prototype

简单来说,构造函数是在JavaScript的运行时创建新对象时运行的函数。我们将使用构造函数方法在对象上设置实例变量,因为它在创建对象时正确运行。

使用ES6类语法创建³³对象时,必须我们super()在任何其他方法之前调用该方法。调用该super()函数调用父类的constructor()函数。我们将使用相同的参数调用它,我们因为调用constructor()了类的函数。

当用户单击该按钮时,将我们要更新状态以表示该searchVisible标志已更新。由于我们希望用户能够在<input />第二次单击搜索图标种植后关闭/隐藏字段,因此将我们e月刊状态而不是将其设置为真。

让我们创建这个方法来绑定我们的单击事件:

class Header extends React.Component {
  // ...
  showSearch() {
    this.setState({
      searchVisible: !this.state.searchVisible
    })
  }
  // ...
}

最后,我们可以onClick在icon元素上附加一个单击处理程序(使用prop)来调用我们的新showSearch()方法。我们Header组件的整个更新源代码如下所示:

class Header extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      searchVisible: false
    }
  }

  // toggle visibility when run on the state
  showSearch() {
    this.setState({
      searchVisible: !this.state.searchVisible
    })
  }

  render() {
    // Classes to add to the <input /> element
    let searchInputClasses = ["searchInput"];

    // Update the class array if the state is visible
    if (this.state.searchVisible) {
      searchInputClasses.push("active");
    }

    return (
      <div className="header">
        <div className="fa fa-more"></div>

        <span className="title">
          {this.props.title}
        </span>

        <input
          type="text"
          className={searchInputClasses.join(' ')}
          placeholder="Search ..." />

        {/* Adding an onClick handler to call the showSearch button */}
        <div
          onClick={this.showSearch.bind(this)}
          className="fa fa-search searchIcon"></div>
      </div>
    )
  }
}

尝试单击搜索图标并观察输入字段的显示和消失(动画效果由CSS动画处理)。

输入事件

每当我们在React中构建表单时,我们将使用React提供的输入事件。最值得注意的是,我们最常使用onSubmit()onChange()道具。

让我们更新我们的搜索框演示,以便在更新时捕获搜索字段内的文本。每当一个<input />字段具有onChange()prop设置时,它将在每次字段改变时调用该函数。当我们点击它并开始输入时,将调用该函数。

使用此prop,我们可以捕获我们州的字段值。

我们不是更新我们的<Header />组件,而是创建一个包含<form />元素的新子组件。通过将表单处理职责移动到它自己的表单,我们可以简化<Header />代码,当我们的用户提交表单时,我们可以调用标题的父级(这是通常的React模式)。

让我们创建一个我们称之为的新组件SearchForm。这个新组件是一个有状态组件,因为我们需要保持搜索输入的值(跟踪它的变化):

class SearchForm extends React.Component {
  // ...
  constructor(props) {
    super(props);

    this.state = {
      searchText: ''
    }
  }
  // ...
}

现在,我们已经在<Header />组件中编写了表单的HTML ,所以让我们从Header组件中获取它并从我们的SearchForm.render()函数返回它:

class SearchForm extends React.Component {
  // ...
  render() {
    const { searchVisible } = this.state;
    let searchClasses = ['searchInput']
    if (searchVisible) {
      searchClasses.push('active')
    }

    return (
      <form className='header'>
        <input
          type="search"
          className={searchClasses.join(' ')}
          onChange={this.updateSearchInput.bind(this)}
          placeholder="Search ..." />

        <div
          onClick={this.showSearch.bind(this)}
          className="fa fa-search searchIcon"></div>
      </form>
    );
  }
}

请注意,我们在我们的<input />领域丢失了样式。由于我们searchVisible的新组件中不再具有状态,因此我们不能再使用它来设置样式<input />但是,我们可以从我们的Header组件传递一个prop,它告诉SearchForm将输入呈现为可见。

让我们定义searchVisibleprop(PropTypes当然使用)并更新render函数以使用新的prop值来显示(或隐藏)搜索<input />。我们还将字段可见性的默认值设置为false(因为我们的Header显示/隐藏它很好):

class SearchForm extends React.Component {
  static propTypes = {
    onSubmit: PropTypes.func.isRequired,
    searchVisible: PropTypes.bool
  }
  // ...
}

 

现在我们将样式返回到<input />元素上,让我们添加用户在搜索框中键入的功能,我们将要捕获搜索字段的值。我们可以通过将onChangeprop添加到<input />元素并向其传递函数来实现此工作流程,以便每次<input />更改元素时调用它。

class SearchForm extends React.Component {
  // ...
  updateSearchInput(e) {
    const val = e.target.value;
    this.setState({
      searchText: val
    });
  }
  // ...
  render() {
    const { searchVisible } = this.state;
    let searchClasses = ['searchInput']
    if (searchVisible) {
      searchClasses.push('active')
    }

    return (
      <form className='header'>
        <input
          type="search"
          className={searchClasses.join(' ')}
          onChange={this.updateSearchInput.bind(this)}
          placeholder="Search ..." />

        <div
          onClick={this.showSearch.bind(this)}
          className="fa fa-search searchIcon"></div>
      </form>
    );
  }
}

当我们输入字段时,updateSearchInput()将调用该函数。我们将通过更新状态来跟踪表单的价值。在updateSearchInput()函数中,我们可以直接调用this.setState()以更新组件的状态。

该值保存在event对象的目标上event.target.value

class SearchForm extends React.Component {
  // ...
  updateSearchInput(e) {
    const val = e.target.value;
    this.setState({
      searchText: val
    });
  }
  // ...
}

受控制与不受控制

我们正在创建所谓的不受控制的组件,因为我们没有设置<input />元素的值。我们现在无法对输入文本值进行任何验证或后处理。

如果我们想要验证字段或操纵<input />组件的值,我们必须创建所谓的受控组件,这实际上只意味着我们使用valueprop 传递一个值。受控组件版本的render()功能如下所示:

class SearchForm extends React.Component {
  render() {
    return (
      <input
        type="search"
        value={this.state.searchText}
        className={searchInputClasses}
        onChange={this.updateSearchInput.bind(this)}
        placeholder="Search ..." />
    );
  }
}

截至目前,我们无法实际提交表单,因此我们的用户无法真正搜索。让我们改变这一点。我们要将<input />组件包装在<form />DOM元素中,以便我们的用户可以按回车键提交表单。我们可以通过使用元素onSubmit上的prop 来捕获表单提交<form />

让我们更新render()函数以反映这一变化。

class SearchForm extends React.Component {
  // ...
  submitForm(e) {
    e.preventDefault();

    const {searchText} = this.state;
    this.props.onSubmit(searchText);
  }
  // ...
  render() {
    const { searchVisible } = this.props;
    let searchClasses = ['searchInput']
    if (searchVisible) {
      searchClasses.push('active')
    }

    return (
      <form onSubmit={this.submitForm.bind(this)}>
        <input
          type="search"
          className={searchClasses.join(' ')}
          onChange={this.updateSearchInput.bind(this)}
          placeholder="Search ..." />
      </form>
    );
  }
}

我们马上打电话event.preventDefault()给这个submitForm()功能。这会阻止浏览器冒泡事件,这会导致整个页面的默认行为重新加载(浏览器提交表单时的默认功能)。

现在,当我们输入<input />字段并按Enter键时,将submitForm()使用事件对象调用该函数。

所以...很好,我们可以提交表格和内容,但我们什么时候才能进行搜索?出于演示的目的,现在,我们会通过搜索文本了亲子部分构成的链,因此Header可以决定什么搜索。

SearchForm组件当然不知道它在搜索什么,所以我们必须将责任传递给链。我们将使用这个回调策略。

为了将搜索功能传递给链,我们SearchForm需要接受一个支柱函数来在提交表单时调用。让我们定义一个我们称之为onSubmit可以传递给我们SearchForm组件的道具。作为优秀的开发人员,还将我们为此功能添加默认prop值和a 。由于我们要确保定义,我们将把道具设置为必需的道具:propTypeonSubmitonSubmit()onSubmit

class SearchForm extends React.Component {
  static propTypes = {
    onSubmit: PropTypes.func.isRequired,
    searchVisible: PropTypes.bool
  }
  // ...
  static defaultProps = {
    onSubmit: () => {},
    searchVisible: false
  }
  // ...
}

提交表单时,我们可以直接从中调用此函数props。由于我们在状态中跟踪搜索文本,可以我们使用searchText状态中的值调用函数,因此onSubmit()函数只获取值而不需要处理事件。

class SearchForm extends React.Component {
  // ...
  submitForm(event) {
    // prevent the form from reloading the entire page
    event.preventDefault();
    // call the callback with the search value
    this.props.onSubmit(this.state.searchText);
  }
}

现在,当用户按下回车键时,可以我们通过我们的组件调用此onSubmit()函数。propsHeader

可以我们SearchForm在组件中使用这个组件,Header并将它们传递给我们定义的两个道具(searchVisibleonSubmit):

import React from 'react';
import SearchForm from './SearchFormWithSubmit'

class Header extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      searchVisible: false
    }
  }

  // toggle visibility when run on the state
  showSearch() {
    this.setState({
      searchVisible: !this.state.searchVisible
    })
  }

  render() {
    // Classes to add to the <input /> element
    let searchInputClasses = ["searchInput"];

    // Update the class array if the state is visible
    if (this.state.searchVisible) {
      searchInputClasses.push("active");
    }

    return (
      <div className="header">
        <div className="menuIcon">
          <div className="dashTop"></div>
          <div className="dashBottom"></div>
          <div className="circle"></div>
        </div>

        <span className="title">
          {this.props.title}
        </span>

        <SearchForm
          searchVisible={this.state.searchVisible}
          onSubmit={this.props.onSubmit} />

        {/* Adding an onClick handler to call the showSearch button */}
        <div
          onClick={this.showSearch.bind(this)}
          className="fa fa-search searchIcon"></div>
      </div>
    )
  }
}

export default Header

现在我们有一个搜索表单组件,可以我们在我们的应用程序中使用状语从句:重用当然,我们实际上还没有搜索任何东西。让我们解决这个问题并实施搜索。

要在我们的组件中实现搜索,需要我们搜索将职责从我们的Header组件再次传递给我们将调用的容器组件Panel

首先,我们让实现从子Panel容器中的容器到Header组件的回调到父组件的相同模式。

Header组件上,我们让更新propTypes一个我们将定义为支柱的道具onSearch

class Header extends React.Component {
  // ...
}
Header.propTypes = {
  onSearch: PropTypes.func
}

Header组件的'submitForm()'函数内部,调用此onSearch()丙我们将传递给它:

class Header extends React.Component {
  // ...
  submitForm(val) {
    this.props.onSearch(val);
  }
  // ...
}
Header.propTypes = {
  onSearch: PropTypes.func
}

请注意,我们的虚拟树看起来像这样:

<Panel>
  <Header>
    <SearchForm></SearchForm>
  </Header>
</Panel

<SearchForm />更新时,它会传递它对搜索输入对其父级的更改的感知<Header />,它将当向上网站传递给<Panel />组件时,此方法在作出反应程序应用中非常常见,并为我们的组件提供了一组良好的功能隔离。

回到Panel我们在第7构建天的组件中,将我们函数传递给Header作为onSearch()支柱的函数Header。我们在这里说的是,当提交搜索表单时,我们希望搜索表单回调到标题组件,调用然后Panel组件来处理搜索。

由于Header组件不控制内容列表,Panel组件确实如此,我们必须将责任再提高一级,正如我们在此定义的那样。

无论如何,的我们Panel组件基本上的英文Content我们之前处理过的组件的副本:

class Panel extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      loading: false, // <~ set loading to false
      activities: data,
      filtered: data,
    }
  }

  componentDidMount() {this.updateData();}
  componentWillReceiveProps(nextProps) {
    // Check to see if the requestRefresh prop has changed
    if (nextProps.requestRefresh !== this.props.requestRefresh) {
      this.setState({loading: true}, this.updateData);
    }
  }

  handleSearch = txt => {
    if (txt === '') {
      this.setState({
        filtered: this.state.activities
      })
    } else {
      const { activities } = this.state
      const filtered = activities.filter(a => a.actor && a.actor.login.match(txt))
      this.setState({
        filtered
      })
    }
  }

  // Call out to github and refresh directory
  updateData() {
    this.setState({
      loading: false,
      activities: data
    }, this.props.onComponentRefresh);
  }

  render() {
    const {loading, filtered} = this.state;

    return (
      <div>
        <Header
          onSubmit={this.handleSearch}
          title="Github activity" />
        <div className="content">
          <div className="line"></div>
          {/* Show loading message if loading */}
          {loading && <div>Loading</div>}
          {/* Timeline item */}
          {filtered.map((activity) => (
            <ActivityItem
              key={activity.id}
              activity={activity} />
          ))}

        </div>
      </div>
    )
  }
}

我们让我们更新的状态以所有游戏一个searchFilter字符串,它只是搜索值:

class Panel extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      loading: false,
      searchFilter: '',
      activities: []
    }
  }
}

为了实际处理搜索,我们需要将一个onSearch()函数传递给我们的Header组件。让我们onSearch()Panel组件中定义一个函数,并将其传递给函数中的Header道具render()

class Panel extends React.Component {
  // ...
  // after the content has refreshed, we want to
  // reset the loading variable
  onComponentRefresh() {this.setState({loading: false});}

  handleSearch(val) {
    // handle search here
  }

  render() {
    const {loading} = this.state;

    return (
      <div>
        <Header
          onSearch={this.handleSearch.bind(this)}
          title="Github activity" />
        <Content
          requestRefresh={loading}
          onComponentRefresh={this.onComponentRefresh.bind(this)}
          fetchData={this.updateData.bind(this)} />
      </div>
    )
  }
}

在我们这里所做的就是添加一个handleSearch()函数并将其传递给标题。现在,当用户在搜索框中键入时,将handleSearch()调用我们Panel组件上的函数。

实际要实现搜索,需要我们跟踪此字符串并更新我们的updateData()功能以考虑搜索过滤。首先,我们让设置searchFilter状态。我们还可以Content通过设置loading为真来强制重新加载数据,因此我们可以一步完成:

class Panel extends React.Component {
  // ...
  handleSearch(val) {
    this.setState({
      searchFilter: val,
      loading: true
    });
  }
  // ...
}

最后,我们让我们更新的updateData()功能以考虑搜索

class SearchableContent extends React.Component {
  // ...
      this.setState({loading: true}, this.updateData);
  // ...
}

虽然这可能看起来很复杂,它但实际上几乎与我们现有的updateData()函数完全相同,除了我们更新fetch()查询查询结果以调用filter()JSON集合上的方法。

所有collection.filter()函数都运行为每个元素传入的函数,并过滤报道查看虚假值的值,保留返回真值的值。我们的搜索功能只是在Github的活动actor.login(Github的用户)上查找匹配,以查看它是否与该searchFilter值匹配。

随着updateData()功能的更新,我们的搜索已经完成。

尝试搜索auser

现在我们有一个3层应用程序组件,用于处理嵌套子组件的搜索。我们用这篇文章从初学者跳到中级。拍拍自己的背部。这是一些沉重的材料。请确保您理解这一点,因为我们会经常使用我们今天介绍的这些概念。

在下一节中,我们将跳出来看看构建组件。

 

学习REACT正确的方法

React和朋友的最新,深入,完整的指南。

下载第一章

❮上一个

下一章:

纯组件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值