1. 程式人生 > >搭建一個react專案

搭建一個react專案

從頭開始建立一個React App - 專案基本配置

  1. npm init 生成 package.json 檔案.
  2. 安裝各種需要的依賴:
  • npm install --save react - 安裝React.
  • npm install --save react-dom - 安裝React Dom,這個包是用來處理virtual DOM。這裡提一下用React Native的話,這裡就是安裝react-native。
  • npm install --save-dev webpack - 安裝Webpack, 現在最流行的模組打包工具.
  • npm install --save-dev webpack-dev-server
    - webpack官網出的一個小型express伺服器,主要特性是支援熱載入.
  • npm install --save-dev babel-core - 安裝Babel, 可以把ES6轉換為ES5,注意Babel最新的V6版本分為babel-cli和babel-core兩個模組,這裡只需要用babel-cor即可。
    • 安裝其他的babel依賴(babel真心是一個全家桶,具體的介紹去官網看吧..我後面再總結,這裡反正全裝上就是了):
    • npm install --save babel-polyfill - Babel includes a polyfill that includes a custom regenerator runtime and core.js. This will emulate a full ES6 environment
    • npm install --save-dev babel-loader - webpack中需要用到的loader.
    • npm install --save babel-runtime - Babel transform runtime 外掛的依賴.
    • npm install --save-dev babel-plugin-transform-runtime - Externalise references to helpers and builtins, automatically polyfilling your code without polluting globals.
    • npm install --save-dev babel-preset-es2015
      - Babel preset for all es2015 plugins.
    • npm install --save-dev babel-preset-react - Strip flow types and transform JSX into createElement calls.
    • npm install --save-dev babel-preset-stage-2 - All you need to use stage 2 (and greater) plugins (experimental javascript).
  1. 開啟 package.json 然後新增下面的scripts:
"scripts": {
  "start": "webpack-dev-server --hot --inline --colors --content-base ./build",
  "build": "webpack --progress --colors"
}

命令列輸入 npm start 將要啟動webpack dev server.

命令列輸入 npm build 將會進行生產環境打包.

  1. 啟動webpack

Webpack是我們的打包工具,在我們的開發環境中具體很重要的作用,具有很多非常便捷的特性,尤其是熱載入hot reloading. webpack.config.js 是如下所示的webpack的配置檔案. 隨著app的不斷變化,配置檔案也會不斷的更新,這裡我們就用預設的webpack.config.js來命名這個配置檔案,假如你用別的名字比如webpack.config.prod.js那麼上面的指令碼build就需要相應的改變指定相應的配置檔名字:"build": "webpack webpack.config.prod.js --progress --colors"

var webpack = require('webpack');
module.exports = {
  entry: './src/app.js',
  output: {
      path: __dirname + '/build',
      filename: "bundle.js"
  },
  module: {
      rules: [{
          test: /\.js$/,
          exclude: /node_modules/,
          loader: 'babel-loader',
          query: {
              plugins: ['transform-runtime'],
              presets: ['es2015', 'react', 'stage-2']
          }
      }, {
          test: /\.css$/,
          loader: "style-loader!css-loader"
      }]
  }
};

  1. OK,我們專案的基本配置終於完成了,是時候開始寫Reac程式碼了.

React 基礎 - 建立你的第一個Component

在上面的專案的基本配置基礎上,我們開始書寫React的第一個元件來熟悉React的寫法與元件思想。

  1. 首先我們在專案根目錄中新建一個 index.html 檔案。 在這個基礎工程中, 我們使用bootstrap的樣式,直接引入一個cdn即可. 然後新增一個html標籤 <div id="app"></div>,我們的app就會注入到這個div中。 最後再引入 <script src="bundle.js"></script>,這是最後打包生成的js程式碼。

    以下是完整的程式碼:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Document</title>
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
    </head>
    <body>
      <div id="app"></div>
      <script src="bundle.js"></script>
    </body>
    </html>
    
  2. 建立一個新的資料夾 src. 我們app的大部分程式碼都將放在這個資料夾裡面。在 src 中建立 app.js,作為React App的根元件, 其他所有的元件都會注入到這個跟元件中。

  3. 首先我們需要匯入react,現在都已經用ES6的語法, import React from 'react'; , 然後我們要引入react-dom. 這裡面有react中最重要的一個虛擬dom的概念.引入程式碼:import ReactDOM from 'react-dom';

  4. 現在需要引入的依賴都已經完畢我們可以寫第一個元件了:

    class App extends React.Component {
      render(){ // Every react component has a render method.
        return( // Every render method returns jsx. Jsx looks like HTML, but it's actually javascript and functions a lot like xml, with self closing tags requiring the `/` within the tag in order to work propperly
          <div>
            Hello World
          </div>
        );
      }
    }
    

    注意這裡"Hello World"寫在 div中. 所有的jsx程式碼都需要寫在一個父div中.

  5. 最後我們需要把我們寫好的元件render給Dom,這裡就需要用到 ReactDOM.render 方法.

    App.js 的下面新增: ReactDOM.render(<App />, document.getElementById('app'));

    第一個引數就是我們App的根元件, 寫作<App />的形式. 第二個引數就是我們的APP將要主要的DOM元素. 在這個專案中,就是我們在index中寫的id為appdiv標籤。

