学习React不是一蹴而就的事情,入门似乎也没那么简单。但一切都是值得的。

今天给大家带来一个详细的React的实例,实例并不难,但对于初学者而言,足够认清React的思考和编写过程。认真完成这个实例的每一个细节会让你受益匪浅。接下来我们开始吧!

代码下载

预览

首先说明一下,本例究竟做了什么。本文实现了一个单页面人员管理系统的前台应用。包括以下功能:

  • 人员基本信息列表;
  • 人员的录入及删除;
  • 人员详细信息的查看;
  • 人员信息的编辑;
  • 根据人员身份进行筛选;
  • 根据人员某些属性进行排序;
  • 根据人姓名、年龄、身份、性别等关键字进行人员搜索。

页面预览如下:

图1

图2

为了更好地学习,请先到这里去感受一下:

人员管理系统

代码下载

本文构建React组件的时候,使用了es6的语法,最终用webpack打包。最好有相关基础,我会在相关的地方进行言简意赅的说明。

第一步:划分UI Component

React is all about modular, composable components.

React是模块化、组件化的。我们这里第一步要做的就是将应用划分成各个组件。我在图一、图二的基础上圈出了我们即将实现的各个组件。结果如图三、图四所示:

图3

图4

每个圈出的组件功能如下,这是本应用的框架,请大家务必看清楚,其中斜体字是各个组件的名称:

  • ManageSystem 图三最外层的红色方框,这是管理模块的最外层容器,容纳整个应用;
  • StaffHeader 图三最上层蓝色方框,该模块接收用户操作的输入,包括关键字搜索输入、筛选条件以及排序方式;
  • StaffItemPanel 图三中间蓝色方框,该模块用于展示所有基于用户操作(关键字搜索、筛选、排序)结果的条目;
  • StaffFooter 图三最下层蓝色方框,该模块用于新人员的添加;
  • StaffItem 图三内层的红色方框,该模块用于展示一条人员的基本信息,包括删除和详情操作的按钮;
  • StaffDetail 图四的红色方框,每当点击StaffItem的’详情’后会显示该条目的详细信息。该模块用于展示人员的详细信息,兼有人员信息编辑的功能。

为了更清楚地展示框架结构:

ManageSystem

    StaffHeader

    StaffItemPanel

        StaffItem
StaffItem
StaffItem... StaffFooter StaffDetail(只在点击某条目的详情后展示)

第二步:构建静态版的React应用

在第一步中我们已经划分了各个组件,也说明了各个组件的职责。接下来我们分步完成我们的应用,首先我们做一个静态版的React,只用于render UI组件,但并不包含任何交互。

这个步骤我们只需要参照图一、图二去做就好了,绝大部分工作基本上就是使用JSX按部就班地写html代码。这个过程不需要太多思考。每个组件中都仅仅只包含一个render()方法。

需要注意的是,静态版的应用,数据由父组件通过props属性向下传递,state属性是用不到的,记住,state仅仅为动态交互而生。

本应用的组件相对较多,我们不妨采用bottom-up的方式,从子组件开始。

好了,我们开始吧。

StaffHeader

首先以StaffHeader为例,创建一个StaffHeader.js文件。如下:


import React from 'react';
export default class StaffHeader extends React.Component{ render(){
return (
<div>
<h3 style={{'text-align':'center'}}>人员管理系统</h3>
<table className="optHeader">
<tbody>
<tr>
<td className="headerTd"><input type='text' placeholder='Search...' /></td>
<td className="headerTd">
<label for='idSelect'>人员筛选</label>
<select id='idSelect'>
<option value='0'>全部</option>
<option value='1'>主任</option>
<option value='2'>老师</option>
<option value='3'>学生</option>
<option value='4'>实习</option>
</select>
</td>
<td>
<label for='orderSelect'>排列方式</label>
<select id='orderSelect'>
<option value='0'>身份</option>
<option value='1'>年龄升</option>
<option value='2'>年龄降</option>
</select>
</td>
</tr>
</tbody>
</table>
</div>
);
}
}

该组件主要用于提供搜索框,人员筛选下拉框以及排列方式下拉框。没错,我们首先就是要搭建一个静态版的React。呈现的样子参考图三最上方的蓝色框。当然,为了实现最终的样式,需要css的配合,css不是本文的关注点,本应用的css也十分简单,自行查看源代码。

StaffItem

