说明一下:实现这个案例需要准备的东西实在太多,不可能把所有的代码都贴上来(贴了,您也未必想看啊,哈)所以,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( <Item {...{onDestrong,todo:elem,singleSelect,itemEditDone}}key={i} /> ) }) return( <div> <header className="header"> <h1>todos</h1> <input type="text" className="new-todo" value={inputVal} onChange={iptChange} onKeyDown={ handleKeyDownPost } /> </header> <section className="main"> <input type="checkbox" className="toggle-all" checked={allCheck} placeholder="type some of content" onChange={ allSelect } /> <ul className="todo-list"> {items} </ul> </section> {todosData.length?(<Footer {...{unCompelteNum ,onClearComplete,todosData,pathname}}/>):null} </div> ) } } ReactDOM.render( <BrowserRouter> <div> <Bottom name="donna"/> <Route path="/" component={App}/> </div> </BrowserRouter> , 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( <li className="item" className={itemClassName}> <div className="view"> <input type="checkbox" className="toggle" checked={todo.hasCompleted} onChange={ev=> singleSelect(todo)} /> <label htmlFor="" onDoubleClick={ onEdit } > {todo.value} </label> <button className="destroy" onClick={ev=> onDestrong(todo)}></button> </div> <input type="text" className="edit" value={val} onChange={changeFval} onKeyDown={onEnter} onBlur={onBlur} ref="iptFoc" /> </li> ) } } 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 className="footer"> <span className="todo-count"> <strong>{unCompelteNum}</strong> <span>item left</span> </span> <ul className="filters"> <li> <Link to="/" className={pathname=='/'?'selected':''}>{`All ${todosData.length}`}</Link> </li> <li> <Link to="/active" className={pathname=='/active'?'selected':''}>{`Active ${unCompelteNum}`}</Link> </li> <li > <Link to="/completed" className={pathname=='/completed'?'selected':''}>{`completed ${todosData.length-unCompelteNum}`}</Link> </li> </ul> <button className="clear-completed" onClick={ev=>{ onClearComplete() }} > clear all completed </button> </footer> ) } } Footer.propTypes = propTypes;