react新手demo——TodoList

 
react-todolist.gif

一: 寫在文章開頭

今天我們就使用 react 來實現一個簡易版的 todolist ,我們可以使用這個 demo 進行 list 的增刪改差,實際效果如上圖所示。大家可以clone下來查看:react-todolist

這篇文章我們就不使用 redux,因爲這個 demo 本身比較簡單,不需要通過 redux 來管理我們的狀態。

redux中也有非常有名的一句話叫做:

"如果你不知道是否需要 Redux,那就是不需要它。"

我們廢話不多說,直接進入正題。


二:項目的目錄結構

   .
   ├── app                              // 開發的文件夾,組件放在這個文件夾中
   │   ├── components                   // 項目的組件
   │   │   ├── App.js                   // 最外層包含下面組件的總組件
   │   │   ├── AppFooter.js             // App的三個篩選按鈕的組件
   │   │   ├── AppForm.js               // 添加list的form
   │   │   ├── AppList.js               // 顯示list數據的智能組件
   │   │   └── AppTodos.js              // 顯示list的木偶組件
   ├── css                              // 放css文件的地方。
   │   ├── semantic.css                 // 我們的文件用到了semantic.css,
   ├── node_modules                     // 第三方的依賴
   ├── .babelrc                         // babel配置文件
   ├── .gitignore                       // git上傳時忽略的文件
   ├── bundle.js                        // webpack build之後的文件
   ├── index.html                       // 項目的模版文件
   ├── main.js                          // 項目的入口文件
   ├── webpack.config.js                // webpack配置文件
   ├── README.md                        // readme文件
   └── package.json                     // 當前整一個項目的依賴

三:前期準備,安裝依賴

1,首先我們新建一個todolist文件夾,根據我的目錄結構建好相應的文件,如果大家嫌麻煩,大家可以clone我的項目,然後看着我的代碼,我會一一進行說明的。package.json我們可以自己創建。

$ mkdir todolist
$ cd todolist

2,建立package.json文件

npm init

3,安裝相應的依賴,我先解釋一下這些依賴的作用

  • 首先安裝Babel,Babel 是一個 JavaScript 編譯器,他可以將es6或者es7的語法轉化爲瀏覽器能識別的javascript。

    npm install babel-cli babel-core --save-dev
    
  • 其次安裝我們的主角,react

    npm install react react-dom --save-dev
    
  • 安裝webpack,打包工具;和webpack-dev-server,用於來給我們開啓一個服務的。

    npm install webpack webpack-dev-server --save-dev
    
  • 安裝loader打包,通過使用不同的loaderwebpack有能力調用外部的腳本或工具,實現對不同格式的文件的處理,比如說分析轉換scss爲css,或者把下一代的JS文件(ES6,ES7)轉換爲現代瀏覽器兼容的JS文件,對React的開發而言,合適的Loaders可以把React的中用到的JSX文件轉換爲JS文件。

    大家想了解更多的webpack的內容,可以參考webpack中文文檔

    npm install css-loader babel-loader style-loader --save-dev
    

    然後我們在webpack.config.js中引用這些依賴,具體的格參數的意思,大家可以參考webpack各文檔說明,我下面也會簡單的註釋一下。

    module.exports = {
      entry: './main.js',                 // webpack打包的入口文件
      output: {
        filename: './bundle.js'           // 輸出之後的文件名
      },
      module: {
        loaders: [
          {
            test: /\.jsx?$/,
            exclude: /node_modules/,
            loader: 'babel-loader'         // babel的loader,jsx文件使用babel-loader處理
          }, {
            test: /\.css$/,
            exclude: /node_modules/,
            loader: 'style!css'              // css和styleloader,對css後綴的文件進行處理
          }
        ]
      },
      devtool: 'cheap-source-map',
    }
    
  • 同時要讓我們的 babel 能在 react中生效,同時支持es6,我們需要安裝下面的插件

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

    安裝完依賴後,我們在.babelrc文件中引入這幾個依賴

    {
      "presets": ["es2015","react",'stage-0']
    }
    
  • 其次爲了當我們每次添加list的時候有一個唯一的id,我們使用uuid

    npm install uuid --save-dev
    