StaffItem是每个具体人员的基本信息组件,用于展示人员的基本信息并接收用户的删除和点击详情的操作。新建一个StaffItem.js(该组件在StaffItemPanel中被引用):


import React from 'react';
export default class StaffItem extends React.Component{ render(){
return (
<tr
style={{'cursor': 'pointer'}}
>
<td className='itemTd'>{this.props.item.info.name}</td>
<td className='itemTd'>{this.props.item.info.age}</td>
<td className='itemTd'>{this.props.item.info.id}</td>
<td className='itemTd'>{this.props.item.info.sex}</td>
<td className='itemTd'>
<a className="itemBtn">删除</a>
<a className="itemBtn">详情</a>
</td>
</tr>
);
}
}

StaffItemPanel

接下来是StaffItemPanel,该组件仅用于展示由父组件传入的各个人员条目,新建一个StaffItemPanel.js文件:


import React from 'react';
import StaffItem from './StaffItem.js';
export default class StaffItemPanel extends React.Component{ render(){
let items = []; if(this.props.items.length == 0) {
items.push(<tr><th colSpan="5" className="tempEmpty">暂无用户</th></tr>);
}else {
this.props.items.forEach(item => {
items.push(<StaffItem key={item.key} item={item}/>);
});
} return (
<table className='itemPanel'>
<thead>
<th className='itemTd'>姓名</th>
<th className='itemTd'>年龄</th>
<th className='itemTd'>身份</th>
<th className='itemTd'>性别</th>
<th className='itemTd'>操作</th>
</thead>
<tbody>{items}</tbody>
</table>
);
}
}

该组件的功能相对简单,其中


if(this.props.items.length == 0) {
items.push(<tr><th colSpan="5" className="tempEmpty">暂无用户</th></tr>);
}else {
this.props.items.forEach(item => {
items.push(<StaffItem key={item.key} item={item} />);
});
}

是为了在暂无条目的时候给出相应的提示,如下图:

图5

StaffFooter

StaffFooter组件的功能是添加新人员,新建StaffFooter.js文件:


import React from 'react';
export default class StaffFooter extends React.Component{ render(){
return (
<div>
<h4 style={{'text-align':'center'}}>人员新增</h4>
<hr/>
<form ref='addForm' className="addForm">
<div>
<label for='staffAddName' style={{'display': 'block'}}>姓名</label>
<input ref='addName' id='staffAddName' type='text' placeholder='Your Name'/>
</div>
<div>
<label for='staffAddAge' style={{'display': 'block'}}>年龄</label>
<input ref='addAge' id='staffAddAge' type='text' placeholder='Your Age(0-150)'/>
</div>
<div>
<label for='staffAddSex' style={{'display': 'block'}}>性别</label>
<select ref='addSex' id='staffAddSex'>
<option value='男'>男</option>
<option value='女'>女</option>
</select>
</div>
<div>
<label for='staffAddId' style={{'display': 'block'}}>身份</label>
<select ref='addId' id='staffAddId'>
<option value='主任'>主任</option>
<option value='老师'>老师</option>
<option value='学生'>学生</option>
<option value='实习'>实习</option>
</select>
</div>
<div>
<label for='staffAddDescrip' style={{'display': 'block'}}>个人描述</label>
<textarea ref='addDescrip' id='staffAddDescrip' type='text'></textarea>
</div>
<p ref="tips" className='tips' >提交成功</p>
<p ref='tipsUnDone' className='tips'>请录入完整的人员信息</p>
<p ref='tipsUnAge' className='tips'>请录入正确的年龄</p>
<div>
<button>提交</button>
</div>
</form>
</div>
)
}
}

代码看起来比较长,其实就是一个html表单,这个步骤基本都是不需要太多思考的操作,代码也没有任何理解上的难度,记住,我们现在就是要把整个框架搭起来,做一个静态版的应用!同样的,呈现出最终的样式,需要一些css,自行参考源代码。呈现的样子见图三最下面的蓝色方框。

StaffDetail

通常情况下,该组件是不显示的,只有当用户点击某条目的详情的时候,我用了一种动画效果将该组件’浮现出来’。方法就是在css中将该组件的z-index设置为一个很大的值,比如100,然后通过逐渐改变背景透明度的动画实现浮现的效果。目前我们只需要做一个静态版的React,尚未实现用户点击操作的交互,所以这里只需要创建以下js文件,并在css中将.overLay的display设置为none就可以了,源码中的css文件已经做好了。


