这篇文章是30天React系列的一部分 。
在本系列中,我们将从非常基础开始,逐步了解您需要了解的所有内容,以便开始使用React。如果您曾经想学习React,那么这里就是您的最佳选择!
互动
今天,我们将介绍如何为我们的应用程序添加交互性,使其具有吸引力和动态性。
通过这一点,我们构建了少数几个组件,而没有增加太多的用户交互。今天,我们要改变这一点。
用户互动
浏览器是一个事件驱动的应用程序。用户在浏览器中执行的所有操作都会触发事件,从单击按钮到甚至只是移动鼠标。在纯的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
将输入呈现为可见。
让我们定义searchVisible
prop(PropTypes
当然使用)并更新render
函数以使用新的prop值来显示(或隐藏)搜索<input />
。我们还将字段可见性的默认值设置为false(因为我们的Header
显示/隐藏它很好):
class SearchForm extends React.Component {
static propTypes = {
onSubmit: PropTypes.func.isRequired,
searchVisible: PropTypes.bool
}
// ...
}
现在我们将样式返回到<input />
元素上,让我们添加用户在搜索框中键入的功能,我们将要捕获搜索字段的值。我们可以通过将onChange
prop添加到<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
prop 传递一个值。受控组件版本的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 。由于我们要确保定义,我们将把道具设置为必需的道具:propType
onSubmit
onSubmit()
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()
函数。props
Header
可以我们SearchForm
在组件中使用这个组件,Header
并将它们传递给我们定义的两个道具(searchVisible
和onSubmit
):
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和朋友的最新,深入,完整的指南。
下一章:
纯组件