react-todo实战(二)之react/es6

看过一遍webpack/es6/react的基础之后,感觉还算不上真正入门react的实战开发,于是乎想边学边搞一个小项目、把这三webpack/es6/react结合起来用一遍,从实际小项目中反过来学习基础要点。
(react的基础学习在 前面的学习。)

全部亲手一行行敲完后,终于有了这个稍微成型的入门小实例:
完整代码github:react-todo
演示链接demo:react-todo
实践中收货良多,过后便稍微总结一下系列的笔记。

module的导入导出

module的导入

1
2
3
4
5
6
7
8
9
10
11
import React from 'react';
import ReactDOM from 'react-dom';
//import { render } from 'react-dom'; //导入模块的某一个功能函数
import todoDb from './todoDb.js'; //导入本地数据库localStorage模块
import TodoSearch from './TodoSearch.js'; //导入搜索任务组件
import TodoMain from './TodoMain.js'; //导入main任务组件
import TodoAdd from './TodoAdd.js'; //导入add新增任务组件
import './todo.scss'; //导入scss
import todoSearch from './todo-search.png'; //导入图片

module的导出

1
2
3
4
5
//默认导出
export default class TodoSearch extends React.Component{}
//命名导出
class TodoSearch extends React.Component{}
export TodoSearch;

class

直接来看MainItem.js组件里的一个 class的结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MainItem extends React.Component{
deleteClick(){
this.props.deleteTask(this.props.index);
}
changeClick(){
let isDone = !this.props.isDone;
this.props.changeDone(this.props.index,isDone);
}
render(){
let isDoneClass = this.props.isDone ? 'li-status' : 'li-status li-status-no'; //已/未完成的样式
let isDoneTxt = this.props.isDone ? '已完成' : '未完成'; //已/未完成的文字
return(
<li>
<span className="li-text">{this.props.index+1}.{this.props.task}</span>
<button className="li-delete" onClick={this.deleteClick.bind(this)}>删除</button>
<button className={isDoneClass} onClick={this.changeClick.bind(this)}>{isDoneTxt}</button>
</li>
)
}
}

类MainItem里面,定义方法不用写function,方法之间不用写逗号 ,
render里面的dom元素可以绑定这些事件:onClick={this.deleteClick.bind(this)}
虚拟事件对象一般有如下这些,它们是 react 封装好的,跟浏览器本地事件很像:
onKeyDown onKeyPress onKeyUp onFocus onBlur onClick onDoubleClick 等;
具体可 查阅文档

props refs

props:父组件向子组件传递数据:

1
2
3
4
5
//Todo.js中向子组件传递 searchTasks 方法
<TodoSearch searchTasks={this.searchTasks.bind(this)}/>
//TodoSearch.js 通过 this.props 调用
this.props.searchTasks(val);

ref: 获取当前组件下的真实dom:

1
2
3
4
5
//TodoSearch.js搜索组件中,需要获取 input 的值;先给 input 定义 ref
<input type="text" ref='inputSearch' onKeyUp={this.searchUp.bind(this)} placeholder="搜索"/>
//通过 this.refs 访问获取
let val = ReactDOM.findDOMNode(this.refs.inputSearch).value;

spread操作符

Todo.js 通过 spread 向子组件 TodoMain.js 传递 count

1
2
3
4
5
6
//Todo.js 的 render(){}里面:
var count = {
countAll: this.state.tasks.length || 0,
countDone: (this.state.tasks && this.state.tasks.filter((task) => task.isDone)).length || 0
}
<TodoMain {...count}/>

那么,我们在 TodoMain.js 的 this.props 得到的就是一个对象:{countAll:数字, countDone:数字}。

spread 操作符 在子组件向孙组件传递父组件的数据时,特别好用。比如子组件 TodoMain.js 向 孙组件 MainItem.js 传递父组件 Todo.js 的数据:<MainItem {...this.props}/>

几个函数

filter
过滤,遍历数组,对每一个元素触发一个回调函数,经处理判断、保留或删除当前元素,最后返回一个新数组;
如,在 Todo.js 的 searchTasks方法中,就是通过 filter 筛选出搜索结果的:

1
let searchResult = this.todoDb.get().filter((tasks) => tasks.task.includes(val));

this.todoDb.get() 得到的是一个对象为元素的数组,具体形式如下:
[{'task':'任务1','isDone':true},{'task':'任务2','isDone':false}]
箭头函数无需 return 。

map
遍历数组,对每一个元素触发一个回调函数,经处理后生成新的当前元素,最后返回一个新数组;
如:在 TodoMain.js 向 MainItem.js 传递数据时就采用 map 遍历:

1
2
3
this.props.tasks.map((tasks,index) => {
return <MainItem key={index} {...tasks} index={index} {...this.props}/>
})

array1.map(callbackfn[, thisArg])
array1 必选 一个数组对象。
callbackfn 必选 最多可以接受三个参数的函数。
thisArg 可选

回调函数 callbackfn 语法:
function callbackfn(value, index, array1):
Value 数组元素的值。(tasks:数组里的一个对象,如:{‘task’:’任务1’,’isDone’:true})
index 数组元素的数字索引。(index:当前元素的索引)
array1 包含该元素的数组对象。

some()、every()
遍历数组,对每一个元素触发一个回调函数,通过判断,返回一个布尔值。
some():只要有一个满足判断,就返回true,
every():只要有一项不满足判断,就返回false。

todoDb
本地数据采用 localStorage 来存取,具体详见 todoDb.js 模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default class todoDb{
constructor(name){
this.name = name;
if(JSON.stringify(this.get(this.name)) == '{}' ){
// this.set([{'task':'默认的任务','isDone':false}]);
this.set([]);
}
}
set(val){
window.localStorage.setItem(this.name,JSON.stringify(val));
}
get(){
return JSON.parse(window.localStorage.getItem(this.name)) || {};
}
}

localStorage 不能存数组,所以关键是
存的时候(set)先把数组对象转成字符串: JSON.stringify()
取的时候(get)先把字符串转成数组对象: JSON.parse()

构造函数 constructor 里先判断如果数据为空,则默认先设置一个空的数组;不然 被 map 遍历时出错(map遍历的是数组)。