import React from 'react';
export default class StaffDetail extends React.Component{ render(){
let staffDetail = this.props.staffDetail;
if(!staffDetail)
return null; return (
<div className="overLay">
<h4 style={{'text-align':'center'}}>点击'完成'保存修改,点击'关闭'放弃未保存修改并退出.</h4>
<hr/>
<table ref="editTabel">
<tbody>
<tr>
<th>姓名</th>
<td><input id='staffEditName' type="text" defaultValue={staffDetail.info.name}></input></td>
</tr>
<tr>
<th>年龄</th>
<td><input id='staffEditAge' type="text" defaultValue={staffDetail.info.age}></input></td>
</tr>
<tr>
<th>性别</th>
<td>
<select ref='selSex' id='staffEditSex'>
<option value="男">男</option>
<option value="女">女</option>
</select>
</td>
</tr>
<tr>
<th>身份</th>
<td>
<select ref="selId" id='staffEditId'>
<option value="主任">主任</option>
<option value="老师">老师</option>
<option value="学生">学生</option>
<option value="实习">实习</option>
</select>
</td>
</tr>
<tr>
<th>个人描述</th>
<td><textarea id='staffEditDescrip' type="text" defaultValue={staffDetail.info.descrip}></textarea></td>
</tr>
</tbody>
</table>
<p ref='Dtips' className='tips'>修改成功</p>
<p ref='DtipsUnDone' className='tips'>请录入完整的人员信息</p>
<p ref='DtipsUnAge' className='tips'>请录入正确的年龄</p>
<button>完成</button>
<button>关闭</button>
</div>
);
}
}

和staffFooter类似,这里主要就是一个表单。

ManageSystem

子组件都已经做好了,接下来就是最外层的容器了。按部就班,新建一个ManageSystem.js:


import React from 'react';
import StaffHeader from './StaffHeader.js';
import StaffItemPanel from './StaffItemPanel.js';
import StaffFooter from './StaffFooter.js';
import StaffDetail from './StaffDetail.js'; var rawData = [{ info: {descrip:'我是一匹来自远方的狼。', sex: '男', age: 20, name: '张三', id: '主任'}},
{ info: {descrip:'我是一匹来自远方的狼。', sex: '女', age: 21, name: '赵静', id: '学生'}},
{ info: {descrip:'我是一匹来自远方的狼。', sex: '女', age: 22, name: '王二麻', id: '学生'}},
{ info: {descrip:'我是一匹来自远方的狼。', sex: '女', age: 24, name: '李晓婷', id: '实习'}},
{ info: {descrip:'我是一匹来自远方的狼。', sex: '男', age: 23, name: '张春田', id: '实习'}},
{ info: {descrip:'我是一匹来自远方的狼。', sex: '男', age: 22, name: '刘建国', id: '学生'}},
{ info: {descrip:'我是一匹来自远方的狼。', sex: '男', age: 24, name: '张八', id: '主任'}},
{ info: {descrip:'我是一匹来自远方的狗。', sex: '男', age: 35, name: '李四', id: '老师'}},
{ info: {descrip:'我是一匹来自远方的猪。', sex: '男', age: 42, name: '王五', id: '学生'}},
{ info: {descrip:'我是一匹来自远方的牛。', sex: '男', age: 50, name: '赵六', id: '实习'}},
{ info: {descrip:'我是一匹来自远方的马。', sex: '男', age: 60, name: '孙七', id: '实习'}}]; class App extends React.Component { render(){
return (
<div>
<StaffHeader/>
<StaffItemPanel items={rawData} />
<StaffFooter/>
<StaffDetail/>
</div>
);
}
} React.render(<App />, document.getElementById('app'));

以上代码中rawData是演示数据,生产中的数据应该从数据库获得,这里为了简便,直接生成了11条演示用的数据。

第三步:编译并打包

在第二步中,我们已经生成了各个component以及subcomponent。主要的任务已经完成了,这一步是做什么的呢?

简单地说,上文中我们编写React Component的过程中,使用了es6和JSX的语法。(特别值得一提的是es6的Module,终于从语言规格上让Javascript拥有了模块功能。如今js渐入佳境,学习es6是十分重要且值得的!)但这些目前是不能被浏览器直接支持的。所以在使用之前,要先经过’编译’,这个过程我们是使用Babel完成的。

