1. 前言

说到React,我从一年之前就开始试着了解并且看了相关的入门教程,而且还买过一本《React:引领未来的用户界面开发框架 》拜读。React的轻量组件化的思想及其virtual-dom的这种技术创新,也算是早就有了初步了解。一来没有学的太深入,二来后来在工作中和业余项目中都没有用到,因此慢慢的就更加生疏了。

近期,因为我想把自己的开源项目wangEditor能放在React、angular和vuejs中使用。先从react开始,顺手自己也重试一下React的基础知识,顺便再做一个小demo,体验一下React写程序的一些提倡的思路。经过几天的回顾学习,我也写了一个在React中集成wangEditor的简单demo:http://www.kancloud.cn/wangfupeng/wangeditor2/129376

不得不说一下,React风靡世界、功能强大、适应性强,但是入门起来相当简单。反观angularjs,学习成本就比较高,我还没弄明白1.x呢,2.0已经出来了。纵然我非常努力,但是某些方面还是摆脱不了out的命运(见笑...)。

2. 基础入门

想入门React,首先你得有比较扎实的javascript语法基础以及前端开发的基础知识,否则我下面推荐的教程讲的再好,你也咂摸不出啥滋味来。所以如果你是初学者,不要被现在前端这些琳琅满目的框架、库迷惑了眼睛,以为学会了这个那个就行了——基础不行学啥都白搭。

闲话不多扯。前人栽树后人乘凉,给大家推荐两个我看过的非常好的React入门教程,一个文字一个视频。

这两篇教程的篇幅都不长 ,阅读加练习的话,两个晚上(正常下班吃完饭之后的剩余时间)绝对能搞定的。当然你如果具备程序员优质的熬夜技能,一晚上搞定也说不定啊,创造奇迹的同时照顾一下身体,哈哈。看完这两篇教程,你能基本了解react的设计思想,技术特点,使用的语法和技巧。做个hello word什么的,完全没啥问题的。

3. 入门之后

记得几年前上大学乃至刚毕业那会儿,无论是学java还是php还是.net的,会了语法、会写个hello world肯定不能算是入门的。当时跟hello world齐名还有一个东西叫做『留言板』。师生之间经常有这样的对话。

  • 老师:xxx技术会用了吗?
  • 学生:会了
  • 老师:那写个留言板系统吧,能留言、查看、删除、回复
  • 学生:不会
  • 老师:....

上述的『留言板』也随着几年之前流行的bbs、论坛、校内网等没几年就河了西了(《大宅门》学的),目前用来做demo的一般都是todolist,例如backbone的官方demo就是一个todolist。

无论是『留言板』还是『todolist』,我们需要用它来表达的就是——我们如何通过这项技术去实现基本的『增删改查』 这种能力,因为一个系统其他所有的业务逻辑操作,都是『增删改查』这几个功能的拼接。所以,我们在刚刚接触一个新东西的时候,就用它来做一个简单的todolist吧。

4. todolist

做出来大约是这样子的,很简单很丑,too 羊 too 森破 sometime native 。不过没关系,虽然它很丑,但是很温柔啊。我们只是抛开了其他内容,专注于这项技术实现的本身而已。如果你想漂亮一点,自己写一个css样式喽。

下面我将一步一步讲解如何使用React来制作出一个简单的todolist,不过还是需要你耐心把文章读完,我也尽量写的可读性强一些,不至于太乏味。

4.1 整体分析

React最大的卖点是轻量组件化。我们分析一下以上截图中的页面,如果要分组件的话,我们大约可以分成一个总组件和两个子组件。一个输入内容的组件,一个显示内容列表(带删除功能)的组件,外面再用一个总组件将两个子组件包括起来。

因此,我们的代码的整体结构大约是这么写的:

 // TodoList 组件是一个整体的组件,最终的React渲染也将只渲染这一个组件
