最近学完React的最基本概念,闲下来的时候就自己写了一个Todo-List的小应用。这里做个简略的说明,给想好好学React的新手看。

开始之前

这里我用了webpackb做了babel和JSX预处理和模块打包。所以对React和一些ES2015(ES6)的语法要有一定的了解。我相信学习ES2015绝对是划算的,因为它是Js的规范。这里给出学习的地方,阮一峰老师的ECMAScript 6 入门或者babel的相关文档Learn ES2015。

最后的实际效果:

我们需要做到的功能有:

可以在最上面的input里,使用回车来添加任务。

在中间的任务列表里,由checkbox来控制任务的状态。

已完成的任务有一个line-through的样式。

当鼠标移到每一个任务时,都会出现删除按钮提供删除。

在底部有一个全选按钮,用于控制所有的任务状态。

还有已完成与总数的显示。

可以清空已完成的任务。

上面就是一个Todo-List最基本的功能,而我们这次就是用React实现上述功能。例子在我的github上可以download下来,可以用作参考:React-Todos

加载npm模块

终于要开始我们的React-Todo的项目了,首先我们就要新建项目,通过npm我们可以很轻松的创建项目,并加载我们所需要的各个组件。大家可以在自己的项目里,用我的package.json去加载所需要的模块。通过命令行进行安装。

$ npm install

这里提一下,因为我们这里仅仅是前端静态的,并不涉及到数据库。所以我自己写了一个非常简单的用于操作localStorage的小模块localDb。所以涉及到数据存储的时候,都是用localStorage来代替数据库。它的原理就是,通过将数据格式化成JSON字符串进行存储,使用的时候就解析JSON字符串。这个模块在我的github的例子里有,需要从那里复制一份来,放在node_modules的文件夹内。

配置webpack

经过一轮漫长的等待,我们终于安装好所需要的各个模块了。我们在开始我们的react的编码前,需要对webpack进行配置。关于webpack的学习,我这里就不赘述了,在前一篇刚讲完。下面直接看一看webpack.config.js。

// webpack.config.js

var path = require('path');

module.exports = {

entry: "./src/entry.js",

output: {

path: path.join(__dirname, 'out'),

publicPath: './out/',

filename: "bundle.js"

},

externals: {

'react': 'React'

},

module: {

loaders: [

{ test: /\.js$/, loader: "jsx!babel", include: /src/},

{ test: /\.css$/, loader: "style!css"},

{ test: /\.scss$/, loader: "style!css!sass"},

{ test: /\.(jpg|png)$/, loader: "url?limit=8192"}

]

}

};

这里一切从简,可以看到入口文件是在src文件夹里的entry.js,然后输出文件放在out文件夹的bundle.js里。

配置一下模块的loaders,先用babel-loader再用jsx-loader。这样子我们就可以让ES6配合JSX编写我们的React组件了。其它的加载器也没什么好说的了,如果不清楚可以翻我上一篇关于webpack的文章。

这里提一下externals属性,这个属性是告诉webpack当遇到require('react')的时候,不去处理并且默认为全局的React变量。这样子,我们就需要在index.html单独用src去加载js。

分析各个组件

App组件

我这里并不会教大家手把手将这个React-Todo做出来,但是可以结合例子进行分析理解。先来看看总的组件,也就是App。

import React from "react";

import LocalDb from "localDb";

import TodoHeader from "./TodoHeader.js";

import TodoMain from "./TodoMain.js";

import TodoFooter from "./TodoFooter.js";