关于Babel,正如其官网所言–Babel is a Javascript compiler.本应用中,它帮我们完成了es6以及JSX的编译。只不过在本例中babel是以webpack的loader的方式出现的。

关于webpack这里也不多言了–webpack is a module bundler.请大家自己查阅相关资料。

安装依赖项

在这里,首先执行以下命令,安装开发依赖:

npm install

该命令会自动读取当前目录下的package.json文件,并自行安装其中的依赖项。文件内容如下:

{
"name": "StaffManage",
"version": "1.0.0",
"description": "",
"main": "",
"scripts": {
"start": "webpack"
},
"author": "WYH",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.14.0",
"babel-loader": "^6.2.5",
"babel-preset-es2015": "^6.14.0",
"babel-preset-react": "^6.11.1",
"webpack": "^1.13.2"
}
}

更具体地说,其中的开发依赖项就是

  "devDependencies": {
"babel-core": "^6.14.0",
"babel-loader": "^6.2.5",
"babel-preset-es2015": "^6.14.0",
"babel-preset-react": "^6.11.1",
"webpack": "^1.13.2"
}

编译打包

安装开发依赖项后,接下来就是使用webpack打包了,webpack的loader在解析文件的时候会自动使用babel对文件进行编译。配置文件如下:

module.exports = {
entry: __dirname + '/src/ManageSystem.js',
output: {
path: __dirname + '/build',
filename: "bundle.js"
},
externals: {
'react': 'React'
},
devtool: 'eval-source-map', //生成source file
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015', 'react']
}
}
]
}
};

将第二步中的所有组件都放到当前目录下的src目录中,目录结构可以参考源代码,然后执行以下命令:

npm start

该命令也是在package.json中指定的。

"scripts": {
"start": "webpack"
}

好了,在build目录下应该已经生成bundle.js文件,这就是我们打包好的文件,我们只需要在html中引用它就行了。

在当前目录下生成html文件如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>人员管理</title>
<link href="build/style.css" rel="stylesheet" /> </head>
<body>
<div id="app">
</div> <script src="http://cdn.bootcss.com/react/0.13.3/react.min.js"></script>
<script src="build/bundle.js"></script>
</body>
</html>

接下来在浏览器中打开index.html看看吧,静态版的React已经生成了,只是还没有动态交互而已。至此,已经完成了构建静态版的React的工作,大框架已经建立,接下来我们让它动起来!

第四步:添加STAFF类

本文应用涉及的功能有排序,筛选、新增、删除、修改以及关键字搜索等。功能较多,业务逻辑有些复杂。为了让React集中精力完成view层的事情,我们这里新建一个STAFF类来完成业务逻辑。

Javascript中,类的实现是基于其原型继承机制的。但在es6中,提供了更接近传统面向对象语言的写法,引入了类(class)的概念。我们可以通过class关键字来定义类。实际上,es6的class只是一个语法糖(syntax sugar),它的绝大部分功能,es5均可以做到。而引入的class写法,是为了让对象的写法更加清晰、更加具有面向对象的感觉。

接下来我们新建一个STAFF.js文件:


class staffItem {
constructor(item){
this.info = {};
this.info.name = item.name;
this.info.age = item.age || 0;
this.info.sex = item.sex;
this.info.id = item.id;
this.info.descrip = item.descrip || '';
this.key = ++staffItem.key;
}
}
staffItem.key = 0; export default class STAFF { constructor(){
this.allStaff = [
new staffItem(STAFF.rawData[0]),
new staffItem(STAFF.rawData[1]),
new staffItem(STAFF.rawData[2]),
new staffItem(STAFF.rawData[3]),
new staffItem(STAFF.rawData[4]),
new staffItem(STAFF.rawData[5]),
new staffItem(STAFF.rawData[6]),
new staffItem(STAFF.rawData[7]),
new staffItem(STAFF.rawData[8]),
new staffItem(STAFF.rawData[9]),
new staffItem(STAFF.rawData[10])
];
this.staff = this.allStaff;
}
} STAFF.rawData = [{ descrip:'我是一匹来自远方的狼。', sex: '男', age: 20, name: '张三', id: '主任'},
{ descrip:'我是一匹来自远方的狼。', sex: '女', age: 21, name: '赵静', id: '学生'},
{ descrip:'我是一匹来自远方的狼。', sex: '女', age: 22, name: '王二麻', id: '学生'},
{ descrip:'我是一匹来自远方的狼。', sex: '女', age: 24, name: '李晓婷', id: '实习'},
{ descrip:'我是一匹来自远方的狼。', sex: '男', age: 23, name: '张春田', id: '实习'},
{ descrip:'我是一匹来自远方的狼。', sex: '男', age: 22, name: '刘建国', id: '学生'},
{ descrip:'我是一匹来自远方的狼。', sex: '男', age: 24, name: '张八', id: '主任'},
{ descrip:'我是一匹来自远方的狗。', sex: '男', age: 35, name: '李四', id: '老师'},
{ descrip:'我是一匹来自远方的猪。', sex: '男', age: 42, name: '王五', id: '学生'},
{ descrip:'我是一匹来自远方的牛。', sex: '男', age: 50, name: '赵六', id: '实习'},
{ descrip:'我是一匹来自远方的马。', sex: '男', age: 60, name: '孙七', id: '实习'}];