Ok,我們的APP結構已經出來了,經典的hello world已經實現。馬上我們就在這個基礎上再實現經典的todo app。大致的原型就有一個輸入框用來輸入代辦事項然後新增到事件列表中。事件列表中每一個代辦事項被點選就會標註一條刪除線表示完成,點選後面的刪除按鈕則會將其從列表中刪除。通過完成這個APP的過程你將學會一個完整的react app的所有的基本構建塊。

生命週期方法和兩種形式的元件構建

我們從一些小的模組開始起步.一個元件component就是一個react app的構件塊. 有兩種形式的元件: 類元件(Class)和函式型元件(Functional). 在這個專案中,這兩種形式的元件我們都會使用, 並且使用生命週期的鉤子,同時也會使用state和props兩個react中重要的屬性。

  1. 首先在 src資料夾中新建components 資料夾,你的檔案結構就是這樣 ~/src/components

  2. 然後在components中新建檔案 ToDoApp.js。 對於所有的react元件我們都需要在頭部引入reactimport React from 'react';

  3. 下面我們寫一個類元件. 所有的class 元件有一個render方法用來返回jsx。

    ToDoApp的class就如下所示:

    class ToDoApp extends React.Component {
      render() {
        return (
          <div>To Do App</div>
        );
      }
    }
    
  4. 為了將這個元件注入到我們的APP中, 首先我們需要輸出它。 在這個元件程式碼底部新增 export default ToDoApp;

  5. 然後在app.js頂部我們新增 import ToDoApp from '.components/ToDoApp'; 匯入元件用來代替 Hello World 。 render中替換為新的jsx程式碼 <ToDoApp />半閉合型別的標籤即可。

  6. 然後在瀏覽器中你就可以看到"To Do App" 代替了原來的 "Hello World"!這樣我們就完成了將第一個子元件嵌入到根元件之中了,這就是構建react app的常規模式。下面繼續完善我們的元件。

  7. 返回到ToDoApp 中來構建我們的第一個代辦事項列表。首先我們使用bootstrap來構建比較方便且美觀。 用下面的jsx替換當前render方法中 return 中的jsx:

<div className="row">
  <div className="col-md-10 col-md-offset-1">
    <div className="panel panel-default">
      <div className="panel-body">
        <h1>My To Do App</h1>
        <hr/>
        List goes here.
      </div>
    </div>
  </div>