class App extends React.Component {

constructor(){

super();

this.db = new LocalDb('React-Todos');

this.state = {

todos: this.db.get("todos") || [],

isAllChecked: false

};

}

// 判断是否所有任务的状态都完成,同步底部的全选框

allChecked(){

let isAllChecked = false;

if(this.state.todos.every((todo)=> todo.isDone)){

isAllChecked = true;

}

this.setState({todos: this.state.todos, isAllChecked});

}

// 添加任务,是传递给Header组件的方法

addTodo(todoItem){

this.state.todos.push(todoItem);

this.allChecked();

this.db.set('todos',this.state.todos);

}

// 改变任务状态,传递给TodoItem和Footer组件的方法

changeTodoState(index, isDone, isChangeAll=false){

if(isChangeAll){

this.setState({

todos: this.state.todos.map((todo) => {

todo.isDone = isDone;

return todo;

}),

isAllChecked: isDone

})

}else{

this.state.todos[index].isDone = isDone;

this.allChecked();

}

this.db.set('todos', this.state.todos);

}

// 清除已完成的任务,传递给Footer组件的方法

clearDone(){

let todos = this.state.todos.filter(todo => !todo.isDone);

this.setState({

todos: todos,

isAllChecked: false

});

this.db.set('todos', todos);

}

// 删除当前的任务,传递给TodoItem的方法

deleteTodo(index){

this.state.todos.splice(index, 1);

this.setState({todos: this.state.todos});

this.db.set('todos', this.state.todos);

}

render(){

var props = {

todoCount: this.state.todos.length || 0,

todoDoneCount: (this.state.todos && this.state.todos.filter((todo)=>todo.isDone)).length || 0

};

return (

)

}

}

React.render(, document.getElementById("app"));

用ES6写React最大的不同就是,组件可以通过继承React.Components来得到,并且初始化state也不需要冗长的getInitalialState,直接在构造函数里操作this.state即可。更优秀的便是...spread扩展操作符,可以让我们省下一堆不必要的代码,这个接下来再说。

App状态state

我们知道React的主流思想就是,所有的state状态和方法都是由父组件控制,然后通过props传递给子组件,形成一个单方向的数据链路,保持各组件的状态一致。所以我们在这个父组件App上,看的东西稍微有点多。一点点来看:

constructor(){

super();

this.db = new LocalDb('React-Todos');

this.state = {

todos: this.db.get("todos") || [],

isAllChecked: false

};

}

在App组件的constructor内,我们先是初始化了我们的localStorage的数据库,放在了this.db上。然后便是初始化了state,分别有两个,一个是todos的列表,一个是所有的todos是否全选的状态。

App方法

// 判断是否所有任务的状态都完成,同步底部的全选框

allChecked()

// 添加一个任务,参数是一个todoItem的object

addTodo(todoItem)

// 改变任务的状态,index是第几个,isDone是状态,isChangeAll是控制全部状态的

changeTodoState(index, isDone, isChangeAll=false) // 参数默认位false

// 清空已完成

clearDone()

// 删除面板上第几个任务

deleteTodo(index)

// react用于渲染的函数

render(){

}

我们可以从render函数看到整个组件的结构,可以看到其实结构非常简单,就是上中下。上面的TodoHeader自然就是用来输入任务的地方,中间就是展示并操作todo-list的,而底部就是显示数据并提供特殊操作。这里还是要提醒一句,所有标签都必须闭合,即使是非结对的,也要用斜杠闭合上。

render(){

var props = {

todoCount: this.state.todos.length || 0,

todoDoneCount: (this.state.todos && this.state.todos.filter((todo)=>todo.isDone)).length || 0

};

return (

)

}

我们可以看到,其他的方法都是传到子组件上,就不一一详细说如何实现的了。总体的思想就是,方法在父组件定义,通过props传给需要的子组件进行调用传参,最后返回到父组件上执行函数,存储数据、改变state和重新render。方法需要bind(this),不然方法内部的this指向会不正确。

计算需要的数据后,通过props传递到子组件。如果细心的同学应该可以看到像这样的{...props},这就是我之前说过的spread操作符。如果我们没有用这个操作符,就要这样写:

// spread操作符

最佳的实践就是,当父组件传props给子组件,然后子组件要将props转发给孙子组件的时候,spread操作符简直让人愉悦!可以对一堆麻烦又丑又长的代码可以say goodbye了!

最后我们将整个App渲染到DOM上即可。

React.render(, document.getElementById("app"));

AppHeader组件

import React from "react";