在STAFF.js中我们实际上创建了2个类,为了实现更好的’封装性’,我们将每一个人员条目单独作为一个staffItem类,该对象中包含了该人员的所有信息,在本应用中包含他的姓名、年龄、性别、身份、个人描述等,实践中我们可以加入类似入职时间,福利薪酬,个人经历等信息。另外还有一个key值,它是一个类变量,这个值是唯一标识该staffItem用的。

在第二步,我们在ManageSystem.js中伪造了一些数据,现在我们也把它搬到STAFF中。毕竟React不是存数据用的。

在STAFF类的构造函数中,创建了2个实例变量,一个是allStaff,其中存储所有staffItem;一个是staff,它是最终需要给React展示的数据,是经过用户筛选操作、关键字搜索操作之后得到的人员数组。之所以这么设计变量也是为了后面的筛选、搜索等功能。在这里我们尚无这些操作的逻辑,直接将allStaff赋给staff即可。

好了,接下来在ManageSystem中引入Staff.js,并初始化state:


import React from 'react';
import StaffHeader from './StaffHeader.js';
import StaffItemPanel from './StaffItemPanel.js';
import StaffFooter from './StaffFooter.js';
import StaffDetail from './StaffDetail.js'; import STAFF from './STAFF.js'; class App extends React.Component { constructor(){
super();
this.state = {
staff : new Staff
};
} render(){
return (
<div>
<StaffHeader/>
<StaffItemPanel items={this.state.staff.staff} />
<StaffFooter/>
<StaffDetail/>
</div>
);
}
} React.render(<App />, document.getElementById('app'));

在构造函数中,new了一个STAFF类,然后将this.state.staff.staff传入<StaffItemPanel/>的items属性。

然后重新编译打包:

npm start

再次在浏览器打开index.html文件,虽然还是一个静态版的React,不过它已经变得更加模块化和’专一’了,结构也更加漂亮。

第五步:完成新增人员功能

关于state

上文说过,state是为交互而生的。React是自上而下的单向数据流,state通常由上层组件拥有并控制,state的变化将触发建立在该state上的一系列自上而下的组件更新。注意,组件只能update它自己的state,如果下层组件的操作希望改变应用的状态,形成一个inverse data flow–反向数据流,我们需要从上层组件传入一个回调函数。关于state如何确定,可以参考官网一篇文章Thinking in React。接下来我们看人员功能添加是如何完成的。

实现人员新增功能

真正让React动起来,不妨从新增人员逻辑开始吧,这个功能比较纯粹,和其他业务耦合度不高。重新打开StaffFooter.js,加入部分代码:


