React实现todos
TodoMVC
官网实例
git clone https://github.com/tastejs/todomvc-app-template.git --depth 1
React的配置
- 可以使用脚手架
cnpm install -g create-react-app
create-react-app todos
# 支持es6,加上
cnpm install --save-dev babel-plugin-import react-app-rewired
- 这里我是自己配置的
下载项目所需要的包
# 初始化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配置比较熟悉可以不用看,或则使用脚手架;想要自己写模板也可以;直接跳过
实现业务功能
渲染数据
- 模拟一个数据,在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'))
- 将数据保存在我们的App.jsx
constructor(props){super(props)// 1. 将数据储存在我们的state状态里面this.state = {todos:props.data}}
- 渲染数据
<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>
- 当点击勾选按钮,(即
checkbox
改变时),那么数据重新改变
changeCheckHandle = (index)=>{let todos = this.state.todos.map((item,i)=>{if(i==index){item.bool = !item.bool}return item})this.setState({todos})}
- 我们可以查看一下我们的数据是否已经改变
双击编辑数据
- 我们从模板可知,完成状态是给每一个todo(
li
)添加一个类editing
,完成状态是completed
- 我们只需要在双击时给该条todo添加一个类
editing
即可进入编辑状态(在这里聚焦我试了好多方式总是有bug,后面我在试试后更) - 当使用
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
按钮删除该条数据
- 绑定事件
onClick={()=>{this.delTodoHandle}}
- 点击删除该条数据(处理函数)
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
- 绑定事件
<button className="clear-completed" onClick={this.clearCompletedHandle}>Clear completed</button>
- 清除已完成事件
clearCompletedHandle(){let todos = this.state.todos.filter(t=>!t.bool)this.setState({todos})}
选中完成,未完成,所有的项目completed,active,all
- 监听路由的变化(
window.location.hash
) - 我们可以返回一个值,让todo每一项遍历是我们返回的值,而不是
todos
(当然也可以直接操作设置todos
数据setState
,不过要考虑改变了原来的数据todos
) - 定义一个数组存放todos数据
this.state = {todos:props.data,newTodo:{},newTodos:[] //用来拷贝todos}
- 定义一个函数处理路由规则,判断路由的哈希值
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})}
- 初始化就调用它(利用React的生命周期函数)
componentWillMount(){this.hashChangeHandle()}
- 监听hash值的改变
componentDidMount(){window.onhashchange = function(){// console.log(this)this.hashChangeHandle()}.bind(this)}
- 类的高亮,根据路由变化给类添加选中样式,这个比吃炒面还简单…
<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 = ''}
全选与反选
- 先实现反选:即所有todo都是完成completed时,我们的箭头才是高亮,其余情况不高亮
- 我们首先使用defaultChecked进行绑定,然而,该属性只在初始的时候有用,怎么改变todo的选择都没用
<input id="toggle-all" className="toggle-all" type="checkbox" defaultChecked={this.state.newTodos.every(t=>t.bool)} ref="selectedCheck"/>
- 使用
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}/>
数据持久化(本地储存)
- 获取本地储存的todos没有则定义为空数组(main.js)
let todos = JSON.parse(localStorage.getItem('todos') || '[]')
ReactDom.render(<App data={todos}></App>,document.querySelector('#app'))
- 当todos数据有变化时,立刻储存
shouldComponentUpdate(nextProps,nextState){localStorage.setItem('todos',JSON.stringify(nextState.todos))return true}
bug解决
- 我们使用了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>
- 当添加一条数据时,三角按钮
#toggle
不会跟我们想象那样变化
shouldComponentUpdate(nextProps,nextState){this.refs.selectedCheck.checked=this.state.newTodos.every(t=>t.bool)return true}
- 编辑状态
editedHandle(e,index){.......this.setState({todos,newTodos:todos,newTodo:{}})} }
- 因为我没做过测试,所以可能有其它bug存在…
资料
子组件向父组件传值
React实现todos相关推荐
- 微前端解决方案初探 02 微前端框架 single-spa
single-spa 概述 single-spa 是一个实现微前端架构的框架. 在 single-spa 框架中有三种类型的微前端应用: single-spa application / parcel ...
- 狼叔来找翻译人员了--plato--持续翻译中.....
这个项目是使用 Create React App引导的. 你将在下面找到如何执行常见任务的一些信息. 你可以在这找到最新版本的指南. 目录 更新到新版本 发送反馈 文件夹结构 可用的脚本 npm st ...
- 从Preact了解一个类React的框架是怎么实现的(一): 元素创建
首先欢迎大家关注我的掘金账号和Github博客,也算是对我的一点鼓励,毕竟写东西没法获得变现,能坚持下去也是靠的是自己的热情和大家的鼓励. 之前分享过几篇关于React的文章: React技术内幕: ...
- React学习笔记3:React脚手架
使用create-react-app创建react应用 react脚手架 xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目 包含了所有需要的配置(语法检查.jsx编译.devServe ...
- uiswitchbutton 点击不改变状态_Redux 包教包会(一):解救 React 状态危机
前端应用的状态管理日益复杂.随着大前端时代的到来,前端愈来愈注重处理逻辑,而不只是专注 UI 层面的改进,而以 React 为代表的前端框架的出现,大大简化了我们编写 UI 界面的复杂度.虽然 Rea ...
- [转] React 是什么
用脚本进行DOM操作的代价很昂贵.有个贴切的比喻,把DOM和JavaScript各自想象为一个岛屿,它们之间用收费桥梁连接,js每次访问DOM,都要途径这座桥,并交纳"过桥费",访 ...
- React笔记:React基础(2)
1. JSX JSX是一种拥有描述UI的JavaScript扩展语法,React使用这种语法描述组件的UI. 1.1 基本语法 JSX可以嵌套多个HTML标签,可以使用大部分符号HTML规范的属性. ...
- Redux 学习总结 (React)
在 React 的学习和开发中,如果 state (状态)变得复杂时(例如一个状态需要能够在多个 view 中使用和更新),使用 Redux 可以有效地管理 state,使 state tree 结构 ...
- React系列---Redux高阶运用
参考资料:<深入React技术栈> 高阶reducer 高阶函数是指将函数作为参数或返回值的函数,高阶reducer就是指将reducer作为参数或返回值的函数. 在Redux架构中,re ...
- React文档(十四)深入JSX
根本上,JSX只是为React.createElement(component, props, ...children)函数提供语法糖.JSX代码是这样的: <MyButton color=&q ...
最新文章
- boost::phoenix::delete和using boost::phoenix::new_相关的测试程序
- Qt Designer的编辑模式
- 前端学习(2259)查看历史
- 记录:Android中StackOverflow的问题
- pythonic code_Pythonic Code (Part III)
- 你愿意一辈子当一个打工的吗
- 231 · 自动补全
- mathtype7.x与基本使用
- java案例代码20--斗地主V2
- SolidWorks2016 从入门到入坟 下载安装+画图
- 考研,我就推荐几个常用的APP
- 吉司机线段树(segment tree beats!)
- Pulsar的Proxy支持和SNI路由 - 修改...
- 西安电子科技大学期末C语言考试2022年真题 --LXY
- 携程称将继续参与一嗨私有化 Ocean联合体有33.2%投票权
- 微信小程序项目优化(初学者)
- 300兆的网速测试软件,光纤是300兆的网速,用电脑测试只有100兆,光猫坏了?
- 【020】翼辉信息董事长韩辉先生受北京工业大学邀请发表学术演讲
- 购物车 mongoDB
- matlab运行一直正忙,MATLAB运行时一直处于忙的状态,是不是程序存在死循环