class TodoHeader extends React.Component {

// 绑定键盘回车事件,添加新任务

handlerKeyUp(event){

if(event.keyCode === 13){

let value = event.target.value;

if(!value) return false;

let newTodoItem = {

text: value,

isDone: false

};

event.target.value = "";

this.props.addTodo(newTodoItem);

}

}

render(){

return (

)

}

}

export default TodoHeader;

到了子组件,方法就没那么多了,一般子组件就是绑定事件。可以看到在子组件绑定了keyUp事件,用来确定回车键并调用父组件传来的addTodo(),将新生成的todo任务作为参数传入。

AppFooter组件

import React from "react";

export default class TodoFooter extends React.Component{

// 处理全选与全不选的状态

handlerAllState(event){

this.props.changeTodoState(null, event.target.checked, true);

}

// 绑定点击事件,清除已完成

handlerClick(){

this.props.clearDone();

}

render(){

return (

{this.props.todoDoneCount}已完成 / {this.props.todoCount}总数

清除已完成

)

}

}

我们先来看看这个footer上有哪些方法。第一个就是处理todo状态的,它通过底部的checkbox的change事件触发。然后就是清空已完成的按钮的点击事件的方法handlerClick()。然后下面的数据显示,就通过props的值进行显示。

TodoMain

import React from "react";

import TodoItem from "./TodoItem.js"

export default class TodoMain extends React.Component{

// 遍历显示任务,转发props

render(){

return (

{this.props.todos.map((todo, index) => {

return

})}

)

}

}

Main组件的作用就是,将props传过来的todos遍历显示出来。所以对每一个todo的细致操作都是放在TodoItem上。

TodoItem

import React from "react";

export default class TodoItem extends React.Component{

// 处理任务是否完成状态

handlerChange(){

let isDone = !this.props.isDone;

this.props.changeTodoState(this.props.index, isDone);

}

// 鼠标移入

handlerMouseOver(){

React.findDOMNode(this.refs.deleteBtn).style.display = "inline";

}

// 鼠标移出

handlerMouseOut(){

React.findDOMNode(this.refs.deleteBtn).style.display = "none";

}

// 删除当前任务

handlerDelete(){

this.props.deleteTodo(this.props.index);

}

render(){

let doneStyle = this.props.isDone ? {textDecoration: 'line-through'} : {textDecoration: 'none'};

return (

onMouseOver={this.handlerMouseOver.bind(this)}

onMouseOut={this.handlerMouseOut.bind(this)}

>

{this.props.text}

删除

)

}

}

在TodoItem主要处理多个交互,包括修改任务状态,删除任务。还有就是鼠标移到相应的任务上才显示删除按钮。

我们可以看到render()函数,是控制了任务的样式。标签内的style是需要接受一个对象的,所以所有的CSS属性名,都要变成驼峰形的。

总结

其实真正的回过头看React-Todos,会觉得React带给我们的组件化的思想用起来太舒服了。我们通过父组件来控制状态,并通过props传递,来保证组件内的状态一致。我们可以非常有效的维护我们的交互代码,因为我们一眼就知道,这个事件属于哪个组件管理。它的模型其实非常轻,只有View层,但是它带给我们全新的书写前端组件的方法是非常好的,我个人认为如果未来的站点交互性愈来愈多,React是很有可能代替jQuery成为必备的技能。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