import React from 'react';
export default class StaffFooter extends React.Component{ handlerAddClick(evt){
evt.preventDefault();
let item = {};
let addForm = React.findDOMNode(this.refs.addForm);
let sex = addForm.querySelector('#staffAddSex');
let id = addForm.querySelector('#staffAddId'); item.name = addForm.querySelector('#staffAddName').value.trim();
item.age = addForm.querySelector('#staffAddAge').value.trim();
item.descrip = addForm.querySelector('#staffAddDescrip').value.trim();
item.sex = sex.options[sex.selectedIndex].value;
item.id = id.options[id.selectedIndex].value; /*
*表单验证
*/
if(item.name=='' || item.age=='' || item.descrip=='') {
let tips = React.findDOMNode(this.refs.tipsUnDone);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
return;
}
//非负整数
let numReg = /^\d+$/;
if(!numReg.test(item.age) || parseInt(item.age)>150) {
let tips = React.findDOMNode(this.refs.tipsUnAge);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
return;
} this.props.addStaffItem(item);
addForm.reset(); //此处应在返回添加成功信息后确认
let tips = React.findDOMNode(this.refs.tips);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
} render(){
return (
<div>
<h4 style={{'text-align':'center'}}>人员新增</h4>
<hr/>
<form ref='addForm' className="addForm">
<div>
<label for='staffAddName' style={{'display': 'block'}}>姓名</label>
<input ref='addName' id='staffAddName' type='text' placeholder='Your Name'/>
</div>
<div>
<label for='staffAddAge' style={{'display': 'block'}}>年龄</label>
<input ref='addAge' id='staffAddAge' type='text' placeholder='Your Age(0-150)'/>
</div>
<div>
<label for='staffAddSex' style={{'display': 'block'}}>性别</label>
<select ref='addSex' id='staffAddSex'>
<option value='男'>男</option>
<option value='女'>女</option>
</select>
</div>
<div>
<label for='staffAddId' style={{'display': 'block'}}>身份</label>
<select ref='addId' id='staffAddId'>
<option value='主任'>主任</option>
<option value='老师'>老师</option>
<option value='学生'>学生</option>
<option value='实习'>实习</option>
</select>
</div>
<div>
<label for='staffAddDescrip' style={{'display': 'block'}}>个人描述</label>
<textarea ref='addDescrip' id='staffAddDescrip' type='text'></textarea>
</div>
<p ref="tips" className='tips' >提交成功</p>
<p ref='tipsUnDone' className='tips'>请录入完整的人员信息</p>
<p ref='tipsUnAge' className='tips'>请录入正确的年龄</p>
<div>
<button onClick={this.handlerAddClick.bind(this)}>提交</button>
</div>
</form>
</div>
)
}
}

我们在提交的按钮上绑定了点击事件。点击提交按钮后,执行以下函数:

handlerAddClick(evt){
evt.preventDefault();
let item = {};
let addForm = React.findDOMNode(this.refs.addForm);
let sex = addForm.querySelector('#staffAddSex');
let id = addForm.querySelector('#staffAddId'); item.name = addForm.querySelector('#staffAddName').value.trim();
item.age = addForm.querySelector('#staffAddAge').value.trim();
item.descrip = addForm.querySelector('#staffAddDescrip').value.trim();
item.sex = sex.options[sex.selectedIndex].value;
item.id = id.options[id.selectedIndex].value; /*
*表单验证
*/
if(item.name=='' || item.age=='' || item.descrip=='') {
let tips = React.findDOMNode(this.refs.tipsUnDone);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
return;
}
//非负整数
let numReg = /^\d+$/;
if(!numReg.test(item.age) || parseInt(item.age)>150) {
let tips = React.findDOMNode(this.refs.tipsUnAge);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
return;
} this.props.addStaffItem(item);
addForm.reset(); //此处应在返回添加成功信息后确认
let tips = React.findDOMNode(this.refs.tips);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
}

这里我们获取并简单处理了表单,特别注意

this.props.addStaffItem(item);

这一行代码,就是调用了ManageSystem通过prop属性传入的回调函数。在ManageSystem中加入相关代码:


import React from 'react';
import StaffHeader from './StaffHeader.js';
import StaffItemPanel from './StaffItemPanel.js';
import StaffFooter from './StaffFooter.js';
import StaffDetail from './StaffDetail.js'; import Staff from './STAFF.js'; class App extends React.Component {
constructor(){
super();
this.state = {
staff : new Staff,
staffDetail: null
};
} //增
addStaffItem(item){
this.setState({
staff: this.state.staff.addStaffItem(item)
});
} render(){
return (
<div>
<StaffHeader/>
<StaffItemPanel items={this.state.staff.staff}}/>
<StaffFooter addStaffItem={this.addStaffItem.bind(this)}/>
<StaffDetail/>
</div>
);
}
} React.render(<App />, document.getElementById('app'));

<StaffFooter addStaffItem={this.addStaffItem.bind(this)}/>中传入了addStaffItem方法。

//增
addStaffItem(item){
this.setState({
staff: this.state.staff.addStaffItem(item)
});
}