// 该组件用于将『新增』和『列表』两个组件集成起来
var TodoList = React.createClass({
render: function () {
return (
<div>
<TypeNew />
<ListTodo />
</div>
);
}
}); // TypeNew 组件用于新增数据,
var TypeNew = React.createClass({
render: function () {
return (
<form>
<input type="text" placeholder="typing a newthing todo" autoComplete="off" />
</form>
);
}
}); // ListTodo 组件用于展示列表,并可以删除某一项内容,
var ListTodo = React.createClass({
render: function () {
return (
<ul id="todo-list">
{/* 其中显示数据列表 */}
</ul>
);
}
}); // 将 TodoList 组件渲染到页面
React.render(<TodoList />, document.getElementById('container'));

4.2. 显示数据

下面,我们要把todolist的数据,显示到列表中,并且每个数据项后面都显示一个『删除』按钮,就像这样:

既然是展示数据,首先要考虑数据存储在哪里,来自于哪里。现在这里放一句话——React提倡所有的数据都是由父组件来管理,通过props的形式传递给子组件来处理——先记住,接下来再解释这句话。

上文提到,做一个todolist页面需要一个父组件,两个子组件。父组件当然就是todolist的『总指挥』,两个子组件分别用来add和show、delete。用通俗的方式讲来,父组件就是领导,两个子组件就是协助领导开展工作的,一切的资源和调动资源的权利,都在领导层级,子组件配合领导工作,需要资源或者调动资源,只能申请领导的批准

这么说来就明白了吧。数据完全由父组件来管理和控制,子组件用来显示、操作数据,得经过父组件的批准,即——父组件通过props的形式将数据传递给子组件,子组件拿到父组件传递过来的数据,再进行展示。

另外,根据React开发的规范,组件内部的数据由state控制,外部对内部传递数据时使用 props 。这么看来,针对父组件来说,要存储todolist的数据,那就是内部信息(本身就是自己可控的资源,而不是『领导』控制的资源),用state来存储即可。而父组件要将todolist数据传递给子组件,对子组件来说,那就是传递进来的外部信息(是『领导』的资源,交付给你来处理),需要使用props。

好了,我们再修改一下代码,用代码表述一下这个问题:

 // TodoList 组件是一个整体的组件,最终的React渲染也将只渲染这一个组件
// 该组件用于将『新增』和『列表』两个组件集成起来
var TodoList = React.createClass({
// 初始化数据,todolist的数据由state来控制
getInitialState: function () {
return {
todolist: []
};
},
render: function () {
return (
<div>
<TypeNew />
{/*
集成 ListTodo 组件
todo - 将todolist的数据传入到组件,用于组件展示数据
*/}
<ListTodo todo={this.state.todolist} />
</div>
);
}
}); // TypeNew 组件用于新增数据,
var TypeNew = React.createClass({
// 此处省略 ... 字
}); // ListTodo 组件用于展示列表,并可以删除某一项内容,
var ListTodo = React.createClass({
render: function () {
return (
<ul id="todo-list">
{
// this.props.todo 获取父组件传递过来的数据
// {/* 遍历数据 */}
this.props.todo.map(function (item, i) {
return (
<li>
<label>{item}</label>
<button>delete</button>
</li>
);
})
}
</ul>
);
}
}); // 将 TodoList 组件渲染到页面
React.render(<TodoList />, document.getElementById('container'));

4.3 新增数据

刚才都把数据展示讲完了,但是想展示一下,目前还没有数据呢,那就新增一个吧。如下图:

根据刚才的拐弯抹角、高谈阔论、旁征博引的那几句话,我们知道,子组件得到数据后,就需要将新数据添加到todolist的数据中。而todolist的数据是由父组件来管理的,子组件不能说改就改呀,得申请父组件的允许和同意呀。因此,我们需要让父组件开放一个可以修改数据的接口,然后将这个接口作为props传递给子组件,让其能修改数据。

另外,子组件调用父组件的接口对todolist数据进行修改了之后,相当于修改了React对象的state数据,此时就会触发React的自动更新(就是通过virtual-dom对比,然后更新真实的dom那一套),React会将UI实时随着数据更新,就不用我们操心了,这也是React比较强大的地方之一。

因此,代码将改为:

 // TodoList 组件是一个整体的组件,最终的React渲染也将只渲染这一个组件