</div>
  1. 現在開啟瀏覽器, 你將會看到一個標題 "My To Do App" 下面跟隨一個bootstrap的panel元件裡面寫有 "List Goes Here",我們將在這個地方構建列表。 那麼我們如何將資料儲存在我們的列表中呢? 答案就是使用 state. 每一個類元件都有 state 屬性,可以通過 this.state在元件任何位置獲取並且用 this.setState({ key: "value" })這種方法來更新狀態。但是除非必要我們比較少使用state,這裡暫時先使用作為了解,後期會使用redux來管理狀態。

    ToDoApp中我們可以使用許多生命週期方法的鉤子, 其中一個就是componentWillMount。 這個方法的執行是在頁面載入並且render方法之前。可以在其中獲取列表資料,在我們的APP中直接用一個虛擬的陣列提供。(值得注意的是componentWillMount會引起很多小問題,因此真實專案中儘量不要使用,而是應該用componentDidMount)。
    ToDoApprender 方法之前新增:

      componentWillMount(){ // run before the render method
        this.setState({ // add an array of strings to state.
          list: ['thing1', 'thing2', 'thing3']
        })
      };
    

    現在我們獲取了一個虛擬列表,需要重點注意的就是react依賴於state和props,只有當state和props改變的時候react元件才會重新整理。

  2. 現在我們新增列表到這個view裡,這裡不是直接簡單的在裡面修改jsx,而是再建立一個新的元件來構建列表,這次我們學習使用函式型元件,需要注意的是函式型元件沒有生命週期方法和state屬性,它僅僅是一個返回jsx的函式,並且引數是props。

    那麼props到底是什麼呢?props是從父元件傳遞進子元件的資料的名字,這是一個很重要的概念,也是react app資料傳遞的最典型與最推薦的方法。通常我們將資料保持在app的頂端元件,通過元件讓資料流下來保證APP的精確執行。這些資料和props的一些處理可能會影響APP的執行,但是假如你按照這個課程的實踐流程來做,這些影響都會很小。

    再新建一個components資料夾並在其中新建一個List.js作為我們要建立的函式型元件。用const來新建一個函式,引數名字寫作props。
    函式形式如下所示:

    const List = (props) => { // we're using an arrow function and const variable type, a ES6 features
    
      return (
        <div>
          I'm a list!!!
        </div>
      )
    };
    
    export default List;
    
  3. ToDoApp.js引入 List用List 元件替換 List goes here.,寫法為 <List />.現在在瀏覽器中就可以看到"I'm a list!!!"

    現在我們來把這個變成真實的列表,首先就需要通過props傳遞資料,我們把這個從state中獲取的資料list通過命名為listItems的props傳遞,寫作: <List listItems={this.state.list} /> ,現在 List 已經通過props獲取了 ToDoApp中的資料。

    然後在 List 元件中我們需要render一個列表,先用下面的jsx程式碼代替:

    <div>
      <ul>
        {
          list // this is a variable we'll define next
        }
      </ul>
    </div>
    

    注意這個大括號,js可以在這裡面執行並將返回新增到view裡。首先我們定義一個列表變數:

    const list = props.listItems.map((el, i)=>(
      // All where doing here is getting the items listItems prop
      // (which is stored in the state of the parent component)
      // which is an array, and we're running the .map method
      // which returns a new array of list items. The key attribute is
      // required, and must be unique.
      <li key={i}><h2>el</h2></li>
    ));
    

    完整的元件如下:

    import React from 'react';
    
    const List = (props) => {
    
      const list = props.listItems.map((el, i)=>(
        <li key={i}><h2>el</h2></li>
      ));
    
      return (
        <div>
          <ul>
            {
              list
            }
          </ul>
        </div>
      )
    };
    
    export default List;
    
  4. 現在開啟瀏覽器就可以看到一列列表了。接下來就是給我們的專案加入功能了,包括新增新的事項,標註事項完成和刪除列表中事項。

給APP新增功能

1.函式型元件

首先我們需要新增一個input元素以便可以輸入代辦事項。因此我們在components資料夾中新建一個Input.js,然後在其中建立並輸出一個名叫Input的函式型元件。
把下面的jsx程式碼貼上到你的函式型元件return之中:

<form>
  <div
    className="form-group">
    <label
      htmlFor="listInput">
      Email address
    </label>
    <input
      type="text"
      className="form-control"
      id="listItemInput"
      placeholder="Add new todo"
    />
    <button
      className="btn btn-primary">
      Add Item
    </button>
  </div>
</form>

2. Input

現在我們的jsx沒有做任何特殊的事情,僅僅是一個基本的html檢視,不過我們先測試一下把其匯入到ToDoApp.js,形式就是<Input/>

這時候會發現一個輸入框和按鈕的檢視,這個元件的靜態檢視已經寫好了,下面就需要新增功能了。

3. Props

首先我們需要做的是如何獲取輸入框的值,因為這個輸入框的值需要在其他元件中獲取,所以我們並不想要在Input元件中來處理這個資料儲存。事實上,在子元件中儲存資料在任何時候都是不推薦的,我們應該將資料儲存在app的頂端元件並且通過props傳遞下來。

另一個需要記住的是即使我們目前把資料儲存在了上層的 ToDoApp 元件,後期還是會用redux來代替來處理整個app的資料。這裡先僅僅使用react的state來實現。

ok,我們在ToDoAppcomponentWillMountsetState中新增一個newToDo屬性用來儲存輸入框的值。

  componentWillMount(){
    this.setState({
      list: ['thing1', 'thing2', 'thing3'],
      newToDo: 'test'
    })
  };

同樣的就可以通過在<Input />上通過props傳遞下去。

4. 解構(Destructuring)

Input.js中我們通過引數props可以獲得上級元件傳遞下來的值, 但是還可以用ES6的新特性解構來作為引數,這樣看起來更加酷!

Input元件的props引數修改為({ value })這樣的引數形式,這樣可以把props這個物件引數解構為一個個鍵值對。直接看個小例子來就很明白了:

