【全栈React】第10天: 交互性

前端之家收集整理的这篇文章主要介绍了【全栈React】第10天: 交互性前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

本文转载自:众成翻译
译者:iOSDevLog
链接http://www.zcfy.cc/article/3823
原文:https://www.fullstackreact.com/30-days-of-react/day-10/

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

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

用户交互

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

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

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>';
  })
}

这导致以下行为:

将鼠标移到该文本上

然而,在React中,我们不必在原始JavaScript中与浏览器的事件循环进行交互,因为React为我们使用props处理事件提供了一种方法

例如,要从React上面的(相当不起眼的)演示中收听mousemove 事件,我们将设置onMouseMove (请注意事件名称是驼峰命名的)。

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

React提供了很多props ,我们可以设置监听不同的浏览器事件,例如点击,触摸,拖动,滚动,选择事件等等(参见@L_502_4@文档列出所有这些)。

要看看其中的一些在行动中,以下是一些小的演示,一些props ,我们可以传递我们的元素。 列表中的每个文本元素设置其列出的属性。 尝试使用列表查看事件在元素中的调用和处理方式。

我们将在我们的应用中使用 onClick 属性相当多,所以熟悉它是一个好主意。 在我们的活动列表标题中,我们有一个搜索图标,我们还没有与显示一个搜索框关联起来。

我们_想要_的交互是在用户点击搜索图标时显示搜索。 回想一下,我们的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的Object函数的引用。

在纯英文中,构造函数是JavaScript运行时创建新对象时运行的函数。我们将使用构造函数方法对象创建时正确运行对象时设置实例变量。

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

用户点击按钮时,我们将要更新状态来表示searchVisible 标志被更新。由于我们希望用户能够第二次点击搜索图标后关闭/隐藏 <input />字段,所以我们将_切换_该状态,而不是将其设置为true。

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

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

最后,我们可以在icon元素上附加一个点击处理程序(使用onClick 属性)来调用我们新的 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()属性被设置时,它会在该字段_改变_的每个时间调用函数。当我们点击它并开始输入时,该函数将被调用

使用这个属性,我们可以捕捉到我们这个字段的价值。

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

我们创建一个我们称之为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 />字段上丢失了样式。 由于我们不再在我们的新 <input />组件中具有searchVisible状态,所以我们不能再使用它来对其进行风格化了。 _无论如何_,我们可以从我们的Header组件传递一个支持,该组件告诉SearchForm将输入渲染为可见。

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

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

现在我们在 <input />元素上有我们的样式,让我们添加用户搜索框中键入的功能,我们将要捕获搜索字段的值。 我们可以通过将onChange参数附加到 <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 />组件的值,我们将必须创建一个所谓的控件组件,这真的只是意味着我们使用value'传递一个值 属性。 受控组件版本的render()` 函数将如下所示:

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

到目前为止,我们无法真正提交表单,所以我们的用户无法真正搜索。 我们来改变一下 我们需要将component包含在一个DOM元素中,这样我们的用户可以按回车键提交表单。 我们可以使用 <form />元素上的onSubmit支持来捕获表单提交。

我们来更新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>
    );
  }
}

我们立即在submitForm()函数调用event.preventDefault()。这将阻止浏览器冒泡,从而使整个页面的默认行为重新加载(浏览器提交表单时的默认功能)。

现在当我们键入 <input />字段并按回车键,submitForm() 函数调用的事件对象。

那么好的,我们可以提交表单和内容,但是什么时候我们实际上进行搜索?为了演示目的,我们将把搜索文本传递给父子组件链,以便 Header 可以决定搜索_什么_。

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

为了将搜索功能传递给链,我们的“SearchForm”将需要接受在提交表单时调用函数。我们来定义一个我们称之为 SearchForm属性,我们可以传递给我们的SearchForm 组件。作为好的开发人员,我们还会为这个onSubmit函数添加默认的prop值和propType。因为我们想要确定onSubmit() 是被定义的,所以我们将把onSubmit的prop设置成一个必需的参数:

class SearchForm extends React.Component {
  static propTypes = {
    onSubmit: React.PropTypes.func.isrequired,searchVisible: React.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);
  }
}

现在,当用户按下enter时,我们可以通过我们的Header 组件来调用props 中传递的onSubmit() 函数

我们可以在我们的 Header 组件中使用这个 SearchForm 组件,并传递我们定义的两个属性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

现在我们有一个搜索表单组件,可以在我们的应用中使用和重用。 当然,我们还没有搜索任何东西。 我们来解决这个问题,实现搜索

[](#implementing-search)实现搜索

要在我们的组件中实现搜索,我们希望将搜索责任从我们的 Header 组件传递到容器组件,我们称之为 Panel

首先,让我们实现一个从 Panel 容器到Header 组件的子组件中将回调传递给父组件的模式。

Header 组件上,我们来更新一个属性propTypes ,我们将它定义为onSearch属性

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

Header 组件的'submitForm()'函数里面,调用这个onSearch() 属性,我们将传入它:

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

请注意,我们的虚拟树如下所示:

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

<SearchForm />更新时,它会传递它的意识,搜索输入的变化到它的父组件<Header />,当它将向上传递到<Panel />组件。 这种方法在React应用中是_very common_,并为我们的组件提供了一套很好的功能隔离。

回到我们在第7天构建的Panel 组件中,我们将把一个函数作为HeaderonSearch() 属性传递给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 组件。 我们在我们的Panel组件中定义一个onSearch() 函数,并将其传递给render() 函数中的Header 属性

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() 函数并将其传递给标题。 现在当用户键入搜索框时,我们的Panel组件上的handleSearch() 函数将被调用

为了_实现_搜索,我们需要跟踪这个字符串,并更新我们的updateData() '函数来考虑搜索过滤。 首先让我们把searchFilter 设置为状态。 我们也可以强制内容通过将加载设置为true来重新加载数据,因此我们可以在一个步骤中执行此操作:

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()结果以在json集合上调用filter() 方法

所有的collection.filter() 函数都是运行着每个元素传递的函数,并且过滤_掉_返回伪造值的值,保留返回真值的值。我们的搜索功能只是在Github活动的 actor.login (Github用户)上查找匹配,以查看它是否正确匹配searchFilter 值。

随着updateData() 功能的更新,我们的搜索完整了。

尝试搜索auser

现在我们有一个3层应用组件来处理嵌套子组件的搜索。我们通过这个post从初级阶段跳到了中级阶段。靠着自己。这是一些重大的材料。确保你明白这一点,因为我们会经常使用我们今天介绍的这些概念。

原文链接:https://www.f2er.com/react/303277.html

猜你在找的React相关文章