TodoMVC

官网实例

git clone https://github.com/tastejs/todomvc-app-template.git --depth 1

React的配置

  1. 可以使用脚手架
cnpm install -g create-react-app
create-react-app todos
# 支持es6,加上
cnpm install --save-dev babel-plugin-import react-app-rewired
  1. 这里我是自己配置的
    下载项目所需要的包
# 初始化package.json
cnpm init -y
# 下载所需要的包
cnpm i webpack webpack-cli html-webpack-plugin webpack-dev-server babel-loader@7 babel-core babel-preset-env babel-preset-stage-0 babel-plugin-transform-runtime less less-loader css-loader style-loader babel-preset-react -D
cnpm i react react-dom -S
# 可能需要用到路由
cnpm i react-router-dom -S
# 可能需要用到配置文件


webpack.config.js

const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')module.exports = {entry: path.join(__dirname, './src/main.js'),output: {path: path.join(__dirname, './dist'),filename: 'bundle.js'},plugins: [ // 插件new htmlWebpackPlugin({template: path.join(__dirname, './src/index.html'),filename: 'index.html'})],module: {rules: [{ test: /\.css$/, use: ['style-loader', 'css-loader'] }, // 如果想要启用 CSS 模块化,可以为 css-loader 添加 modules 参数即可{ test: /\.less$/, use: ['style-loader', 'css-loader?modules&localIdentName=[name]_[local]-[hash:5]', 'less-loader'] },{ test: /\.jsx?$/, use: 'babel-loader', exclude: /node_modules/ }]}
}

.babelrc

{"presets": ["env", "stage-0","react"],"plugins": ["transform-runtime"]}

main.js

import React from 'react'
import ReactDom from 'react-dom'
import App from './App.jsx'
ReactDom.render(<App></App>,document.querySelector('#app'))

我们下载好的todomvc-app-template里面有个index.html,我们将其复制到我们的App.jsx

  • 根据模板引入的文件,我们需要下载css包cnpm i todomvc-common todomvc-app-css -S
  • 由于React的注释是{},所以我们需要把里面的html注释掉
  • class在React是关键字,className才是JS的类名,需要替换掉;还有for要改为htmlFor、autofocus改为autoFocus,value改为defaultValue,checked改为defaultChecked
  • App.jsx
import React from 'react'
import 'todomvc-common/base.css'
import 'todomvc-app-css/index.css'
export default class App extends React.Component{constructor(props){super(props)}render(){return <div><section className="todoapp"><header className="header"><h1>todos</h1><input className="new-todo" placeholder="What needs to be done?" autoFocus={true}/></header><section className="main"><input id="toggle-all" className="toggle-all" type="checkbox" /><label htmlFor="toggle-all">Mark all as complete</label><ul className="todo-list"><li className="completed"><div className="view"><input className="toggle" type="checkbox" /><label>Taste JavaScript</label><button className="destroy"></button></div><input className="edit" defaultValue="Create a TodoMVC template" /></li><li><div className="view"><input className="toggle" type="checkbox" /><label>Buy a unicorn</label><button className="destroy"></button></div><input className="edit" defaultValue="Rule the web" /></li></ul></section><footer className="footer"><span className="todo-count"><strong>0</strong> item left</span><ul className="filters"><li><a className="selected" href="#/">All</a></li><li><a href="#/active">Active</a></li><li><a href="#/completed">Completed</a></li></ul><button className="clear-completed">Clear completed</button></footer></section><footer className="info"><p>Double-click to edit a todo</p><p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p><p>Created by <a href="http://todomvc.com">you</a></p><p>Part of <a href="http://todomvc.com">TodoMVC</a></p></footer></div>}
}

启动项目(在package.json配置启动项)

    "serve": "webpack-dev-server --open --port 3000 --hot"
cnpm run serve


以上对React配置比较熟悉可以不用看,或则使用脚手架;想要自己写模板也可以;直接跳过

实现业务功能

渲染数据

  1. 模拟一个数据,在main引入