var props = {
  name: 'hector',
  age: 21
}


function log(props){
  console.log(props.name);
  console.log(props.age);
}

log(props);

is the same as this:

let props = {
  name: 'hector',
  age: 21
}

log = ({name, age}) => {
  console.log(name);
  console.log(age);
}

log(props);

5. setState

上面的newToDo僅僅是添加了一個state用來儲存輸入框的值,給定一個值,輸入框就會顯示,明顯還不是我們要的效果,我們需要做的是基於輸入框的值的改變來動態改變這個state。

為了實現這個功能,我們需要再新增一個onChange方法同樣利用props傳進Input元件: onChange={onChange}, 然後解構引數就是({ onChange, value })

然後在 ToDoApp.jscomponentWillMount 新增一個新的方法 onInputChange。這個方法有一個引數event, 它將要捕獲使用者在輸入框輸入的值。

onInputChange = (event) => {
  this.setState({ newToDo: event.target.value}); // updates state to new value when user changes the input value
};

6. 新增新列表事項

現在需要向列表中新增新的事項,也就是在提交後能把輸入框的值儲存並顯示到列表中。我們需要再新建一個onInputSubmit的方法,引數同樣是event,函式體內首先需要寫 event.preventDefault(),然後用 setState 方法把新事項新增到列表陣列中,但是,一定要注意我們的state應該是immutable的,這是react中必須遵循的一個準則,這樣才能保證對比性與可靠性。

為了實現這個功能, 需要用到this.setState 回撥函式,引數為previousState

this.setState((previousState)=>({
  list: previousState.list.push(previousState.newToDo)
}))

正如我上面的描述,最開始寫state的時候很多人都會犯這樣的錯誤,直接用push這樣的方法,修改了state,這樣就不算immutable的,我們一定要保證絕不直接修改原state。

這裡又可以用到ES6中的新特性了,擴充套件操作符,它通過遍歷舊陣列返回一個新陣列,使舊的陣列保持原樣,這樣我們就把事項新增到列表陣列末尾:

this.setState((previousState)=>({
  list: [...previousState.list, previousState.newToDo ], // the spread opperator is called by using the ... preceding the array
}));

在提交新增新事項的同時,需要將newToDo重置為''

this.setState((previousState)=>({
  list: [...previousState.list, previousState.newToDo ],
  newToDo: ''
}));

7. 劃掉事項

是時候新增劃掉事項的功能了。為了實現這個功能需要新增一個新的屬性用來標註是否需要劃掉,因此需要改變原來的陣列為一個物件陣列,每一個事項都是一個物件,一個key為item表示原來的事項內容,一個key為done用布林值來表示是否劃掉。 然後先把原來的onInputSubmit方法修改,同樣要注意immutable,使用擴充套件操作符如下:

onInputSubmit = (event) => {
  event.preventDefault();
  this.setState((previousState)=>({
    list: [...previousState.list, {item: previousState.newToDo, done: false }], // notice the change here
    newToDo: ''
  }));
};

屬性done新增完成後就需要新增一個方法當點選事項時候來改變這個值:

onListItemClick = (i) => { // takes the index of the element to be updated
  this.setState((previousState)=>({
    list: [
      ...previousState.list.slice(0, i), // slice returns a new array without modifying the existing array. Takes everything up to, but not including, the index passed in.
      Object.assign({}, previousState.list[i], {done: !previousState.list[i].done}), // Object.assign is a new ES6 feature that creates a new object based on the first param (in this case an empty object). Other objects can be passed in and will be added to the first object without being modified.
      ...previousState.list.slice(i+1) // takes everything after the index passed in and adds it to the array.
    ]
  }))
};

然後把這個方法通過props傳遞給List 元件,這裡就沒有使用解構引數傳遞,用來和Input的做對比。因為這個函式需要一個引數就是當前列表的序列號,但是肯定不能直接call這個函式否則會報錯,因此使用bind方法,出入i引數:

onClick={props.onClick.bind(null, i)}

當然還有另一種方法:

onClick={() => props.onClick(i)}

然後在事項內容的span標籤上新增 onClick 方法,改變當前事項的done值後,在通過判斷此布林值來進行樣式的修改新增或者劃掉刪除線。

<span
  style={
    el.done
    ? {textDecoration: 'line-through', fontSize: '20px'}
    : {textDecoration: 'none', fontSize: '20px'}
  }
  onClick={props.onClick.bind(null, i)}
>

8. 刪除事項

最後我們在新增刪除事項的功能,這個和劃掉事項非常相似,我們只需要新增一個刪除按鈕,然後再新增一個方法修改列表,具體程式碼如下:

<button
  className="btn btn-danger pull-right"
  >
  x
</button>
deleteListItem = (i) => {
  this.setState((previousState)=>({ // using previous state again
    list: [
      ...previousState.list.slice(0, i), // again with the slice method
      ...previousState.list.slice(i+1) // the only diffence here is we're leaving out the clicked element
    ]
  }))
};

deleteListItem 方法傳遞到列表元件中然後在刪除按鈕上繫結即可,仿照上一個自己寫一下就好。

現在我們有一個完整功能的APP了,是不是感覺很cool,這個就是不用redux時候的形態了,但是你會發現當狀態越來越複雜時候很繁瑣,因此我們下面就要介紹redux來管理狀態了。

遷移到redux的準備工作

截至目前我們已經學會如何用webpack和babel搭建react應用,構建類元件和函式型元件並處理state,新增功能。然而這只是基本滿足一個小型應用的需求,隨著app的增長,處理資料和行為會越來越吃力,這就是要引入redux的必要性。

那麼redux如何處理資料?首先,redux給你的app一個單一的state物件,與flux等根據view來劃分為多個state物件正好相反。你可能會有疑問,一個單一的物件來處理一個複雜的app豈不是非常複雜?redux採用的方法是把資料處理分為reducer functionsaction creatorsactions然後組合在一起工作流線型的處理資料。

1. 首先安裝必須的依賴

首先安裝 redux and react-redux

npm install --save redux
npm install --save react-redux

然後安裝 redux middleware,這裡就先安裝 redux-logger,它的功能是幫助我們開發。

npm install --save redux-logger

還有一些常用的中介軟體,比如 redux-thunk and redux-promise, 但是在我們的這個專案中暫時先不需要,可以自行去github瞭解。

2. 構建

使用redux構建react應用一般都有一個標準的模板,可能不同模板形式上有區別,但是思想都是一樣的,下面就先按照一種檔案結構來構建。

首先我們在src中新建一個資料夾redux,然後在其中新建一個檔案configureStore.js,新增以下程式碼:

import { createStore, applyMiddleware, combineReducers } from 'redux';
import createLogger from 'redux-logger';

createStore 是由redux提供的用來初始化store的函式, applyMiddleware是用來新增我們需要的中介軟體的。

combineReducers 用來把多個reducers合併為一個單一實體。

createLogger 就是我們這裡唯一使用的一箇中間件,可以console出每一個action後資料的詳細處理過程,給除錯帶來了很大方便。

然後新增下面程式碼:

const loggerMiddleware = createLogger(); // initialize logger

const createStoreWithMiddleware = applyMiddleware( loggerMiddleware)(createStore); // apply logger to redux

這裡暫時沒有完成,需要後面的模組寫完了再匯入到這裡繼續來完成。

3. 模組Modules

src/redux/ 新建一個資料夾 modules。在這個資料夾中我們將存放所有的reducersaction creatorsconstants。這裡我們使用的redux組織結構叫做ducks,思想就是把相關的reducersaction creatorsconstants都放在一個單獨的檔案中,而不是分開放在多個檔案中,這樣修改一個功能時候直接在一個檔案中修改就可以。

modules 檔案中新建 'toDoApp.js',注意這裡的命名是依據容器元件的名字來命名,這個也是規範,容易管理程式碼。

現在我們可以開始建立initial statereducer function,這其實非常簡單,state就是一個js物件,reducer就是js的switch語句:

const initialState = {}; //The initial state of this reducer (will be combined with the states of other reducers as your app grows)

export default function reducer(state = initialState, action){ // a function that has two parameters, state (which is initialized as our initialState obj), and action, which we'll cover soon.
  switch (action.type){
  default:
    return state;
  }
}

4. 完善Store

現在我們已經完成了第一個reducer,可以將其新增到 configureStore.js 中去了, 匯入: import toDoApp from './modules/toDoApp';

然後用combineReducers 來組合當前的reducer,因為未來會有更多的模組加入。

const reducer = combineReducers({
  toDoApp
});

最後在底部加入下面完整的程式碼:

const configureStore = (initialState) => createStoreWithMiddleware(reducer, initialState);
export default configureStore;

Cool. We're done here.

5. Connect

現在我們已經有了一個reducer,那麼怎麼和app建立聯絡呢?這需要兩步工作。

前面已經講過類元件和函式型元件,有時候也可以稱為smart componentsdumb components,這裡我們新增一種容器元件,顧名思義,這種元件就是作為一個容器用來給元件提供actionsstate

下面來建立第一個容器元件,首先在 /src/ 下新增一個資料夾containers,然後再其下面新建一個檔案 toDoAppContainer.js
在檔案頂部首先匯入 connect 用來將容器和元件聯絡在一起,

import { connect } from 'react-redux';
import ToDoApp from '../components/ToDoApp.js'

connect 這個函式被呼叫兩次, 第一次是兩個回撥函式: mapStateToProps and mapDispatchToProps。 第二次是把statedispatch傳入元件的時候。這裡的dispatch又是什麼呢?

當我們需要在redux中發生某些行為時候,就需要呼叫dispatch函式傳遞一個action然後呼叫reducer這一套流程。因為我們還沒有編寫具體的行為,這裡就暫時空白,後面再補,程式碼形式如下:

function mapStateToProps(state) {
  return {
    toDoApp: state.toDoApp // gives our component access to state through props.toDoApp
  }
}

function mapDispatchToProps(dispatch) {
  return {}; // here we'll soon be mapping actions to props
}

然後在底部新增:

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(ToDoApp);
  1. Provider

redux的基本工作已經完成,最後一步就是返回到app.js 檔案, 首先我們不再需要匯入 ToDoApp 元件,而是用容器元件ToDoAppContainer來替代,然後需要匯入 configureStore 函式和 Provider,在頭部新增程式碼:

import { Provider } from 'react-redux';
import ToDoAppContainer from './containers/ToDoAppContainer';
import configureStore from './redux/configureStore';

configureStore is the function we created that takes our combined reducers and our redux middleware and mashes them all together. Let's intialize that with the following line:

const store = configureStore();

然後return的jsx中同樣需要把ToDoApp 改為 ToDoAppContainer,然後需要用Provider 元件將其包裹,它的作用就是將整個app的state傳遞給它所包裹的容器,從而使容器元件可以獲取這些state。

<Provider store={store}> // we pass the store through to Provider with props
  <ToDoAppContainer />
</Provider>

現在整個redux的基本結構已經搭建起來,下一步就可以把整個行為邏輯程式碼補充進去就可以了。

Redux Actions 和 Reducers

搭建起redux的基本結構後,就可以填充redux的元素了,簡單來說我們只需要記住四個概念, Types, Actions, Action Creators, and Reducers。然後把這些元素用ducks的檔案組織結構組織起來就可以了。

Ducks

規則

在module中我們需要遵循下面的程式碼風格和命名方式:

  1. 須用 export default 輸出名為 reducer()的函式
  2. 須用 export 輸出 函式形式的action creators
  3. 須用 npm-module-or-app/reducer/ACTION_TYPE
    的命名形式來命名action types,因為到後期很多reducer,不同的人協同工作難免會出現命名重複,這樣子加上app和模組的字首的話就不會出現命名衝突的問題。
  4. 須用大寫的蛇形方式UPPER_SNAKE_CASE來命名action types

Types

這個types就是上面第三條中需要按照ducks的規範命名的常量名字,將其寫在檔案的頂部,當action 觸發時候會傳遞給reducerreducer的switch語句會根據這個type來進行相應的資料處理。

const ADD_ITEM = 'my-app/toDoApp/ADD_ITEM';
const DELETE_ITEM = 'my-app/toDoApp/DELETE_ITEM';

Actions

Actions 就是一個至少包含type的簡單的js物件,同時可以包含資料以便傳遞給reducer。當用戶在頁面上觸發了某種行為,一個aciton creator將會發送acitonreducer做資料處理。

action示例如下:

{ type: ADD_ITEM, item: 'Adding this item' }
{ type: DELETE_ITEM, index: 1 }
{ type: POP_ITEM }

Action Creators

Action creators 是建立acitons並傳遞給reducer的函式,它通常返回一個action物件,有時候借用thunk這樣的中介軟體也可以返回dispatch多個actions,在我們的app中為了簡化暫時不涉及這個模式。

function addItem(item){
  return {
    type: ADD_ITEM,
    item // this is new ES6 shorthand for when the key is the same as a variable or perameter within the scope of the object. It's the same as item: item
  }
}

Reducers

reducer是唯一可以觸碰store的元素,初始值為initialState,形式上就是一個簡單的switch語句,但是注意不能直接改變state,因為state是immutable。也就是說我們不能直接使用.pop or .push這些方法運算元組。

下面是示例程式碼:

const initialState = {
  list: []
};

