標籤:
react這麼熱門的架構也不介紹了,redux是一個單項資料流的小架構,當然不只配合react,它起初是為react而配的,現在面向所有了,比如ng-redux的項目。redux做為react的標準搭配,大有超越flux的勢頭。今天show一個例子來入門redux。源碼在此。 (本文預設你已有react基礎,es6基礎)
這個效果很簡單就是一個count計數,+++++的按鈕按一下就會+1;input裡面寫什麼,submit就會alert什麼內容,這個demo很雜亂,是我修改的官方counter的例子,加了一些實驗的東西,比較適合熟悉redux這個東西。
大家都知道,react是由一個個組件構成的,每個組件都是與其他沒有關係的,資料的傳遞都是通過props。redux實現的單項資料流就是為react量身定做的。它維護了一個store,處理一些東西,商務邏輯,資料都在這個store裡面,就把它看成一個操作資料的顯示資料的東西,這個東西把它當作props傳入react組件,然後就可以用來顯示和操作資料了。這個react組件和平常寫的react組件差不多,但是裡面的處理資料是用store,也就是傳入的props處理。詳細的教程請戳redux中文api。
1.首先看一下首頁的html,很簡單,沒什麼東西。裡面這個ul其實沒啥用的,哈哈哈。其實就是一個div,裡面的東西都讓react處理。引入一個bundle.js(webpack壓縮後的檔案)
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>react+redux</title></head> <body> <div id="root"> <ul> <li>l1</li> <li>l2</li> <li>l3</li> <li>l4</li> <li>l5</li> </ul> </div> <script type="text/javascript" src="bundle.js"></script> </body></html>
2.主要就是main.js裡的代碼
import {render} from "react-dom"import React from "react"import {Provider} from "react-redux"import configureStore from "./store/configureStore"import App from ‘./containers/app.js‘const store =configureStore();render((<Provider store={store}> <App /></Provider> ),document.getElementById(‘root‘));
我們看它做了什麼。引入了render,React,Provider,和自己寫的配置store-configureStore,還有經過redux處理過的組件App。render只是es6裡的寫法,也可以換成平常react的寫法,就不列出了。Provider也不用管,只知道redux就是這麼寫組件就可以了,裡面把通過自己建立的store傳入到react組件,然後把處理過的App組件扔在那裡,就完了。主要我們看它的store怎麼建立的,和App怎麼處理的。
3.先看store怎麼處理的。這裡是/store/configureStore.js
import {applyMiddleware,createStore} from "redux";import thunk from "redux-thunk";import reducer from "../reducers/reducer.js"const createStoreWithMiddleware=applyMiddleware(thunk)(createStore)export default function configuerStore(initialStore){ const store=createStoreWithMiddleware(reducer,initialStore); return store;}
它引入了中介軟體thunk,具體的作用就是讓action可以通過函數的處理,這個中介軟體據說不到10行。詳情看上面連結的教程。可以看到建立store的函數createStoreWithMiddleware(reducer,initialStore);傳入的reducer和initialStore,initialStore是初始的Store狀態,可以隨便取結構,比如本例的store只有一個count欄位,用來儲存count數字。reducer是重要的一點,它是執行商務邏輯的地方,它是一個純函數,任何時候結果都是不變的(傳入相同的東西)。
4.reducer /reducers/reducer.js"
import {combineReducers} from "redux"import { ADD,RED } from ‘../actions/action‘function counter(state=0,action){ switch(action.type){ case ADD: return state+1; case RED:return state-1; default:return state; }}const rootReducer=combineReducers({ counter})export default rootReducer;
combineReducers也不用管,他是組合多個reducer的,用於拆分業務。reducer就是如何改變資料的一個東西,這個counter的函數就是一個reducer,它是通過看action的type的值來操作state的,如果action的type是ADD,state就+1,是RED就-1;它是個純函數,不論你什麼時候傳入相同的action,它輸出都不會變的。這個action又是什嗎?
5.action actions/action.js
export const ADD="ADD"export const RED="RED"export function increment(){ return { type:ADD }}export function decrement(){ return { type:RED }}export function ince(){ return (dispatch,getState) =>{ //const {counter}=getState() dispatch(increment()); }}export function dece(){ return (dispatch,getState)=>{ //const {counter}=getState() dispatch(decrement()) }}
為了簡單,我把官網例子的action改了,就留下加減兩個action。它是什麼呢?你如何改變資料就看它了,它定義的ADD,RED兩個常量,作為type,redux規定action必須有type屬性,別的它不管。redux提供的api只有幾個,其中有dispatch和getState,dispatch是唯一改變state的api,getState是得到當前state,所以dispatch(action)就是改變當前state的方法,我們想增加count,就是dispatch(increment());我們把它封裝成ince()傳遞到組件裡,讓它可以直接改變state,也就是每次點擊+++++就執行一下ince()這樣就可以了。
總結一下,這就是整個store了,它建立了一個store,傳入了reducer(怎麼根據action資料),reducer裡包含了action(改變什麼資料,具體怎麼改)。把這個store傳入組件App,就能通過props裡面的方法改變count了。
6.經過redux修飾的組件
看main.js裡面直接把封裝的App展現出來了,我們看它怎麼處理的App /containers/app.js
import {bindActionCreators} from "redux"import {connect} from "react-redux"import App from ‘../component/App.js‘import *as Actions from ‘../actions/action‘function mapStateToprops(state){ return { counter:state.counter }}function mapDispatchToProps(dispatch){ return bindActionCreators(Actions,dispatch)}export default connect(mapStateToprops,mapDispatchToProps)(App)
bindActionCreators,惟一使用 bindActionCreators
的情境是當你需要把 action creator 往下傳到一個組件上,卻不想讓這個組件覺察到 Redux 的存在,而且不希望把 Redux store 或dispatch傳給它。它把所有的Action都封裝了。 bindActionCreators
把action裝上一層
這裡的App組件就是普通react組件,修飾它的方法就是connect。我們看connect幹了什麼。
connect傳入兩個函數為參,第一個參數的名字mapStateToprops,也很清楚了,你需要把state裡的什麼放到props裡,這裡就只有counter一個欄位。第二個mapDispatchToProps,把dispatch傳入到props,讓組件可以調用dispatch來改變資料的結構,然而有bindActionCreators,就不用dispatch了,而且我們還在action裡封裝的ince(),裡面就是dispatch增加的action,也不用dispatch了。
把資料,怎麼修改資料傳到props裡,然後就可以用了。
最後看一下App這個組件,怎麼用資料和怎麼修改資料呢?
7.APP /component/App.js
import {render} from "react-dom"import React from "react"class App extends React.Component { constructor(props){ super(props); this.handleClick=this.handleClick.bind(this); this.onSubmit=this.onSubmit.bind(this); } handleClick(){ const {ince,dece,counter} = this.props; ince(); } onSubmit(event){ alert(this.refs.text.value); } render() { const {ince,dece,counter} = this.props; return( <form onSubmit={this.onSubmit}> <input ref=‘text‘ type="text" /> <div>{counter}</div><input type="button" onClick={this.handleClick} value="++++++++++" /> <button>submit</button> </form> ) }} class Bpp extends React.Component{ render(){ return ( <div>{this.props.fuk} </div>) }}class Cpp extends React.Component{ render(){ const {ince,dece,increment,decrement,counter} =this.props; const id=this.props.params.id return ( <div>this is cpp id為{id}</div>) }}export default App;
Bpp和Cpp沒有顯示出來,只是用作實驗,刪掉也沒事。(我只是忘記刪了)
這裡寫組件是用es6的寫法寫的。es6封裝了原生js的prototype操作,然而它是個不完全的文法糖,因為在裡面不能寫屬性,public property也不行。
引入了React,render相當於ReactDOM.render。
組件用extends關鍵字繼承React.Componet,其實還可以用React.createClass,其實是一樣的。後者還是有很多react開發人員使用的,因為它比較方便,為啥說比較方便,後面再提。
看一下這個jsx,就是一個form表單,提交觸發submit事件,一個輸入框,一個+++++的按鈕,一個div裡面是{counter},這樣顯示資料。
值得一提的是const {ince,dece,counter} = this.props;把props傳的東西拿出來。拿出來什麼呢?資料和如何修改資料的東西。這裡只拿出了ince和dece,他們是我們封裝的action,在裡面dispatch了,你就直接用它操作資料就可以了。counter是我們的資料,在div裡{counter}就顯示了。
onSubmit={this.onSubmit},submit執行自身定義的事件,彈出this.refs.text的值,也就是input的值。
還有注意的就是constructor裡面的東西了,這個東西是在class繼承的時候執行的,super(props);是必有的,如果不寫也會自動給你加上,就是執行一遍父類的this。要注意的是
this.handleClick=this.handleClick.bind(this);
this.onSubmit=this.onSubmit.bind(this);
這兩個必須要綁定一下this,不然在這兩個函數裡取不到this,這很坑的,而且還很麻煩。再加上es6的class功能並不完善,有的地方替代不了js的原型操作,這也就促成了很開發人員用React.createClass寫,組件生命週期事件還可以直接用,自然是比較方便的。
總結,這就是整個例子的代碼了,
這裏手繪了一幅圖,主程式分為建立store和修飾app兩部分,主程式main.js將他們弄到一塊就完成了react-redux的例子。裡面用的一些api沒有詳細講解,詳情看上面給出的redux中文api的連結。
webpack裡面沒有用任何複雜的工具,僅僅是用babel轉換了一下es6,其他就沒有什麼了。
初入react-redux