// 该组件用于将『新增』和『列表』两个组件集成起来
var TodoList = React.createClass({
// 初始化数据,todolist的数据由state来控制
getInitialState: function () {
return {
todolist: []
};
},
// 接收一个传入的数据,并将它实时更新到组件的 state 中,以便组件根据数据重新render
// 只要改变了 state ,react自动执行 reader 计算
handleChange: function (rows) {
this.setState({
todolist: rows
});
},
render: function () {
return (
<div>
{/*
集成 TypeNews 组件,传入两个属性 onAdd 和 todo
todo - 将todolist的数据传入到组件,当新增时,更新todolist数据
onAdd - 将 handleChange 函数传入到组件,新增时,用它来处理最新的todolist数据
*/}
<TypeNew onAdd={this.handleChange} todo={this.state.todolist} />
{/*
集成 ListTodo 组件
todo - 将todolist的数据传入到组件,用于组件展示数据
*/}
<ListTodo todo={this.state.todolist} />
</div>
);
}
}); // TypeNew 组件用于新增数据,它需要 todo 和 onAdd 两个属性,上文已经提到过
// 基本逻辑是:当从 input 中获取数据时,将新数据 push 到todo中,
// 然后使用 onAdd 调用 TodoList 的 handleChange 来更新state,然后react自动render
var TypeNew = React.createClass({
handleAdd: function (e) {
e.preventDefault();
// 通过 refs 获取dom元素,然后获取输入的内容
var inputDom = this.refs.inputnew.getDOMNode();
var newthing = inputDom.value.trim();
// 获取传入的todolist数据
var rows = this.props.todo;
if (newthing !== '') {
// 更新数据,并使用 onAdd 更新到 TodoList 组件的 state 中
rows.push(newthing);
this.props.onAdd(rows);
}
inputDom.value = '';
},
render: function () {
return (
// form submit 时,触发 handleAdd 事件
<form onSubmit={this.handleAdd}>
<input type="text" ref="inputnew" id="todo-new" placeholder="typing a newthing todo" autoComplete="off" />
</form>
);
}
}); // ListTodo 组件用于展示列表,并可以删除某一项内容,
var ListTodo = React.createClass({
render: function () {
return (
<ul id="todo-list">
{
// this.props.todo 获取父组件传递过来的数据
// {/* 遍历数据 */}
this.props.todo.map(function (item, i) {
return (
<li>
<label>{item}</label>
<button>delete</button>
</li>
);
})
}
</ul>
);
}
}); // 将 TodoList 组件渲染到页面
React.render(<TodoList />, document.getElementById('container'));

4.4 删除数据

删除数据和新增数据,逻辑上是一样的,都是需要父组件提供一个修改数据的接口,通过props形式传递给子组件,然后让子组件来调用。就不再赘述了,直接上代码,注意看注释:

     // TodoList 组件是一个整体的组件,最终的React渲染也将只渲染这一个组件
