react的小實例todolist


一構建開發環境

      todolist的開發工具包為webpack+es6+react,首先需要構建開發環境,我在上一篇文章webpack構建ant-design中已經構建了ant-design的開發環境,這里也是用到這邊文章中所用的環境,在這個實例中需要用到箭頭函數,所以還需要安裝一下 stage-0,安裝語句如下:

npm install --save-dev babel-preset-stage-0

 修改.babelrc文件如下

{
  "presets": [
    "es2015",
    "react",
    "stage-0"
  ],
  "sourceMaps": true
}

 二分析todolist結構

     

完成版的todolist如上圖所示,按照react組件化編程的風格,我大致將todolist分為三個組件,分別是TodoHeader,用來輸入任務名稱,TodoMain,用來展示任務,TodoFooter,用來全選和清楚選中的任務,我創建的文件,及問價目錄如下

三 輸入,存儲任務

  index.js作為webpack打包的入口文件,App作為主要的組件來引用上面三個組件。

 index.js

require("./style/index.less");
import ReactDom from 'react-dom';
import React from 'react';
import App from '../src/component/App';
ReactDom.render(<App/>, document.getElementById('root'));

 App.jsimport React from 'react';import TodoHeader from './TodoHeader';

import TodoMain from './TodoMain';
import TodoFooter from './TodoFooter';
class App extends React.Component{
    constructor(props){
        super(props);
        this.db = localStorage; //定義一個localStorage容易
        let todos = this.db.getItem('todos') || []; //通過鍵todos,來獲取localStorage容器中的值
        this.state = {
            todos: eval('(' + todos + ')'),
            isAllChecked: false //用來表示是否全選
        };
    }
    componentDidMount(){
       // let todos = eval('(' + this.state.todos + ')');
       // if(todos.length > 0){
       //     this.setState({todos: todos});
       // }
    }
    allChecked() {
//全選函數,供子組件調用 let isAllChecked = false;
// every() 方法用於檢測數組所有元素是否都符合指定條件(通過函數提供)。
if(this.state.todos.every((todo) => todo.isDone)){ isAllChecked = true; } this.setState({ todos: this.state.todos, isAllChecked: isAllChecked }) } clearDone = () =>{
//清除選中的項目 let todos = this.state.todos.filter(todo => !todo.isDone); //過濾沒有選中的項目 this.setState({ todos: todos, isAllChecked: false, }); this.db.setItem('todos', JSON.stringify(todos)); //修改localStorage中鍵todos的值,以json數組的形式存儲,方便后續取出操作 } addTodo =(todoItem) =>{
//添加項目操作 this.state.todos.push(todoItem);//todo列表 ,向todos數組中插入一條記錄 this.db.setItem('todos', JSON.stringify(this.state.todos)); //將所有的記錄再次保存如localStorage中 this.allChecked(); } deleteTodo = (index) =>{
//刪除一個項目 this.state.todos.splice(index, 1);//通過傳過來的索引值,刪除數組中的一條記錄 this.setState({todos: this.state.todos}); // this.db.setItem('todos', JSON.stringify(this.state.todos));//將更新的todos數組,插入到localStorage中 } changeTodoState = (index, isDone, isChangeAll = false) => { // 改變任務狀態,傳遞給TodoItem和Footer組件的方法 if(isChangeAll){ this.setState({ todos: this.state.todos.map( (todo) =>{ todo.isDone = isDone; return todo; }),isAllChecked: isDone }) }else{ this.state.todos[index].isDone = isDone; this.allChecked(); } this.db.setItem('todos', JSON.stringify(this.state.todos)); } render(){ let info = { isAllChecked: this.state.isAllChecked, todoCount: this.state.todos.length || 0, todoDoneCount: (this.state.todos && this.state.todos.filter((todo) => todo.isDone)).length || 0 }; return( <div className="todo-wrap"> <TodoHeader addTodo={this.addTodo} name="add" /> <TodoMain todos={this.state.todos} deleteTodo={this.deleteTodo} changeTodoState={this.changeTodoState} {...info}/> <TodoFooter {...info} changeTodoState={this.changeTodoState} clearDone={this.clearDone} /> </div> ); } } export default App;

  在HTML5中,新加入了一個localStorage特性,這個特性主要是用來作為本地存儲來使用的,解決了cookie存儲空間不足的問題(cookie中每條cookie的存儲空間為4k),localStorage中一般瀏覽器支持的是5M大小,這個在不同的瀏覽器中localStorage會有所不同。

三組件TodoHeader

   在App.js中引用了組件TodoHeader,並給這個子組件傳入了方法addTodo ,組件TodoHeader的代碼如下,用戶在輸入框中輸入,點擊回車按鈕,保存用戶的輸入到localStorage中去。

import React from 'react';
class TodoHeader extends React.Component{
    constructor(props){
        super(props);
        this.state = {

        };
        this.handlerKeyUp = this.handlerKeyUp.bind(this); //綁定鍵盤事件,也可以使用箭頭函數
    }
    componentDidMount(){

    }
    handlerKeyUp(e){
        //鼠標回車事件
        if(e.keyCode == 13){
            //鍵盤敲擊回車時間
            let value = e.target.value; //獲取輸入框中輸入的值
            if(!value) return false; //如果輸入框沒有輸入,則返回false
            let newTodoItem = {
                text: value,
                isDone: false
            };//將輸入的值保存在一個對象中
            e.target.value = '';//將輸入框的值置空
            this.props.addTodo(newTodoItem) //調用父組件的方法,通過this.props調用父控件的方法,將輸入的值傳遞給父控件,在父控件中對state的值進行修改,從而重新渲染頁面。
        }
    }
    render() {
        return(
            <div className="todo-header">
                <h1 className="text-center">React Todo</h1>
                <div className="form-horizontal">
                    <div className="form-group">
                    <label className="col-md-2 control-label">Task</label>
                    <div className="col-md-10">
                      <input onKeyUp={this.handlerKeyUp} type="text" placeholder="請輸入你的任務名稱,按回車鍵確認" className="form-control"/> 
                    </div>
                    </div>
                </div>
            </div>
        )
    }
};
export default TodoHeader;

  四 展示組件TodoMain

 定義一個組件TodoItem,來處理具體某一條的記錄,然后再TodoMain中來處理出所有的數據

TodoItem.js代碼如下

   

import React from 'react';
export default class TodoItem extends React.Component{
    constructor(props){
        super(props);
        this.state = {
           isShow: false,
        };
    }
    componentDidMount(){

    }
    handlerChange =()=>{
//多選框處理事件,選中則表示已處理 let isDone = !this.props.isDone; this.props.changeTodoState(this.props.index, isDone); } handlerDelete =()=>{
//刪除事件 this.props.deleteTodo(this.props.index); } handlerMouseOver =()=>{
//鼠標移入事件 this.setState({isShow:true}); } handlerMouseOut =() =>{
//鼠標移出事件 this.setState({isShow:false}); } render(){ let className = this.props.isDone? 'task-done': ''; return( <li className={this.props.isDone?"list-group-item-success item":"item"} onMouseOver={this.handlerMouseOver} onMouseOut={this.handlerMouseOut}> <label> <input type="checkbox" checked={this.props.isDone} onChange={this.handlerChange} className="pull-left"/> <span className={className}>{this.props.text}</span> </label> <div className="pull-right"> <button type="button" className={ this.state.isShow?"btn btn-xs":"btn btn-xs close"} onClick={this.handlerDelete}>刪除</button> </div> </li> ) } }

  TodoMain.js代碼如下

import React from 'react';
import TodoItem from './TodoItem';
export default  class TodoMain extends React.Component{
    constructor(props){
        super(props);
        this.state = {

        };
    }
    componentDidMount(){

    }
    render() {
         if(this.props.todos.length == 0){
             return(
                 <div className="todo-empty">恭喜您,目前沒有待辦事項!</div>
             )
         }else{
             return (
                 <ul className="todo-main">
                     {
                         this.props.todos.map((todo, index) => {
                             return <TodoItem text={todo.text} isDone={todo.isDone} index = {index} {...this.props} key={index} />
                         })
                     }
                     <li className="item">
                         <label>
                           <span><strong>{this.props.todoCount}</strong>總數/<strong>{this.props.todoDoneCount}</strong>已完成</span>
                         </label>
                     </li>
                 </ul>
             )
         }
    }
}

  {..this.props}將App中的方法,通過props傳遞給組件TodoIten中  

五 TodoFooter

        TodoFooter代碼如下

              

import React from 'react';
export default class TodoFooter extends React.Component{
    constructor(props){
        super(props);
        this.state = {

        };
    }
    componentDidMount(){

    }
    handlerSelectAll =(e) =>{
//全選事件,調用父組件的全選事件 this.props.changeTodoState(null, e.target.checked, true); } handlerDeleteDone = () => {
//清除已選任務操作,調用父組件的事件 this.props.clearDone(); } render(){ let count = this.props.todoCount; if(count > 0){ return( <div className="todo-footer"> <label> <input type="checkbox" checked={this.props.isAllChecked} onChange={this.handlerSelectAll} />全選 </label> <div className="clearTask"> <button className="btn" onClick={this.handlerDeleteDone}>清除已完成任務</button> </div> </div> ) }else{ return ( <div className="todo-footer"></div> ) } } }

  六項目中用到的樣式

   樣式文件放在style/index.less文件中

       

body {
  font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
  font-size: 14px;
  line-height: 1.42857143;
  color: #333;
  background-color: #fff;
}
#root{
  width: 1170px;
  padding-right: 15px;
  padding-left: 15px;
  margin-right: auto;
  margin-left: auto;
}
.todo-header{
  .form-horizontal .form-group {
    margin-right: -15px;
    margin-left: -15px;
  }
  .form-group {
    margin-bottom: 15px;
  }
  .form-horizontal .control-label {
    padding-top: 7px;
    margin-bottom: 0;
    text-align: right;
  }
  .col-md-2 {
    float: left;
    width: 16.66666667%;
  }
  .col-md-10{
    width: 83.33333333%;
  }
  .form-control {
    display: block;
    width: 100%;
    height: 34px;
    padding: 6px 12px;
    font-size: 14px;
    line-height: 1.42857143;
    color: #555;
    background-color: #fff;
    background-image: none;
    border: 1px solid #ccc;
    border-radius: 4px;
    -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
    box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
    -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;
    -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
    transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
  }
}
.todo-main{
  padding-left: 0;
  margin-bottom: 20px;
  .item{
    position: relative;
    display: block;
    padding: 10px 15px;
    margin-bottom: -1px;
    background-color: #fff;
    border: 1px solid #ddd;
    .pull-left{
      float: left!important;
    }
  }
  .list-group-item-success{
    color: #3c763d;
    background-color: #dff0d8;
  }
}
.todo-main:first-child{
  border-top-left-radius: 4px;
  border-top-right-radius: 4px;
}
.todo-footer{
  margin-bottom: 40px;
  .clearTask{
    display: inline-block;
    margin-left: 20px;
    .btn{
      color: #fff;
      background-color: #337ab7;
      border-color: #2e6da4;
    }
    .btn:hover {
      color: #fff;
      background-color: #286090;
      border-color: #204d74;
    }
  }
}
.todo-empty{
}
input[type=checkbox], input[type=radio] {
  margin: 4px 0 0;
  margin-top: 1px\9;
  line-height: normal;
}
input[type="checkbox"] {
  margin-right: 10px;
}
button.close {
  display: none;
  font-size: 12px;
  height: 18px;
  width: 18px;
}
button.close {
  -webkit-appearance: none;
  padding: 0;
  cursor: pointer;
  background: 0 0;
  border: 0;
}
.close {
  float: right;
  font-size: 21px;
  font-weight: 700;
  line-height: 1;
  color: #000;
  text-shadow: 0 1px 0 #fff;
  filter: alpha(opacity=20);
  opacity: .2;
}
.btn-group-xs>.btn, .btn-xs {
  padding: 1px 5px;
  font-size: 12px;
  line-height: 1.5;
  border-radius: 3px;
}
.btn {
  display: inline-block;
  padding: 6px 12px;
  margin-bottom: 0;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.42857143;
  text-align: center;
  white-space: nowrap;
  vertical-align: middle;
  -ms-touch-action: manipulation;
  touch-action: manipulation;
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  background-image: none;
  border: 1px solid transparent;
  border-radius: 4px;
}
.todo-wrap{
  min-height: 20px;
  padding: 19px;
  margin-bottom: 20px;
  background-color: #f5f5f5;
  border: 1px solid #e3e3e3;
  border-radius: 4px;
  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.05);
  box-shadow: inset 0 1px 1px rgba(0,0,0,.05);
  margin-left: 25%;
}
.todo-empty{
  text-align: center;
}
.text-center {
  text-align: center;
}
.h1, h1 {
  font-size: 36px;
}

  

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM