国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

詳解React-Todos入門例子

2019-11-19 19:00:52
字體:
來源:轉載
供稿:網友

最近學完React的最基本概念,閑下來的時候就自己寫了一個Todo-List的小應用。這里做個簡略的說明,給想好好學React的新手看。

開始之前

這里我用了webpackb做了babel和JSX預處理和模塊打包。所以對React和一些ES2015(ES6)的語法要有一定的了解。我相信學習ES2015絕對是劃算的,因為它是Js的規范。這里給出學習的地方,阮一峰老師的ECMAScript 6 入門或者babel的相關文檔Learn ES2015。

最后的實際效果:

我們需要做到的功能有:

  1. 可以在最上面的input里,使用回車來添加任務。
  2. 在中間的任務列表里,由checkbox來控制任務的狀態。
  3. 已完成的任務有一個line-through的樣式。
  4. 當鼠標移到每一個任務時,都會出現刪除按鈕提供刪除。
  5. 在底部有一個全選按鈕,用于控制所有的任務狀態。
  6. 還有已完成與總數的顯示。
  7. 可以清空已完成的任務。

上面就是一個Todo-List最基本的功能,而我們這次就是用React實現上述功能。例子在我的github上可以download下來,可以用作參考:React-Todos

加載npm模塊

終于要開始我們的React-Todo的項目了,首先我們就要新建項目,通過npm我們可以很輕松的創建項目,并加載我們所需要的各個組件。大家可以在自己的項目里,用我的package.json去加載所需要的模塊。通過命令行進行安裝。

$ npm install

這里提一下,因為我們這里僅僅是前端靜態的,并不涉及到數據庫。所以我自己寫了一個非常簡單的用于操作localStorage的小模塊localDb。所以涉及到數據存儲的時候,都是用localStorage來代替數據庫。它的原理就是,通過將數據格式化成JSON字符串進行存儲,使用的時候就解析JSON字符串。這個模塊在我的github的例子里有,需要從那里復制一份來,放在node_modules的文件夾內。

配置webpack

經過一輪漫長的等待,我們終于安裝好所需要的各個模塊了。我們在開始我們的react的編碼前,需要對webpack進行配置。關于webpack的學習,我這里就不贅述了,在前一篇剛講完。下面直接看一看webpack.config.js。

// webpack.config.jsvar path = require('path');module.exports = {  entry: "./src/entry.js",  output: {    path: path.join(__dirname, 'out'),    publicPath: './out/',    filename: "bundle.js"  },  externals: {    'react': 'React'  },  module: {    loaders: [      { test: //.js$/, loader: "jsx!babel", include: /src/},      { test: //.css$/, loader: "style!css"},      { test: //.scss$/, loader: "style!css!sass"},      { test: //.(jpg|png)$/, loader: "url?limit=8192"}    ]  }};

這里一切從簡,可以看到入口文件是在src文件夾里的entry.js,然后輸出文件放在out文件夾的bundle.js里。

配置一下模塊的loaders,先用babel-loader再用jsx-loader。這樣子我們就可以讓ES6配合JSX編寫我們的React組件了。其它的加載器也沒什么好說的了,如果不清楚可以翻我上一篇關于webpack的文章。

這里提一下externals屬性,這個屬性是告訴webpack當遇到require('react')的時候,不去處理并且默認為全局的React變量。這樣子,我們就需要在index.html單獨用src去加載js。

分析各個組件

App組件

我這里并不會教大家手把手將這個React-Todo做出來,但是可以結合例子進行分析理解。先來看看總的組件,也就是App。

