時間:2016.4.7-17:24
作者:三月懶驢
入門配置文章:連結
準備
在你的專案下面加入redux / react-redux
npm install redux --save
npm install react-redux --save
入門小例子
網上有很多關於Redux的解析了,我也不從抽象化去講解整個redux的作用,而發現講解Redux的程式設計化例子其實很少,所以在這裡用程式碼來說明一下。什麼叫做redux,以及它的Action/reducer/store
看過我上一遍專案環境搭建的朋友應該有一個基本的專案目錄架構。
主要寫程式碼的還是在app這個資料夾裡面,裡面的目錄分佈
- component -->放置元件的
- redux -->放置action && reducer
- redux_lesson -->目前是放置用Provider打包出來的元件
- main.js -->程式入口
程式碼開始前的思考
我們現在要做一個很簡單的東西,用前端的話來說就是一個div標籤,裡面放置一個數字0,當我們點選這個div的時候,裡面的數字就遞增。這裡面我們要進行一步就是,寫程式碼前的思考。
如上所說,我們的需求就是:點選,數字遞增。那麼我們的一些引數應該定義出來了。
改變View的資料—state
個人簡單理解的state就是可以反映到view上的可變資料,這裡我們的state定義如下
state = {count:0}
改變state的鑰匙—Action
同樣是個人理解:state是可變的,但不是隨便的可變,要改變它,就需要一把鑰匙去開啟這道大門,而action就是這把鑰匙了
我們把這個action定義成如下:
increaseAction = {type:`increase`}
Action 本質上是 JavaScript普通物件。我們約定,action內使用一個字串型別的 type欄位來表示將要執行的動作。多數情況下,type 會被定義成字串常量。
改變state的動作—Reducer
個人的胡亂理解:有了state,有了要改變state的鑰匙Action,那麼誰來進行改變state的操作?reducer就是這麼一個加工車間,你拿著原料(state)和鑰匙(Action)進去總車間,用鑰匙(Action)開啟對應的生產線,生產出來新的產品(也是state)回去
let reducer = (state={count:0},action)=>{
//這裡面傳遞進來兩個引數,
//一個是我們前面定義的state,如果木有傳入的話,就用{count:0}
//一個是我們前面定義的action,下面就要檢查它的type來確定操作
let count = state.count
switch(action.type){
//如果鑰匙插對了孔,我們就返回進行了相應操作後的state物件
case `increase`:
return {count:count+1}
break
//如果鑰匙都不對,就返回沒操作過的state
default:
return state
}
}
被我吃了的store
因為相對前面三個東西來說,store是在太容易理解了,引入官方的話:
Store 就是把它們聯絡到一起的物件。Redux 應用只有一個單一的 store。當需要拆分處理資料的邏輯時,使用 reducer 組合 而不是建立多個 store。
開始真正寫程式碼了
其實上面的步驟我們都把一個redux處理資料的相關工作做的差不多了,那麼接下來就是要真正的去寫成程式
建立action
檔案位置: app/redux/action.js
export const increaseAction = {type:`increase`}
建立reducer
檔案位置: app/redux/reudcer.js
let reducer = (state={count:0},action)=>{
let count = state.count
switch(action.type){
case `increase`:
return {count:count+1}
break
default:
return state
}
}
export default reducer
生成store,打包出新元件
重要的事情:store只有一個!
檔案位置: app/redux_lesson/lesson_0.js
`use strict`
import React from `react`
import { createStore } from `redux`
import { Provider,connect } from `react-redux`
//這個index.js檔案會在在下一步建立
import Index from `../component/index`
import reducers from `../redux/reducer`
//建立store
let store = createStore(reducers)
/*
mapStateToProps你可以理解成在下面connect的時候為元件提供一個props,這個props的值是redux的state
*/
let mapStateToProps = (state) =>{
return {count:state.count}
}
//連線你的state和最外層的元件
let Content = connect(mapStateToProps)(Index)
let {Component} = React
//使用Provider來把新的App元件打包出來
class App extends Component{
render(){
return <Provider store={store}><Content /></Provider>
}
}
export default App
建立View
在View裡面我們會接受到兩個props。一個是在mapStateToProps生成的state,一個是store給我們的dispatch,這是是一個函式,我們用它的方法很簡單粗暴,往裡面傳入一個Action就行了,它接受了這個Action就會告訴reducer去執行。
檔案位置: app/compoment/index.js
`use strict`
import React from `react`
import { connect } from `react-redux`
//請注意這裡面引入了action
import {increaseAction} from `../redux/action`
let {Component,PropTypes} = React
class Index extends Component{
//這一步是檢查傳入的各個prop型別是否正確
ProTypes = {
count:PropTypes.number.isRequired,
}
constructor(props){
super(props)
}
handleClick(){
/*
這一步輸入this.props可以看到,其實裡面有兩個東西
在下面的render裡面我們用到了this.props.count這個
那麼這裡我們要用到dispatch
*/
console.log(this.props)
let {dispatch} = this.props
//粗暴簡單的使用
dispatch(increaseAction)
}
render(){
let {count} = this.props
return <div onClick = {this.handleClick.bind(this)} style={styles.circle}>{count}</div>
}
}
//樣式檔案,不用細看
let styles = {
circle:{
width:`100px`,
height:`100px`,
position:`absolute`,
left:`50%`,
top:`50%`,
margin:`-50px 0 0 -5px`,
borderRadius:`50px`,
fontSize:`60px`,
color:`#545454`,
backgroundColor:`#fcfcfc`,
lineHeight:`100px`,
textAlign:`center`,
}
}
export default Index
進一步優化程式碼
要做一個點選遞增就需要那麼多步驟是不是很煩惱?但是如果專案大起來之後,你想像這樣,你就可以建立不同的鑰匙Action,再編寫不同的生產線reducer來修改各自的state,但是如上所做,我們的邏輯程式碼(點選遞增)和View還是捆綁在一起(就是在元件裡面使用dispatch)這個方法是不可取的。所以下一步我們就要進一步優化我們的程式碼
檔案位置: app/redux_lesson/lesson_0.js
`use strict`
import React from `react`
import { createStore } from `redux`
import { Provider,connect } from `react-redux`
import Index from `../component/index`
import reducers from `../redux/reducer`
/*
注意:這裡是新增的
相對原來,我們在最外層打包這裡引入Action
*/
import {increaseAction} from `../redux/action`
//建立store
let store = createStore(reducers)
/*
mapStateToProps你可以理解成在下面connect的時候提供一個state
*/
let mapStateToProps = (state) =>{
return {count:state.count}
}
/*
注意:這裡是新增的
mapDispatchToProps你可以理解成在下面connect的時候提供一個放置好鑰匙的函式onIncreaseClick,直接呼叫就可以去reducer修改state了
*/
let mapDispatchToProps = (dispatch) =>{
return{onIncreaseClick:()=>dispatch(increaseAction)}
}
/*
注意:這裡是修改了的
連線你的state和最外層的元件
*/
let Content = connect(mapStateToProps,mapDispatchToProps)(Index)
let {Component} = React
//使用Provider來把新的App元件打包出來
class App extends Component{
render(){
return <Provider store={store}><Content /></Provider>
}
}
export default App
檔案位置: app/compoment/index.js
`use strict`
import React from `react`
import { connect } from `react-redux`
/*
注意:這裡是修改樂的
現在不用引入action了,因為前一步已經把鑰匙Action放到相應的函式中去,作為props傳入元件裡面
*/
//import {increaseAction} from `../redux/action`
let {Component,PropTypes} = React
class Index extends Component{
//這一步是檢查傳入的各個prop型別是否正確
ProTypes = {
count:PropTypes.number.isRequired,
onIncreaseClick:PropTypes.func.isRequired,
}
constructor(props){
super(props)
}
handleClick(){
/*
注意:這裡是修改過的
現在,我們把打包好的,帶著鑰匙的函式進行呼叫
*/
console.log(this.props)
let {onIncreaseClick} = this.props
onIncreaseClick()
}
render(){
let {count} = this.props
return <div onClick = {this.handleClick.bind(this)} style={styles.circle}>{count}</div>
}
}
//樣式檔案,不用細看
...(以下相同就略去)
結語
redux其實不難理解,按照我個人理解的加工工廠模式:有一家大公司叫做store,裡面有工廠(reducer),公司(store)只有一家,但這家公司(store)可以有很多工廠(reducer)。要進去工廠加工產品(state),就得帶上相應得鑰匙(Action),不同的鑰匙(Action)對應工廠中上不同的生產線(redecer裡面的switch函式),從而有不同的產出(改變之後的state)