react实现TodoList案例

说明一下:实现这个案例需要准备的东西实在太多,不可能把所有的代码都贴上来(贴了,您也未必想看啊,哈)所以,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;

猜你喜欢

转载自blog.csdn.net/lhjuejiang/article/details/80376281