react 最佳入门_详解React-Todos入门例子相关推荐

  1. java中batch基础_详解Spring batch 入门学习教程(附源码)

    详解Spring batch 入门学习教程(附源码) 发布时间:2020-09-08 00:28:40 来源:脚本之家 阅读:99 作者:achuo Spring batch 是一个开源的批处理框架. ...

  2. diff算法_详解 React 16 的 Diff 策略

    这是我 Deep In React 系列的第二篇文章,如果还没有读过的强烈建议你先读前一篇:详谈 React Fiber 架构(1). 前言 我相信在看这篇文章的读者一般都已经了解过 React 16 ...

  3. js路由在php上面使用,React中路由使用详解

    这次给大家带来React中路由使用详解,React中路由使用的注意事项有哪些,下面就是实战案例,一起来看一下. 路由 通过 URL 映射到对应的功能实现,React 的路由使用要先引入 react-r ...

  4. webpack搭建php服务器,webpack搭建react开发环境步骤详解

    这次给大家带来webpack搭建react开发环境步骤详解,webpack搭建react开发环境的注意事项有哪些,下面就是实战案例,一起来看一下.mkdir react-redux && ...

  5. android 回退函数,详解React Native监听Android回退按键与程序化退出应用

    详解React Native监听Android回退按键与程序化退出应用 发布时间:2020-09-29 09:25:52 来源:脚本之家 阅读:137 作者:lqh 详解React Native监听A ...

  6. 回退监听android,详解React Native监听Android回退按键与程序化退出应用

    详解React Native监听Android回退按键与程序化退出应用 前言 我们知道Android回退按键,会控制页面返回, 并且退出应用并非真正意义退出,仍在后台运行,所以在某些场景下需要监控an ...

  7. python 快速排序_小白入门知识详解:Python实现快速排序的方法(含实例代码)...

    前言: 今天为大家带来的内容是:小白入门知识详解:Python实现快速排序的方法(含实例代码)希望通过本文的内容能够对各位有所帮助,喜欢的话记得点赞转发收藏不迷路哦!!! 提示: 这篇文章主要介绍了P ...

  8. python爬虫入门实例-Python爬虫天气预报实例详解(小白入门)

    本文研究的主要是Python爬虫天气预报的相关内容,具体介绍如下. 要求是把你所在城市过去一年的历史数据爬出来. 分析网站 我们可以看到,我们需要的天气数据都是放在图表上的,在切换月份的时候,发现只有 ...

  9. python编程入门与案例详解pdf-Python爬虫天气预报实例详解(小白入门)

    本文研究的主要是Python爬虫天气预报的相关内容,具体介绍如下. 这次要爬的站点是这个:http://www.weather.com.cn/forecast/ 要求是把你所在城市过去一年的历史数据爬 ...

  10. python编程入门与案例详解-Python爬虫天气预报实例详解(小白入门)

    本文研究的主要是Python爬虫天气预报的相关内容,具体介绍如下. 这次要爬的站点是这个:http://www.weather.com.cn/forecast/ 要求是把你所在城市过去一年的历史数据爬 ...

最新文章

  1. 金税接口调用实例 java_Java 常见面试题
  2. python语法错误概述_python语法错误
  3. 新版火狐浏览器怎么调整字体 火狐浏览器字体调整技巧分享
  4. a 标签中 rel=“noopener noreferrer“属性的含义和功能
  5. 3007基于二叉链表的二叉树叶子结点到根结点的路径的求解(附DFS在树里的应用分析,思路详解)
  6. 文件上传表单 上传文件的细节 文件上传下载和数据库结合
  7. java设计模式适配器模式_Java解释器设计模式
  8. 中国移动下一代移动技术将选择LTE
  9. bim 骗局_建筑设计BIM工程师是个好职业吗?
  10. P2P技术原理及应用
  11. 华为认证计算机英语,hcna考试是英文还是中文
  12. 云呐|RFID资产盘点系统带审批(rfid库存盘点功能)
  13. 第二章 工厂设计模式
  14. 自学MBA,我推荐你看这本《MBA必读12篇》
  15. 八、PyQt5绘图技术
  16. Maya offline document
  17. 90%的预测准确率覆盖30%的订单,滴滴出行“猜您要去”目的地预测系统是怎么做的?...
  18. Markdown 前言
  19. 超级好看又易上手教你用python画樱花
  20. 1024程序员节!!

热门文章

  1. 通用模块脚本使用案例:玩家与NPC对话
  2. Python爬虫实战-小说网站爬虫开发
  3. ssm框架整合以及登录案例
  4. python area函数,python3 函数
  5. 数据归一化和标准化的区别
  6. Facebook努力回归中国,微博会被人们放弃吗?
  7. CRMEB制作docker-compose
  8. 第5章、解析Hello,world!知其然,更要知其所以然(从零开始学Android)
  9. [Windows] 专业的家谱族谱制作软件My Family Tree v10.3.4
  10. hdu_5145_NPY and girls(莫队算法+组合)