export default function reducer(state = initialState, action){
  switch (action.type){
  case ADD_ITEM:
    return Object.assign(
      {},
      state,
      { list: [...state.list, action.item]} // here we see object.assign again, and we're returning a new state built from the old state without directly manipulating it
    )
  default:
    return state;
  }
}

概念已經介紹完畢,下面開始將原來的功能邏輯用redux重寫。

1. Initial state

首先我們在 src/redux/modules/toDoApp中宣告initialState

const initialState = {
  list: [{item: 'test', done: false}] // just added this to test that state is being passed down propperly,
  newToDo: ''
};

export default function reducer(state = initialState, action){
  switch (action.type){
  default:
    return state;
  }
}

現在在 ToDoApp.jsrender() 方法中return之前新增console.log(this.props) 會打印出下面的物件:

toDoApp: Object
  list: Array[1]
    0: "test"
    length: 1
    __proto__: Array[0]
  __proto__: Object
__proto__: Object

測試通過,我們就可以傳遞這些資料給子元件了,這裡就可以把原來List元件的 listItems prop和Inputvalue prop替換掉了。

<List
  onClick={this.onListItemClick}
  listItems={this.props.toDoApp.list}
  deleteListItem={this.deleteListItem}
/>
<Input
  value={this.props.toDoApp.newToDo}
  onChange={this.onInputChange}
  onSubmit={this.onInputSubmit}
/>

這裡只是替換掉了資料,下面還需要把action也替換。

3. Input action

這個過程就是把我們原來在ToDoApp 元件的行為邏輯全部遷移到redux資料夾下的 toDoApp module中去。

const INPUT_CHANGED = 'INPUT_CHANGED';

export function inputChange(newToDo){
  return {
    type: INPUT_CHANGED,
    newToDo
  }
}

然後在reducer的switch中新增如下處理:

case INPUT_CHANGED:
    return Object.assign(
      {},
      state,
      {newToDo: action.value}
    );

toDoAppContainer.jsmapDispatchToProps 函式就需要返回相應的action,首先匯入 inputChange, 具體程式碼如下:

import { connect } from 'react-redux';
import ToDoApp from '../components/ToDoApp.js'
import {
  inputChange
} from '../redux/modules/toDoApp'; // we added this

function mapStateToProps(state) {
  return {
    toDoApp: state.toDoApp // gives our component access to state through props.toDoApp
  }
}

function mapDispatchToProps(dispatch) {
  return {
    inputChange: (value) => dispatch(inputChange(value)) // we added this
  };
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(ToDoApp);

這樣state和action都傳遞給了toDoApp然後再通過props傳遞給子元件就可以使用了,具體都可以看專案最終程式碼。

4. 其他 actions

其他acitons的程式碼模式跟上面的基本一樣,這裡不在贅述。

總結

到這裡一個使用webpack打包的react+redux(ducks)的基本應用模型就出來了,雖然簡單但是是我們進行更復雜專案的基礎,並且有了這些基礎後面的路程將會順暢多了,一起加入react的大家庭吧。

相關推薦

如何手動使用webpack搭建一個react專案

前言搭一個腳手架真不是一件容易的事,之前為了學習webpack是怎麼配置的選擇自己搭建開發環境,折騰了好幾天總算對入口檔案、打包輸出、JSX, es6編譯成es5、css載入、壓縮程式碼等這些基礎的東西有了一個大體的瞭解。 大體專案結構(模仿網上大佬)   然後就是正題了,當然最先要做的是

使用webpack搭建一個react專案

npm init package.json檔案配置 { "name": "serach-bar", "version": "1.0.0", "description": "", "main": "index.js", "scrip

react開發:從零開始搭建一個react專案

從頭開始建立一個React App - 專案基本配置 npm init 生成 package.json 檔案.安裝各種需要的依賴: npm install --save react - 安裝React.npm install --save reac

搭建一個react專案

從頭開始建立一個React App - 專案基本配置npm init 生成 package.json 檔案. 安裝各種需要的依賴:npm install --save react - 安裝React. npm install --save react-dom - 安裝React Dom,這個包是用來處理vi

一個React專案——使用官方腳手架create-react-app搭建一個React專案

準備工作 1,node js環境 一、下載create-react-app           1)使用命令列的方式進入目標資料夾(shift+右鍵,此處開啟powershell視窗)(win10)           2)輸入命令  npx create-react

clone一個react專案怎麼執行

首先當你從git上面clone一個專案的時候怎麼讓專案跑起來, 首先看專案目錄結構,找到README.md上面有專案執行的步驟, 如果沒有可以看package.json檔案,找到scripts 上面有dev 所以跑起來專案就使用npm run dev 有start 就使用 np

