说明一下:实现这个案例需要准备的东西实在太多,不可能把所有的代码都贴上来(贴了,您也未必想看啊,哈)所以,css代码,配置文件,无关逻辑的就不往这上面贴了(想必大家既然选择做这个案例,这些基本的东西也都是会的,如果有什么疑问或者想要完整的源码的可以留言,嗯、互相帮助,互相进步)
需要掌握的知识点:webpack、npm、CommonJs、ES6、react、react-router
功能效果:(从上到下介绍)(其实很想直接放一张图片清晰明了,但是之前贴的图片好像都失效了,所以只好描述了)
在输入框中输入内容,按enter键添加到列表中,然后右侧有一个全选的按钮,当点击它时,可以实现全部选中下面的列表或全部不选中下面的列表(两者切换)
点击列表前面的复选框可以实现选中效果,点击右侧删除按钮实现删除操作、双击列表可以修改列表内容,按esc键,取消修改
底部:显示总共有多少条内容,选中的有多少条、未选中的有多少条、并且点击这些按钮可以控制列表显示的内容(比如总共有5条,选中3条(其实就是已完成3条)那么当我们点击已完成按钮时,列表中就只有这已完成的三条,未完成的不显示在页面中,其它同理)这些是使用react路由实现的效果,单击右边clear按钮可以删除所有已完成的列表
知道了大致的功能,然后就是按照功能一个个的实现了
index.js中的代码
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter,Route} from 'react-router-dom';
import Item from 'item.js';
import Footer from 'footer.js';
import Bottom from './component/props'
require('style/main.css') ;
require('style/base.css') ;
class App extends Component{
constructor(props){
super(props)
this.state = {
todosData:[],//存储数据
inputVal:'',
allCheck:false,
}
this.handleKeyDownPost=this.handleKeyDownPost.bind(this);
this.onClearComplete = this.onClearComplete.bind(this);
this.onDestrong=this.onDestrong.bind(this);
this.iptChange = this.iptChange.bind(this);
this.allSelect = this.allSelect.bind(this);
this.singleSelect = this.singleSelect.bind(this);
this.itemEditDone = this.itemEditDone.bind(this);
}
//让input变为受控组件
iptChange(ev){
// 如果是在this.setState中使用state中的属性,不需要从state获取一遍,但如果是在setState外使用的话,就需要提前获取后再使用
this.setState({
inputVal:ev.target.value
})
}
//在输入框输入内容,按enter键添加到列表中,同时也要把数据添加到数组中
handleKeyDownPost(ev){
if(ev.keyCode!=13) return ;
let{inputVal} = this.state;
// let value = ev.target.value.trim();
let value=inputVal.trim();
if(value==''){
return;
}
let todo = {};
todo.id=new Date().getTime();
todo.value = value;
todo.hasCompleted = false;
let{todosData} = this.state;
todosData.unshift(todo);
this.setState({
todosData,
inputVal:''
})
// ev.target.value='';
// inputVal = ''
}
//全选功能的实现
allSelect(){
let{todosData,allCheck} = this.state;
if(!todosData.length) return;
allCheck = !allCheck;
todosData = todosData.map((elem,i)=>{
elem.hasCompleted = allCheck;
return elem;
})
this.setState({
todosData,
allCheck
})
}
//列表的状态变化时,改变对应的hasCompleted
singleSelect(todo){
let{todosData,allCheck} = this.state;
todosData = todosData.map((elem,i)=>{
if(todo.id==elem.id){
elem.hasCompleted = !todo.hasCompleted
}
if(!elem.hasCompleted){
allCheck = false;
}
return elem;
})
this.setState({
todosData,
allCheck
})
}
//点右边的关闭按钮,删除列表中的内容(删除数组todosData中的内容)
onDestrong(todo){
let {todosData} = this.state;
todosData = todosData.filter((elem)=>{
return elem.id !== todo.id;
});
this.setState({todosData});
}
//点击清除已完成按钮,实现删除操作
onClearComplete(){
let {todosData} = this.state;
todosData = todosData.filter((elem)=>{
return !elem.hasCompleted;
})
this.setState({
todosData
})
}
itemEditDone(todo,val){
let{todosData}=this.state;
todosData = todosData.map((elem)=>{
if(elem.id==todo.id){
elem.value = val;
}
return elem;
})
}
render(){
let{onClearComplete,onDestrong,handleKeyDownPost,iptChange,allSelect,singleSelect,changeView,itemEditDone} = this;
let{todosData,inputVal,allCheck,view} = this.state;
let items = null;
let unCompelteNum = todosData.length;
let {location:{pathname},location} = this.props;
items=todosData.filter((elem)=>{//筛选出符合条件的数据
elem.hasCompleted?unCompelteNum--:unCompelteNum
switch(pathname){
case '/active':
return !elem.hasCompleted; break;
case '/completed':
return elem.hasCompleted; break;
default:
break;
}
return elem;
})
{!unCompelteNum?allCheck=true:null}//控制全选按钮,未完成的为0,就说明是全选(或者初始化的时候),未完成不为0就说明没有全选
{!todosData.length?allCheck=false:null}//如果数组的长度为0,就让全选按钮变灰
items = items.map((elem,i)=>{
return(
)
})
return(
todos
{items}
{todosData.length?():null}
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
items.js中的代码
import React,{Component} from 'react';
import PropTypes from 'prop-types';
let propTypes = {
todo:PropTypes.object,
onDestrong:PropTypes.func,
singleSelect:PropTypes.func,
itemEditDone:PropTypes.func
}
export default class Item extends Component{
constructor(props){
super(props)
this.state={
inEdit:false,
val:''
}
this.onEdit = this.onEdit.bind(this);
this.changeFval = this.changeFval.bind(this);
this.itemEditDone = this.itemEditDone.bind(this);
this.onEnter = this.onEnter.bind(this);
this.onBlur = this.onBlur.bind(this);
this.oldVal = this.props.todo.value;
}
onEdit(){//双击列表时,对列表中的内容进行编辑
let{todo}=this.props
let {value} = todo;
if(todo.hasCompleted) return;
this.setState({
inEdit:true,
val:value
},()=>{
this.refs.iptFoc.focus()
})
}
changeFval(ev){
this.setState({
val:ev.target.value
})
}
itemEditDone(){
this.setState({
inEdit:false
})
let{itemEditDone,todo}=this.props;
itemEditDone(todo,this.state.val)
this.oldVal = this.state.val
}
onEnter(ev){//按ESC取消修改
if(ev.keyCode==27){
this.setState({
inEdit:false,
val:this.oldVal
})
}else{
if(ev.keyCode!==13) return;
this.itemEditDone()
}
}
onBlur(){//失去焦点时修改完成
this.itemEditDone()
}
render(){
let{onEdit,changeFval,onEnter,onBlur}=this;
let{inEdit,val}=this.state;
let{onDestrong,todo,singleSelect} = this.props;
let itemClassName=todo.hasCompleted?'completed':''
inEdit?itemClassName+='editing':''
return(
singleSelect(todo)}
/>
)
}
}
Item.propTypes = propTypes;
footer.js中的代码
import React,{Component} from 'react';
import PropTypes from 'prop-types';
import {Link} from 'react-router-dom';
let propTypes = {
todosData:PropTypes.array,
unCompelteNum:PropTypes.number,
onClearComplete:PropTypes.func
}
export default class Footer extends Component{
render(){
let{unCompelteNum,onClearComplete,todosData,pathname}=this.props
return(
)
}
}
Footer.propTypes = propTypes;