// 该组件用于将『新增』和『列表』两个组件集成起来,并且存储 todolist 的数据
var TodoList = React.createClass({
// 初始化数据
getInitialState: function () {
return {
todolist: []
};
},
// 接收一个传入的数据,并将它实时更新到组件的 state 中,以便组件根据数据重新render
// 只要改变了 state ,react自动执行 reader 计算
handleChange: function (rows) {
this.setState({
todolist: rows
});
},
render: function () {
return (
<div>
{/*
集成 TypeNews 组件,传入两个属性 onAdd 和 todo
todo - 将todolist的数据传入到组件,当新增时,更新todolist数据
onAdd - 将 handleChange 函数传入到组件,新增时,用它来处理最新的todolist数据
*/}
<TypeNew onAdd={this.handleChange} todo={this.state.todolist} />
{/*
集成 ListTodo 组件,传入两个属性 onDel 和 todo
todo - 将todolist的数据传入到组件,当删除时,更新todolist数据
onDel - 将 handleChange 函数传入到组件,删除时,用它来处理最新的todolist数据
*/}
<ListTodo onDel={this.handleChange} todo={this.state.todolist} />
</div>
);
}
}); // TypeNew 组件用于新增数据,它需要 todo 和 onAdd 两个属性,上文已经提到过
// 基本逻辑是:当从 input 中获取数据时,将新数据 push 到todo中,
// 然后使用 onAdd 调用 TodoList 的 handleChange 来更新state,然后react自动render
var TypeNew = React.createClass({
handleAdd: function (e) {
e.preventDefault();
// 通过 refs 获取dom元素,然后获取输入的内容
var inputDom = this.refs.inputnew.getDOMNode();
var newthing = inputDom.value.trim();
// 获取传入的todolist数据
var rows = this.props.todo;
if (newthing !== '') {
// 更新数据,并使用 onAdd 更新到 TodoList 组件的 state 中
rows.push(newthing);
this.props.onAdd(rows);
}
inputDom.value = '';
},
render: function () {
return (
// form submit 时,触发 handleAdd 事件
<form onSubmit={this.handleAdd}>
<input type="text" ref="inputnew" id="todo-new" placeholder="typing a newthing todo" autoComplete="off" />
</form>
);
}
}); // ListTodo 组件用于展示列表,并可以删除某一项内容,它有 noDel todo 两个属性,上文已经提到过
// 它的基本逻辑是:遍历 todo 的内容,生成数据列表和删除按钮
// 对某一项执行删除时,想将 todo 中的数据删除,
// 然后通过 onDel 事件调用 TodoList 的 handleChange 来更新state,然后react自动render
var ListTodo = React.createClass({
handleDel: function (e) {
var delIndex = e.target.getAttribute('data-key');
// 更新数据,并使用 onDel 更新到 TodoList 的 state 中,以便 React自动render
this.props.todo.splice(delIndex, 1);
this.props.onDel(this.props.todo);
},
render: function () {
return (
<ul id="todo-list">
{
// {/* 遍历数据 */}
this.props.todo.map(function (item, i) {
return (
<li>
<label>{item}</label>
<button className="destroy" onClick={this.handleDel} data-key={i}>delete</button>
</li>
);
}.bind(this)) // {/* 绑定函数的执行this - 以便 this.handleDel */}
}
</ul>
);
}
}); // 将 TodoList 组件渲染到页面
React.render(<TodoList />, document.getElementById('container'));

5. 总结

入门React的基本语法和使用比较简单,但是想要了解它的工作过程和基本的设计思想,还是需要一点时间的。接下来,在大型系统中使用React肯定又需要更多的时间,你可能还会遇到很多坑,等着你去填。

但是无论现在用还是不用,咱们都不能落伍,该学的还是得掌握一些比较好。大家共勉。

最后,此文章参考了 http://react-china.org/t/todolist/1075 感谢本文作者

-------------------------------------------------------------------------------------------------------------

欢迎关注我的教程:

使用grunt搭建全自动web前端开发环境json2.js源码解读视频

深入理解javascript原型和闭包系列》《css知多少》《微软petshop4.0源码解读视频

------------------------------------------------------------------------------------------------------------

wangEditor,轻量化web富文本编辑器

wangEditor-mobile,适用于手机的富文本编辑器

-------------------------------------------------------------------------------------------------------------