let todos = [{id:1,do:'learn vue',bool:false},{id:2,do:'learn react',bool:false},{id:3,do:'learn angular',bool:false},
]
// ReactDom.render(<App {...todos}></App>,document.querySelector('#app'))
ReactDom.render(<App data={todos}></App>,document.querySelector('#app'))
  1. 将数据保存在我们的App.jsx
    constructor(props){super(props)// 1. 将数据储存在我们的state状态里面this.state = {todos:props.data}}
  1. 渲染数据
             <ul className="todo-list">{/* 1.1.循环我们的数据 */}{this.state.todos.map((todo,index)=>{return <li className=className={todo.bool?"completed":""} key={todo.id}><div className="view"><input className="toggle" type="checkbox" onChange={()=>{this.changeCheckHandle(index)}} defaultChecked={todo.bool}/><label>{todo.do}</label><button className="destroy"></button></div><input className="edit" defaultValue={todo.do} /></li>})}</ul>
  1. 当点击勾选按钮,(即checkbox改变时),那么数据重新改变
   changeCheckHandle = (index)=>{let todos = this.state.todos.map((item,i)=>{if(i==index){item.bool = !item.bool}return item})this.setState({todos})}
  1. 我们可以查看一下我们的数据是否已经改变

双击编辑数据

  1. 我们从模板可知,完成状态是给每一个todo(li)添加一个类editing,完成状态是completed
  2. 我们只需要在双击时给该条todo添加一个类editing即可进入编辑状态(在这里聚焦我试了好多方式总是有bug,后面我在试试后更)
  3. 当使用enter编辑成功,使用esc退出编辑
        this.state = {todos:props.data,newTodo:{}//这是用来判断是否进入编辑状态}
                 // return <li className={[todo.bool?"completed":"",this.state.newTodo==todo?"editing":""]} key={todo.id}>return <li className={(todo.bool?"completed":"")+" "+(this.state.newTodo==todo?"editing":"")} key={todo.id}><div className="view"><input className="toggle" type="checkbox" onChange={()=>{this.changeCheckHandle(index)}} defaultChecked={todo.bool}/><label onDoubleClick={()=>{this.editTodoHandle(todo)}}>{todo.do}</label><button className="destroy"></button></div><input className="edit"  autoFocus defaultValue={todo.do} ref="editItem"onKeyUp = {(e)=>{this.editedHandle(e,index)}}/></li>
 // 进入编辑状态editTodoHandle = (item)=>{this.refs.autofocus = truedocument.querySelectorAll('.edit')[0].autofocus = truedocument.querySelectorAll('.edit')[1].autofocus = truedocument.querySelectorAll('.edit')[2].autofocus = truethis.setState({newTodo:item})}// 编辑完成editedHandle(e,index){// 如果是按退出键退出编辑状态=27if(e.keyCode==27){this.setState({newTodo:{}})return;}if(e.keyCode==13){let todos = this.state.todos.map((item,i)=>{if(i==index){item.do = e.target.value}return item})this.setState({todos,newTodo:{}})}   }

点击叉号.destroy按钮删除该条数据

  1. 绑定事件
onClick={()=>{this.delTodoHandle}}
  1. 点击删除该条数据(处理函数)
 delTodoHandle(index){let todos = this.state.todostodos.splice(index,1)// 保存数据this.setState({todos})}

解决现实item left未完成的数量

<span className="todo-count"><strong>{this.state.todos.filter((t)=>!t.bool).length}</strong>item left</span>

清除已完成Clear completed

  1. 绑定事件
<button className="clear-completed" onClick={this.clearCompletedHandle}>Clear completed</button>
  1. 清除已完成事件
 clearCompletedHandle(){let todos = this.state.todos.filter(t=>!t.bool)this.setState({todos})}

选中完成,未完成,所有的项目completed,active,all

  1. 监听路由的变化(window.location.hash
  2. 我们可以返回一个值,让todo每一项遍历是我们返回的值,而不是todos(当然也可以直接操作设置todos数据setState,不过要考虑改变了原来的数据todos
  3. 定义一个数组存放todos数据
        this.state = {todos:props.data,newTodo:{},newTodos:[] //用来拷贝todos}
  1. 定义一个函数处理路由规则,判断路由的哈希值
 hashChangeHandle(){// console.log(location.hash.startsWith('/active',1))// 这是正在完成项目,即未完成(bool:false)let newTodos = []if(location.hash.startsWith('/active',1)){newTodos = this.state.todos.filter(t=>!t.bool)}else if(location.hash.startsWith('/completed',1)){newTodos = this.state.todos.filter(t=>t.bool)}else{newTodos = this.state.todos}this.setState({newTodos})}
  1. 初始化就调用它(利用React的生命周期函数)
 componentWillMount(){this.hashChangeHandle()}
  1. 监听hash值的改变
 componentDidMount(){window.onhashchange = function(){// console.log(this)this.hashChangeHandle()}.bind(this)}
  1. 类的高亮,根据路由变化给类添加选中样式,这个比吃炒面还简单…
             <ul className="filters"><li><a className={"/"==window.location.hash.substring(1)?"selected":""} href="#/" >All</a></li><li><a className={"/active"==window.location.hash.substring(1)?"selected":""} href="#/active">Active</a></li><li><a className={"/completed"==window.location.hash.substring(1)?"selected":""} href="#/completed">Completed</a></li></ul>

添加一条数据

             <input className="new-todo" ref="addTodo"placeholder="What needs to be done?" autoFocus={true}onKeyUp={(e)=>{this.addTodoHandle(e)}}/>
 addTodoHandle(e){//如果文本框为空或则不是使用enter不执行任何操作let addVal = this.refs.addTodo.valuelet todos = this.state.todosif(e.keyCode!==13 || !addVal.length){return;}let id = todos.length?todos[todos.length-1].id+1:1let todo = {id,do:addVal,bool:false}todos.push(todo)this.setState({todos,newTodos:todos})// 清空this.refs.addTodo.value = ''}

全选与反选

  1. 先实现反选:即所有todo都是完成completed时,我们的箭头才是高亮,其余情况不高亮
  2. 我们首先使用defaultChecked进行绑定,然而,该属性只在初始的时候有用,怎么改变todo的选择都没用
<input id="toggle-all" className="toggle-all" type="checkbox" defaultChecked={this.state.newTodos.every(t=>t.bool)} ref="selectedCheck"/>
  1. 使用checkedLink={}又会报错,只能使用js操作checked来实现反选
    changeCheckHandle = (index)=>{let todos = this.state.todos.map((item,i)=>{if(i==index){item.bool = !item.bool}return item})this.setState({todos,newTodos:todos})this.refs.selectedCheck.checked=this.state.newTodos.every(t=>t.bool)}


4. 全选:即点击三角图标,如果它高亮,所有的todo项都是完成

 // 全选toggleHandle = ()=>{let todos = this.state.todoslet toggle = document.querySelectorAll('.toggle')todos.forEach(item => {item.bool = this.refs.selectedCheck.checked  })for(var i=0;i< toggle.length;i++){toggle[i].checked = this.refs.selectedCheck.checked  }this.setState({todos,newTodos:todos})}
 <input className="toggle" type="checkbox" onChange={()=>{this.changeCheckHandle(index)}} defaultChecked={todo.bool}/>

数据持久化(本地储存)

  1. 获取本地储存的todos没有则定义为空数组(main.js)
let todos = JSON.parse(localStorage.getItem('todos') || '[]')
ReactDom.render(<App data={todos}></App>,document.querySelector('#app'))
  1. 当todos数据有变化时,立刻储存
    shouldComponentUpdate(nextProps,nextState){localStorage.setItem('todos',JSON.stringify(nextState.todos))return true}

bug解决

  1. 我们使用了newTodos,那么清除已完成的也要设置newTodos
 clearCompletedHandle(){// console.log(this.state)let todos = this.state.todos.filter(t=>!t.bool)this.setState({todos,newTodos:todos})}
<button className="clear-completed" onClick={()=>{this.clearCompletedHandle()}}>Clear completed</button>
  1. 当添加一条数据时,三角按钮#toggle不会跟我们想象那样变化
    shouldComponentUpdate(nextProps,nextState){this.refs.selectedCheck.checked=this.state.newTodos.every(t=>t.bool)return true}
  1. 编辑状态
 editedHandle(e,index){.......this.setState({todos,newTodos:todos,newTodo:{}})}    }
  1. 因为我没做过测试,所以可能有其它bug存在…

资料

子组件向父组件传值

React实现todos相关推荐

  1. 微前端解决方案初探 02 微前端框架 single-spa

    single-spa 概述 single-spa 是一个实现微前端架构的框架. 在 single-spa 框架中有三种类型的微前端应用: single-spa application / parcel ...

  2. 狼叔来找翻译人员了--plato--持续翻译中.....

    这个项目是使用 Create React App引导的. 你将在下面找到如何执行常见任务的一些信息. 你可以在这找到最新版本的指南. 目录 更新到新版本 发送反馈 文件夹结构 可用的脚本 npm st ...

  3. 从Preact了解一个类React的框架是怎么实现的(一): 元素创建

    首先欢迎大家关注我的掘金账号和Github博客,也算是对我的一点鼓励,毕竟写东西没法获得变现,能坚持下去也是靠的是自己的热情和大家的鼓励. 之前分享过几篇关于React的文章: React技术内幕: ...

  4. React学习笔记3:React脚手架

    使用create-react-app创建react应用 react脚手架 xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目 包含了所有需要的配置(语法检查.jsx编译.devServe ...

  5. uiswitchbutton 点击不改变状态_Redux 包教包会(一):解救 React 状态危机

    前端应用的状态管理日益复杂.随着大前端时代的到来,前端愈来愈注重处理逻辑,而不只是专注 UI 层面的改进,而以 React 为代表的前端框架的出现,大大简化了我们编写 UI 界面的复杂度.虽然 Rea ...

  6. [转] React 是什么

    用脚本进行DOM操作的代价很昂贵.有个贴切的比喻,把DOM和JavaScript各自想象为一个岛屿,它们之间用收费桥梁连接,js每次访问DOM,都要途径这座桥,并交纳"过桥费",访 ...

  7. React笔记:React基础(2)

    1. JSX JSX是一种拥有描述UI的JavaScript扩展语法,React使用这种语法描述组件的UI. 1.1 基本语法 JSX可以嵌套多个HTML标签,可以使用大部分符号HTML规范的属性. ...

  8. Redux 学习总结 (React)

    在 React 的学习和开发中,如果 state (状态)变得复杂时(例如一个状态需要能够在多个 view 中使用和更新),使用 Redux 可以有效地管理 state,使 state tree 结构 ...

  9. React系列---Redux高阶运用

    参考资料:<深入React技术栈> 高阶reducer 高阶函数是指将函数作为参数或返回值的函数,高阶reducer就是指将reducer作为参数或返回值的函数. 在Redux架构中,re ...

  10. React文档(十四)深入JSX

    根本上,JSX只是为React.createElement(component, props, ...children)函数提供语法糖.JSX代码是这样的: <MyButton color=&q ...

最新文章

  1. boost::phoenix::delete和using boost::phoenix::new_相关的测试程序
  2. Qt Designer的编辑模式
  3. 前端学习(2259)查看历史
  4. 记录:Android中StackOverflow的问题
  5. pythonic code_Pythonic Code (Part III)
  6. 你愿意一辈子当一个打工的吗
  7. 231 · 自动补全
  8. mathtype7.x与基本使用
  9. java案例代码20--斗地主V2
  10. SolidWorks2016 从入门到入坟 下载安装+画图
  11. 考研,我就推荐几个常用的APP
  12. 吉司机线段树(segment tree beats!)
  13. Pulsar的Proxy支持和SNI路由 - 修改...
  14. 西安电子科技大学期末C语言考试2022年真题 --LXY
  15. 携程称将继续参与一嗨私有化 Ocean联合体有33.2%投票权
  16. 微信小程序项目优化(初学者)
  17. 300兆的网速测试软件,光纤是300兆的网速,用电脑测试只有100兆,光猫坏了?
  18. 【020】翼辉信息董事长韩辉先生受北京工业大学邀请发表学术演讲
  19. 购物车 mongoDB
  20. matlab运行一直正忙,MATLAB运行时一直处于忙的状态,是不是程序存在死循环

热门文章

  1. php语言简述_PHP语言的简介
  2. 神经元细胞分布全身吗,人体神经细胞分布图
  3. CentOS7--安装谷歌浏览器--详细步骤
  4. html中form表单提交中文乱码问题基本解决办法
  5. 将会彻底破坏你的树莓派的六件事(官网翻译)
  6. python 广告联盟_利用京东联盟API获取自定义推广链接
  7. POJ 2387.Til the Cows Come Home
  8. uib-datepicker-popup使用
  9. 年度Sweb绩效考评表
  10. 华为员工:表面光鲜 工作十年买不起房