從零開始使用vue-cli搭建一個vue專案及注意事項

一、安裝node.js   1.根據電腦的自行下載node.js安裝包http://nodejs.cn        2.點選安裝,按照正常的的一路點選下去   3.驗證安裝是否成功,按鍵win+r,輸入cmd開啟命令列工具,點選確認後再輸入node -v 出現版本好說明npm安裝成功  

建立第一個React專案

一.環境準備:     2.將npm映象改為淘寶cnpm:            1.得到原本的映象地址:                    npm get registry                    > https://registry.npmj

用vue-cli來搭建一個vue專案

用控制檯生成vue專案 在控制檯中輸入vue init webpack project(自己取的專案名稱) “project1”是自己取的專案名稱 下面的: “Project name”寫專案的名稱,不可以出現大寫字母否則會報錯 "Project descri

【gulp】用gulp搭建一個前端專案

一、安裝與建立 1.安裝node:https://nodejs.org/zh-cn/ 2.安裝gulp:npm install gulp -g 3.新建一個資料夾用來存放專案 4.在命令列進入建立的資料夾輸入 gulp init即可 5.新建一

搭建一個VUE專案

1.安裝node.js 官網地址:https://nodejs.org/en/download/  根據需要選擇下載,安裝時一直點下一步就好(傻瓜式安裝),安裝路徑可以自定義。  檢測:命令視窗(win+R 執行cmd)輸入命令 node -v,出現版本號則說明安裝成功。 

如何搭建一個Maven專案

如何搭建一個Maven專案 大家好這裡和大家分享一下如何搭建一個maven專案,如果對你幫助,請大家多多關注. 1 第一步開啟本地倉庫settings.xml的配置路徑 2 第二步修改你本地倉庫的配置路徑 D:/maven/repo 3第三步在settings.xml中配置mirro

個人搭建React專案React音樂播放器

該專案是本人自使用react框架以來製作的較大的專案,目前該專案放在github上,感興趣的朋友可以去看看一下,覺得還行的話可以給個star,哈哈 地址:https://github.com/cocoxiaoyue/react-music-player 專案環境 執行   1、該專案是基於node環境,

如何開始一個react專案

1、新建目錄——>在dos視窗進入到目錄路徑下,輸入npm init   //進行初始化 初始化時packname和entry point(入口檔案)需要輸入值,其餘的可以不輸值 完成後1-1目錄下會生成一個package.json的配置檔案,裡面即是包的

在Linux環境下快速搭建一個javaweb專案網站(阿里雲ubuntu)

前言: 完成一個web專案或者完成了自己部落格的編輯之後,迫切想釋出都網際網路上,展示自己一下。 這裡主要介紹javaweb專案如何快速放在伺服器上,並且釋出至網際網路。 1、前期裝備。 主要是申請伺服器,配置ftp\ssh環境,配置tomcat伺服

簡單搭建一個maven專案

使用java開發已經有一段時間了,在實際工作中,大多是都是使用的別人搭建的框架,一個框架裡面涉及到很多東西,對於很多知識都沒有去好好思考,然後自己想一邊工作一邊抽時間,重新做一個完整的專案來沉澱下自己學過的知識。 首先是簡單的搭建一個maven專案,因為很想弄一個分散式系統

1.SpringBoot之Helloword 快速搭建一個web專案

背景:   Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。通過這種方式,Spring Boot致力於在蓬勃發展的快速應用開發

搭建一個nodejs專案 使用express

初始化專案 新建一個資料夾,執行 npm init 初始化專案 mkdir wqs_node cd wqs_node npm init 按照提示輸入一些專案的相關資訊 D:\web\node>cd wqs_node D:\web\node\wqs_node&

從零開始搭建一個主流專案框架(三)—RxJava2.0+Retrofit2.0+OkHttp

個人部落格:haichenyi.com。感謝關注   上一篇,我們把mvp+dagger加進去了,這一篇,我們把網路請求加上   我這裡的網路請求是用的裝飾者模式去寫的,什麼是裝飾者模式呢?在不必改變原類檔案和使用繼承的情況下,動態地擴充套件一個物件的功能。

從零配置Webpack4.0搭建一個React工程

最近一直大部分精力都在搞App以及Node。前端這塊作為初心,還是不能落下。這裡不用cli。一步一步記錄從零配置Webpack,來搭建起React專案。複習一下Webpack的配置。以及一些前端工程化的一些思考前段工程化思考說道前端工程化,最重要的一個目的就是:解放生產力。對