四:組件的編寫,是我們的頁面能夠顯示出來

  • 編寫模版文件index.html

    在這個模版文件裏面,我們引入 semantic.css 文件,然後建立一個 id=app<div> 爲了我們後續的 react 操作。

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>react-todolist</title>
      <link rel="stylesheet" type="text/css" href="/css/semantic.css">
      <style>
          .active { color: red }
          .line{
              display: inline-block;
              border-bottom: 1px solid #222222;
              width:100px;
              position: absolute;
              left:0;
              top:7px;
          }
          .ui.comments .comment{
              padding:0;
              margin:2em 0 0;
          }
      </style>
    </head>
    <body>
      <div class="ui container" style="padding:30px;">
          <div id="app"></div>
      </div>
    
      <script src="./bundle.js"></script>
    </body>
    </html>
    
  • 編寫入口文件main.js

    這邊的data是我們的模擬數據,將其傳入到<App/>組件,在子組件中可以通過props.data的方法獲取 data。對於react的基礎知識,大家可以參考來自一位react新手的react入門須知

    import React from 'react'
    import ReactDOM from 'react-dom'
    
    import App from './app/component/App'
    
    let data = [
     {id: 0, text: '天氣不錯哦!!!', complete: false},
     {id: 1, text: '天氣不錯哦!!!', complete: false},
     {id: 2, text: '出去玩啊!!!', complete: true},
    ]
    
    ReactDOM.render(
      <App data={data}/>,
      document.getElementById('app')
    )
    
  • 編寫component裏面的組件

    1. App.js

      這個組件我們可以認爲是一個容器組件,我們會把AppFormAppListAppFooter放在這個組件中。

      import React from 'react'
      import AppList from './AppList.js'
      import AppForm from './AppForm.js'
      import AppFooter from './AppFooter.js'
      
      class App extends React.Component {
        state = {
            choosevalue : 1,
            data: this.props.data
        }
        
        render () {
          const { data } = this.state; 
          return (
            <div className='ui comments'>
              <h1>My Todo with React</h1>
              <div className='ui divider'></div>
              <AppForm />
              <AppList data={data}/>
              <AppFooter />
            </div>
          )
        }
      }
      
      export default App;
      
    2. AppForm.js

      這個組件是我們添加 list 用的一個form 組件,其中下面的styles這個對象那個也是jsx中申明樣式的一種方式,我們還可以使用className來添加樣式名字。

      import React from 'react';
      import uuid from 'uuid';
      
      var styles = {
        'title': {
          width: 200,
          display: 'inline-block',
          marginRight: 10,
          verticalAlign: 'top'
        }
      }
      
      class AppForm extends React.Component {
        render () {
          return (
            <form className='ui reply form'>
              <div className='field' style={styles.title}>
                <input type='text' placeholder='TODO' ref='text' />
              </div>
      
              <button type='submit' className='ui blue button'>
                  添加
              </button>
            </form>
          )
        }
      }
      
      export default AppForm;
      
    3. AppList.js

      這個組件是我們在react中常說的智能組件,得到數據lists後通過 map 方法遍歷數據,然後進行渲染。這裏的map方法是用到了es6中的解構賦值,大家可以參考react新手必須懂得es6的基礎知識,然後將值一一傳遞到子組件中去。

      import React from 'react'
      import AppTodos from './AppTodos'
      
      class AppList extends React.Component {
        render () { 
          const a = this.props.data.map(({ id, text, complete }, index) => {
             return  
                 <AppTodos 
                     key={index} 
                     id={id} 
                     text={text} 
                     complete={complete} 
                   />
          })
      
          return (
            <div> { a } </div>
          )
        }
      }
      
      export default AppList;
      
    4. AppTodos.js

      這個組件是我們在react中常說的木偶組件,就是得到數據渲染組件。

      import React from 'react'
      
      var styles = {
        'title': {
          paddingLeft: '20px',
          paddingRight: '50px',
          position: 'relative'
        },
        'delete': {
          marginLeft: '20px',
          marginRight: '50px'
        }
      }
      
      class AppTodos extends React.Component {
        render () {
          return (
            <div className='comment'>
              <div className='content'>
                <span 
                     className='author' 
                      style={styles.title} 
                >
                    {this.props.text}
                    <span 
                         className={this.props.complete ? 'line' : ''} 
                    />
                </span>
                <span className='author' 
                      style={styles.title}>
                      {this.props.complete ? '已完成' : '未完成'}
                </span>
                <span className='author'>{this.props.id}</span>
                <span className='ui blue button' 
                      style={styles.delete} >
                      刪除
                </span>  
              </div>
            </div>
          )
        }
      }
      
      export default AppTodos;
      
    5. AppFooter.js

      這個組件就是下面的三個按鈕全部未完成已完成

      import React from 'react'
      
      var styles = {
        'title': {
          marginRight: 10,
          fontSize: 20
        },
      
        'top': {
          marginTop: 20
        }
      }
      
      class AppFooter extends React.Component {
      
        render () {
          return (
            <div>
              <h2 style={styles.top}>show</h2>
              <button 
                 type='submit' 
                 style={styles.top} 
                 className='ui blue button' 
                 value='1' 
                 ref='all'
               > 
                 全部 
              </button>
              <button 
                 type='submit' 
                 style={styles.top} 
                 className='ui blue button' 
                 value='2' 
                 ref='active'
              > 
                 還未完成 
              </button>
              <button 
                 type='submit' 
                 style={styles.top} 
                 className='ui blue button' 
                 value='3' 
                 ref='complete'
              > 
                 已完成 
              </button>
            </div>
          )
        }
      }
      
      export default AppFooter;
      

    然後我們在命令行輸入,會開啓一個服務。

    npm run server
    

    打開瀏覽器,輸入http://localhost:8080,可看到:

     
    react-demo_1.png