使用React并做一个简单的to-do-list的更多相关文章

  1. 【Bugly干货分享】一起用 HTML5 Canvas 做一个简单又骚气的粒子引擎

    Bugly 技术干货系列内容主要涉及移动开发方向,是由Bugly邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 前言 好吧,说是“粒子引擎”还是大言不 ...

  2. 【 D3.js 入门系列 --- 3 】 做一个简单的图表!

    前面说了几节,都是对文字进行处理,这一节中将用 D3.js 做一个简单的柱形图. 做柱形图有很多种方法,比如用 HTML 的 div 标签,或用 svg . 推荐用 SVG 来做各种图形.SVG 意为 ...

  3. 一起用HTML5 canvas做一个简单又骚气的粒子引擎

    前言 好吧,说是"粒子引擎"还是大言不惭而标题党了,离真正的粒子引擎还有点远.废话少说,先看demo 本文将教会你做一个简单的canvas粒子制造器(下称引擎). 世界观 这个简单 ...

  4. Jmeter初步使用二--使用jmeter做一个简单的性能测试

    经过上一次的初步使用,我们懂得了Jmeter的安装与初步使用的方法.现在,我们使用Jmeter做一个简单的性能测试.该次测试,提交的参数不做参数化处理,Jmeter各元件使用将在介绍在下一博文开始介绍 ...

  5. 用EF DataBase First做一个简单的MVC3报名页面

    使用EF DataBase First做一个简单的MVC3报名网站 ORM(Object Relational Mapping)是面向对象语言中的一种数据访问技术,在ASP.NET中,可以通过ADO. ...

  6. 【 D3.js 入门系列 — 3 】 做一个简单的图表!

    图1. 柱形图 1. 柱形图 前几章的例子,都是对文字进行处理.本章中将用 D3 做一个简单的柱形图.制作柱形图有很多种方法,比如用 HTML 的 <div> 标签,或在 SVG 上绘制 ...

  7. Windows Phone开发(21):做一个简单的绘图板

    原文:Windows Phone开发(21):做一个简单的绘图板 其实我们今天要说的就是一个控件--InkPresenter,这个控件并不是十分强大,没办法和WPF中的InkCanvas相比,估计在实 ...

  8. 使用Multiplayer Networking做一个简单的多人游戏例子-3/3(Unity3D开发之二十七)

    使用Multiplayer Networking做一个简单的多人游戏例子-1/3 使用Multiplayer Networking做一个简单的多人游戏例子-2/3 使用Multiplayer Netw ...

  9. 使用Multiplayer Networking做一个简单的多人游戏例子-2/3(Unity3D开发之二十六)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/51007512 ...

随机推荐

  1. Chrome开发者工具之JavaScript内存分析

    阅读目录 对象大小(Object sizes) 对象的占用总内存树 支配对象(Dominators) V8介绍 Chrome 任务管理器 通过DevTools Timeline来定位内存问题 内存回收 ...

  2. 【Hibernate】一级、二级缓冲

    Hibernate缓冲按级别共分为两种,一级缓冲(Session)和二级缓冲(SessionFactory),有的也说是三种,还有一种是查询缓冲,当然,查询缓冲是依托于二级缓冲. ok,什么是缓冲? ...

  3. hibernate(十)双向关联关系的CRUD

    本文链接:http://www.orlion.ml/28/ 一.保存 1. 假设一个group有多个user,一个user只属于一个group,当保存user对象到数据库中时可以 User u = n ...

  4. poj1703Find them, Catch them(并查集以及路径压缩)

    /* 题目大意:有两个不同的黑帮,开始的时候不清楚每个人是属于哪个的! 执行两个操作 A a, b回答a, b两个人是否在同一帮派,或者不确定 D a, b表示a, b两个人不在同一个帮派 思路:利用 ...

  5. Android自定义ImageView圆形头像

    效果图: 代码如下: RoundImageView.java import cn.comnav.evaluationsystem.R; import android.content.Context; ...

  6. AsyncTask和Handler对比(转)

    1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可 ...

  7. 用Latex写学术论文:作者(Author)&摘要(Abstract)

    标题&作者 1.标题 \title{} "Line breaks (\\) may be used to equalize the length of the title lines ...

  8. Javascript模板引擎mustache.js详解

    mustache.js是一个简单强大的Javascript模板引擎,使用它可以简化在js代码中的html编写,压缩后只有9KB,非常值得在项目中使用.本文总结它的使用方法和一些使用心得,内容不算很高深 ...

  9. a configuration error occurred during startup. place verify the preference field whth the prompt:TomcatJDK name:

    错误一:安装Tomcat的时候出现了错误,这里要是找不到Tomcat6.0我们可以设置其enbale显示就可以,上面的错误解决办法: 错误二:在eclipse中启动Tomcat6.0的时候现在说800 ...

  10. DB表的关系及EF中Fluent API的使用

    现在使用多数的数据库是关系型数据库,那么表与表之间的关系就会显得尤其重要,对于数据的CRUD处理和以后数据的分析有很大的好处.下面是对于数据库中对表关系的理解以及在EF中使用Fluent API来创建 ...