import React from "react";import LocalDb from "localDb";import TodoHeader from "./TodoHeader.js";import TodoMain from "./TodoMain.js";import TodoFooter from "./TodoFooter.js";class App extends React.Component {  constructor(){    super();    this.db = new LocalDb('React-Todos');    this.state = {      todos: this.db.get("todos") || [],      isAllChecked: false    };  }  // 判斷是否所有任務的狀態都完成,同步底部的全選框  allChecked(){    let isAllChecked = false;    if(this.state.todos.every((todo)=> todo.isDone)){      isAllChecked = true;    }    this.setState({todos: this.state.todos, isAllChecked});  }  // 添加任務,是傳遞給Header組件的方法  addTodo(todoItem){    this.state.todos.push(todoItem);    this.allChecked();    this.db.set('todos',this.state.todos);  }  // 改變任務狀態,傳遞給TodoItem和Footer組件的方法  changeTodoState(index, isDone, isChangeAll=false){    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.set('todos', this.state.todos);  }  // 清除已完成的任務,傳遞給Footer組件的方法  clearDone(){    let todos = this.state.todos.filter(todo => !todo.isDone);    this.setState({      todos: todos,      isAllChecked: false    });    this.db.set('todos', todos);  }  // 刪除當前的任務,傳遞給TodoItem的方法  deleteTodo(index){    this.state.todos.splice(index, 1);    this.setState({todos: this.state.todos});    this.db.set('todos', this.state.todos);  }  render(){    var props = {      todoCount: this.state.todos.length || 0,      todoDoneCount: (this.state.todos && this.state.todos.filter((todo)=>todo.isDone)).length || 0    };    return (      <div className="panel">        <TodoHeader addTodo={this.addTodo.bind(this)}/>        <TodoMain deleteTodo={this.deleteTodo.bind(this)} todos={this.state.todos} changeTodoState={this.changeTodoState.bind(this)}/>        <TodoFooter isAllChecked={this.state.isAllChecked} clearDone={this.clearDone.bind(this)} {...props} changeTodoState={this.changeTodoState.bind(this)}/>      </div>    )  }}React.render(<App/>, document.getElementById("app"));

用ES6寫React最大的不同就是,組件可以通過繼承React.Components來得到,并且初始化state也不需要冗長的getInitalialState,直接在構造函數里操作this.state即可。更優秀的便是...spread擴展操作符,可以讓我們省下一堆不必要的代碼,這個接下來再說。

App狀態state

我們知道React的主流思想就是,所有的state狀態和方法都是由父組件控制,然后通過props傳遞給子組件,形成一個單方向的數據鏈路,保持各組件的狀態一致。所以我們在這個父組件App上,看的東西稍微有點多。一點點來看:

constructor(){  super();  this.db = new LocalDb('React-Todos');  this.state = {    todos: this.db.get("todos") || [],    isAllChecked: false  };}

在App組件的constructor內,我們先是初始化了我們的localStorage的數據庫,放在了this.db上。然后便是初始化了state,分別有兩個,一個是todos的列表,一個是所有的todos是否全選的狀態。

App方法

// 判斷是否所有任務的狀態都完成,同步底部的全選框allChecked()// 添加一個任務,參數是一個todoItem的objectaddTodo(todoItem)// 改變任務的狀態,index是第幾個,isDone是狀態,isChangeAll是控制全部狀態的changeTodoState(index, isDone, isChangeAll=false) // 參數默認位false// 清空已完成clearDone()// 刪除面板上第幾個任務deleteTodo(index)// react用于渲染的函數render(){  <div className="panel">    <TodoHeader />    <TodoMain />    <TodoFooter />  </div>}

我們可以從render函數看到整個組件的結構,可以看到其實結構非常簡單,就是上中下。上面的TodoHeader自然就是用來輸入任務的地方,中間就是展示并操作todo-list的,而底部就是顯示數據并提供特殊操作。這里還是要提醒一句,所有標簽都必須閉合,即使是非結對的,也要用斜杠閉合上。

  render(){    var props = {      todoCount: this.state.todos.length || 0,      todoDoneCount: (this.state.todos && this.state.todos.filter((todo)=>todo.isDone)).length || 0    };    return (      <div className="panel">        <TodoHeader addTodo={this.addTodo.bind(this)}/>        <TodoMain deleteTodo={this.deleteTodo.bind(this)} todos={this.state.todos} changeTodoState={this.changeTodoState.bind(this)}/>        <TodoFooter isAllChecked={this.state.isAllChecked} clearDone={this.clearDone.bind(this)} {...props} changeTodoState={this.changeTodoState.bind(this)}/>      </div>    )  }

我們可以看到,其他的方法都是傳到子組件上,就不一一詳細說如何實現的了。總體的思想就是,方法在父組件定義,通過props傳給需要的子組件進行調用傳參,最后返回到父組件上執行函數,存儲數據、改變state和重新render。方法需要bind(this),不然方法內部的this指向會不正確。

計算需要的數據后,通過props傳遞到子組件。如果細心的同學應該可以看到像這樣的{...props},這就是我之前說過的spread操作符。如果我們沒有用這個操作符,就要這樣寫:

<TodoFooter {...props} /> // spread操作符<TodoFooter todoCount={props.todoCount} todoDoneCount={props.todoDoneCount} />

最佳的實踐就是,當父組件傳props給子組件,然后子組件要將props轉發給孫子組件的時候,spread操作符簡直讓人愉悅!可以對一堆麻煩又丑又長的代碼可以say goodbye了!

最后我們將整個App渲染到DOM上即可。

React.render(<App/>, document.getElementById("app"));

AppHeader組件

import React from "react";class TodoHeader extends React.Component {  // 綁定鍵盤回車事件,添加新任務  handlerKeyUp(event){    if(event.keyCode === 13){      let value = event.target.value;      if(!value) return false;      let newTodoItem = {        text: value,        isDone: false      };      event.target.value = "";      this.props.addTodo(newTodoItem);    }  }  render(){    return (      <div className="panel-header">        <input onKeyUp={this.handlerKeyUp.bind(this)} type="text" placeholder="what's your task ?"/>      </div>    )  }}export default TodoHeader;

到了子組件,方法就沒那么多了,一般子組件就是綁定事件。可以看到在子組件綁定了keyUp事件,用來確定回車鍵并調用父組件傳來的addTodo(),將新生成的todo任務作為參數傳入。

AppFooter組件

import React from "react";export default class TodoFooter extends React.Component{  // 處理全選與全不選的狀態  handlerAllState(event){    this.props.changeTodoState(null, event.target.checked, true);  }  // 綁定點擊事件,清除已完成  handlerClick(){    this.props.clearDone();  }  render(){    return (      <div className="clearfix todo-footer">        <input checked={this.props.isAllChecked} onChange={this.handlerAllState.bind(this)} type="checkbox" className="fl"/>        <span className="fl">{this.props.todoDoneCount}已完成 / {this.props.todoCount}總數</span>        <button onClick={this.handlerClick.bind(this)} className="fr">清除已完成</button>      </div>    )  }}

我們先來看看這個footer上有哪些方法。第一個就是處理todo狀態的,它通過底部的checkbox的change事件觸發。然后就是清空已完成的按鈕的點擊事件的方法handlerClick()。然后下面的數據顯示,就通過props的值進行顯示。

TodoMain

import React from "react";import TodoItem from "./TodoItem.js"export default class TodoMain extends React.Component{  // 遍歷顯示任務,轉發props  render(){    return (      <ul className="todo-list">        {this.props.todos.map((todo, index) => {          return <TodoItem key={index} {...todo} index={index} {...this.props}/>        })}      </ul>    )  }}

Main組件的作用就是,將props傳過來的todos遍歷顯示出來。所以對每一個todo的細致操作都是放在TodoItem上。

TodoItem

import React from "react";export default class TodoItem extends React.Component{  // 處理任務是否完成狀態  handlerChange(){    let isDone = !this.props.isDone;    this.props.changeTodoState(this.props.index, isDone);  }  // 鼠標移入  handlerMouseOver(){    React.findDOMNode(this.refs.deleteBtn).style.display = "inline";  }  // 鼠標移出  handlerMouseOut(){    React.findDOMNode(this.refs.deleteBtn).style.display = "none";  }  // 刪除當前任務  handlerDelete(){    this.props.deleteTodo(this.props.index);  }  render(){    let doneStyle = this.props.isDone ? {textDecoration: 'line-through'} : {textDecoration: 'none'};    return (      <li        onMouseOver={this.handlerMouseOver.bind(this)}        onMouseOut={this.handlerMouseOut.bind(this)}      >        <input type="checkbox" checked={this.props.isDone} onChange={this.handlerChange.bind(this)}/>        <span style={doneStyle}>{this.props.text}</span>        <button style={{'display': 'none'}} ref="deleteBtn" onClick={this.handlerDelete.bind(this)} className="fr">刪除</button>      </li>    )  }}

在TodoItem主要處理多個交互,包括修改任務狀態,刪除任務。還有就是鼠標移到相應的任務上才顯示刪除按鈕。

我們可以看到render()函數,是控制了任務的樣式。標簽內的style是需要接受一個對象的,所以所有的CSS屬性名,都要變成駝峰形的。

總結

其實真正的回過頭看React-Todos,會覺得React帶給我們的組件化的思想用起來太舒服了。我們通過父組件來控制狀態,并通過props傳遞,來保證組件內的狀態一致。我們可以非常有效的維護我們的交互代碼,因為我們一眼就知道,這個事件屬于哪個組件管理。它的模型其實非常輕,只有View層,但是它帶給我們全新的書寫前端組件的方法是非常好的,我個人認為如果未來的站點交互性愈來愈多,React是很有可能代替jQuery成為必備的技能。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 轮台县| 南昌市| 武义县| 秀山| 汶上县| 毕节市| 宿州市| 海原县| 汝州市| 新邵县| 平利县| 曲周县| 昌江| 永年县| 靖州| 离岛区| 茶陵县| 宣恩县| 兴国县| 隆尧县| 金沙县| 凉城县| 高要市| 扎鲁特旗| 阳原县| 永兴县| 建水县| 襄汾县| 都安| 元氏县| 宝山区| 南康市| 米脂县| 万源市| 石门县| 银川市| 集贤县| 和林格尔县| 清徐县| 江陵县| 扶绥县|