5,實現list的添加操作

  • 首先理一下流程

    首先在form輸入待辦事情,點擊添加觸發一個handleSubmit點擊函數,但是我們的data是通過<App />組件來分發的,而list是組件AppList渲染的。這裏涉及到了從子組件傳遞值給父組件,其實也很簡單,就從父組件中傳一個函數給子組件,子組件將值通過函數再傳遞出去,大家可以參考react父子組件間的交流

  • 在組件App.js中,我們加入一個OnAddTodoItem函數,並傳入到AppForm組件中,我們新建函數中將傳進來的newItem通過concat()現在的data,然後更新state

      ...
    
      OnAddTodoItem (newItem) {
        let newdata = this.state.data.concat(newItem);
        this.setState({data : newdata});
      }
    
      render () {
        const { data } = this.state; 
        return (
          <div className='ui comments'>
            <h1>My Todo with React</h1>
            <div className='ui divider'></div>
            <AppForm 
              AddTodoItem={this.OnAddTodoItem.bind(this)} />
            <AppList 
              data={data}/>
            <AppFooter />
          </div>
        )
      }
    }
    
    export default App;
    
  • 在組件AppForm.js中,我們加入一個handleSubmit函數,並在form表單添加一個onClick函數,將用戶輸入的數據,通過uuid生成的id、輸入的text、以及是否完成false。通過函數傳遞給父組件。

    ...
    
    handleSubmit (event) {
        event.preventDefault()
        let text = this.refs.text.value
        
        if (!text.trim()) {
          alert("Input can't be null")
          return
        }
        
        let id = uuid();
        this.props.AddTodoItem({id,text,complete:false});
      }
    
      render () {
        return (
          <form 
                  className='ui reply form' 
                  onSubmit={this.handleSubmit.bind(this)}>
            <div 
              className='field' 
                style={styles.title}>
              <input type='text' placeholder='TODO' ref='text' />
            </div>
            <button type='submit' className='ui blue button'>
                添加
            </button>
          </form>
        )
      }
    }
    
    export default AppForm;
    

    你可以看到如下效果:

     
    react-add2.gif

6,完成篩選功能

  • 首先裏一下流程

    我們給下面的三個按鈕設置了不同的value1代表全部、2代表未完成、3代表已完成,然後我們根據相應的value,展示相應的list,給三個按鈕分別加上handleAllhandleActivehandleComplete三個方法,在onClick時觸發。

  • App.js

    添加一個加上choosevaluestate,默認爲1,即全選,同時將其傳入到<AppList/>中去,同時添加chooseValue的方法,然後傳入到AppFooter組件中去。

    ...
    state = {
          choosevalue : 1,
          data: this.props.data
    }
    
    ChooseValue (id) {
        this.setState({choosevalue:id});
    }
    
    ...
    
    <AppList  
          data={this.state.data} 
          choosevalue={this.state.choosevalue} 
    />
    <AppFooter 
      SubmitChooseValue={this.ChooseValue.bind(this)}
    />
    
      
    
  • AppFooter.js

    ...
    
    handleAll () {
        let all = this.refs.all.value;
        this.props.SubmitChooseValue(all);
    }
    
    handleActive () {
        let active = this.refs.active.value;
        this.props.SubmitChooseValue(active);
    }
    
    handleComplete () {
        let complete = this.refs.complete.value
        this.props.SubmitChooseValue(complete);
    }
    
    
    render () {
        return (
          <div>
            <h2 style={styles.top}>show</h2>
            <button 
              type='submit' 
              style={styles.top} 
              className='ui blue button' 
              value='1' 
              ref='all' 
              onClick={this.handleAll.bind(this)}
            > 
                  全部 
            </button>
            <button 
              type='submit' 
              style={styles.top} 
              className='ui blue button' 
              value='2' 
              ref='active' 
              onClick={this.handleActive.bind(this)}
            > 
              還未完成 
            </button>
            <button 
              type='submit' 
              style={styles.top} 
              className='ui blue button' 
              value='3' 
              ref='complete' 
              onClick={this.handleComplete.bind(this)}
            > 
              已完成 
            </button>
          </div>
        )
      }
    }
    
    export 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章