中更新了自己的state。只不过具体的逻辑是在STAFF类的方法中完成的。

STAFF.js:


export default class STAFF { constructor(){
this.allStaff = [
new staffItem(STAFF.rawData[0]),
new staffItem(STAFF.rawData[1]),
new staffItem(STAFF.rawData[2]),
new staffItem(STAFF.rawData[3]),
new staffItem(STAFF.rawData[4]),
new staffItem(STAFF.rawData[5]),
new staffItem(STAFF.rawData[6]),
new staffItem(STAFF.rawData[7]),
new staffItem(STAFF.rawData[8]),
new staffItem(STAFF.rawData[9]),
new staffItem(STAFF.rawData[10])
];
this.staff = this.allStaff;
} //增
addStaffItem(item) {
let newItem = new staffItem(item);
this.allStaff.push(newItem);
this.staff = this.allStaff;
return this;
}
}

重新编译打包生成bundle.js文件:

npm start

再次在浏览器中打开index.html文件,试试我们新添加的人员添加功能吧!

第六步:完成关键字搜索功能

类似第五步新人员的添加,我们首先给StaffHeader中的搜索输入框绑定一个onChange事件,每当搜索内容改变时,触发该函数:

StaffHeader.js


import React from 'react';
export default class StaffHeader extends React.Component{ //search
handlerSearch(){
let bar = React.findDOMNode(this.refs.searchBar);
let value = bar.value;
this.props.searchStaff(value);
} render(){
return (
<div>
<h3 style={{'text-align':'center'}}>人员管理系统</h3>
<table className="optHeader">
<tbody>
<tr>
<td className="headerTd"><input ref='searchBar' onChange={this.handlerSearch.bind(this)} type='text' placeholder='Search...' /></td>
<td className="headerTd">
<label for='idSelect'>人员筛选</label>
<select id='idSelect'>
<option value='0'>全部</option>
<option value='1'>主任</option>
<option value='2'>老师</option>
<option value='3'>学生</option>
<option value='4'>实习</option>
</select>
</td>
<td>
<label for='orderSelect'>排列方式</label>
<select id='orderSelect'>
<option value='0'>身份</option>
<option value='1'>年龄升</option>
<option value='2'>年龄降</option>
</select>
</td>
</tr>
</tbody>
</table>
</div>
);
}
}

同样在事件处理函数中,调用了通过props属性传入的回调函数searchStaff:

//search
handlerSearch(){
let bar = React.findDOMNode(this.refs.searchBar);
let value = bar.value;
this.props.searchStaff(value);
}

逐步完善ManageSystem以及STAFF类:

ManageSystem.js:


class App extends React.Component {
constructor(){
super();
this.state = {
staff : new Staff,
staffDetail: null
};
} //增
addStaffItem(item){
this.setState({
staff: this.state.staff.addStaffItem(item)
});
} /*
* 搜索
*/
searchStaff(word) {
this.setState({
staff: this.state.staff.searchStaff(word)
});
} render(){
return (
<div>
<StaffHeader searchStaff={this.searchStaff.bind(this)} />
<StaffItemPanel items={this.state.staff.staff}}/>
<StaffFooter addStaffItem={this.addStaffItem.bind(this)}/>
<StaffDetail/>
</div>
);
}
}

STAFF.js


export default class STAFF { constructor(){
this.allStaff = [
new staffItem(STAFF.rawData[0]),
new staffItem(STAFF.rawData[1]),
new staffItem(STAFF.rawData[2]),
new staffItem(STAFF.rawData[3]),
new staffItem(STAFF.rawData[4]),
new staffItem(STAFF.rawData[5]),
new staffItem(STAFF.rawData[6]),
new staffItem(STAFF.rawData[7]),
new staffItem(STAFF.rawData[8]),
new staffItem(STAFF.rawData[9]),
new staffItem(STAFF.rawData[10])
];
this.staff = this.allStaff;
this.word = ''; //搜索关键字
} //增
addStaffItem(item) {
let newItem = new staffItem(item);
this.allStaff.push(newItem);
this.staff = this.allStaff;
return this;
} //搜索
searchStaff(word){
this.word = word;
this.staff = this.allStaff;
//在staff中搜索
this.staff = this.staff.filter(item => {
return item.info.name.indexOf(word)!=-1 ||
(item.info.age+'').indexOf(word)!=-1 ||
item.info.id.indexOf(word)!=-1 ||
item.info.sex.indexOf(word)!=-1;
});
return this;
}
}

依据关键字的搜索功能至此也完成了。

完成接下来的功能

作为示例,第五步以及第六步完成了添加人员以及关键字搜索功能。随后随着功能的不断添加,最终代码的实现会有微小的调整。实现的方法大同小异,请大家对照源码,依照上面的方法逐步完整整个应用。我相信你实现了每一个细节之后,对面入门React一定会有十分大的帮助。

React基本实例的更多相关文章

  1. React 入门实例教程(转载)

    本人转载自: React 入门实例教程

  2. React Native实例之房产搜索APP

    React Native 开发越来越火了,web app也是未来的潮流, 现在react native已经可以完成一些最基本的功能. 通过开发一些简单的应用, 可以更加熟练的掌握 RN 的知识. 在学 ...

  3. React 系列文章(1): npm 手动搭建React 运行实例 (新手必看)

    摘 要 刚接触React 开发, 在摸索中构建react 运行环境,总会遇到各种坑:本文,将用最短时间解决webpack+react 环境搭建问题. 1.如果你还没有React基础 看这里. 2.如果 ...

  4. React 入门实例

    React 入门实例教程 一.安装 React 的安装包,可以到官网下载. $ git clone git@github.com:ruanyf/react-demos.git 如果你没安装 git, ...

  5. let import export React入门实例教程 connect provider combineReducers 箭头函数 30分钟掌握ES6/ES2015核心内容 Rest babel

    let与var的区别 http://www.cnblogs.com/snandy/archive/2015/05/10/4485832.html es6 导入导出 http://www.csdn.ne ...

  6. React 入门实例教程

    现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Face ...

  7. React Native实例

    本文主要包括以下内容 View组件的实例 Text组件实例 Navigator组件实例 TextInput组件实例 View组件的实例 效果如下 代码如下 /** * Sample React Nat ...

  8. 2015年最热门前端框架React 入门实例教程

    现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Face ...

  9. React入门实例教程

    文章转自:阮一峰 现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React ...

随机推荐

  1. The web application you are attempting to access on this web server is currently unavailable.......

    今天去服务器安装了个.net 4.0 framework(原本有1.0和2.0的),配置好站点后,选择版本为4.0,访问出错,错误代码如下 Server Application Unavailable ...

  2. hdu 3694 10 福州 现场 E - Fermat Point in Quadrangle 费马点 计算几何 难度:1

    In geometry the Fermat point of a triangle, also called Torricelli point, is a point such that the t ...

  3. Newtonsoft.Json JsonHelper

    Json.net 简单封装 using System; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Serializ ...

  4. [转载]java调用本地dos命令

    在社区看到java调用本地dos命令的代码,特贴出来 String command = "ipconfig"; Runtime run = Runtime.getRuntime() ...

  5. java Object对象的clone方法

    参考copy链接:http://blog.csdn.net/bigconvience/article/details/25025561 在看原型模式,发现要用到clone这个方法,以前和朋友聊过,没怎 ...

  6. 作业要求 20181030-1 Alpha发布用户使用报告

    作业链接[https://edu.cnblogs.com/campus/nenu/2018fall/homework/2325] 队名:二次元梦之队 组长:刘莹莹 组员:周昊 潘世维  王玉潘 赵美增 ...

  7. [Tomcat无法启动]'Starting Tomcat v8.0 Server at localhost' has encountered a problem.

    运行一个index.jsp,运行提示如下错误:点击服务器 解决办法1: 点击Tomcat服务器,将服务器里的web工程删除掉,再重新运行index.jsp. 解决办法2: 关闭Tomcat,最简单的方 ...

  8. .properties文件常量定义

    application.properties文件,位于resources目录下.一般定义一些不会改变的数据如数据库相关配置,文件路径,项目地址. 如何在项目代码中获取该文件中的配置信息: 1,在app ...

  9. multiprocessing创建自定义进程类

    1.继承Process2.编写自己的__init__,同时加载父类init方法3.重写run方法,可以通过生成的对象调用start自动运行 from multiprocessing import Pr ...

  10. 手机dp和px的转换

    1dp   0.75px  ----> 320*240 1dp   1px      ----->480*320 1dp   1.5px   ----->800*480      4 ...