小白路程之----初學React語法棧之redux與react-redux

曉月君發表於2018-08-19

Redux

1、核心概念

    Redux的主要目的,即集中且可預測的管理資料。集中管理指:將所有需要管理的資料放到一個store中進行管理。可預測指:資料的變化可以預測。為了實現這個功能,Redux將資料的操作全部交給了reducer來管理,而在action中定義資料處理的動作。這樣當你的資料處理動作action傳過來時,store便已經知道將要對資料進行的處理。在Redux中主要有三個內容,分別是store,action以及reducer。

  • 1、store:連線reducer和state,並且為處理資料提供介面,
  • 2、action:描述對資料處理的動作,是store 資料的唯一來源
  • 3、reducer:接收 state 和 action,並返回新的 state 的函式。

三大原則

  • 單一資料來源:整個應用的state被儲存在一棵object tree中,並且object tree只存在於唯一一個store中。

避免資料被修改,以及確定唯一的修改資料的方法就是訪問store。

  • State 是隻讀的:唯一改變state的方法就是觸發action,action是一個用於描述已發生事件的普通物件。

對store外部來說,只能訪問state資料,而不能修改,確保state不被其他的方法修改,而所有的資料修改方法都被reducer集中化處理。

  • 使用純函式來執行修改:為了描述 action 如何改變 state tree,你需要編寫 reducers。

action描述處理資料這件事,對資料的處理是在reducer內完成的,接收先前的state以及action,並返回新的state。

使用Redux的步驟

以counter計數器為例,

1、建立state物件

    對應第一個原則,單一資料來源,在Redux中所有的物件都被儲存在一個單一的物件中,

在store.js檔案中先定義所需的物件,如下:

const state = {
    counter:99,
}
複製程式碼

2、定義action

  • 官網:Action是把資料從應用傳到store的有效載荷。它是store資料的唯一來源。一般來說你會通過store.dispatch()將action傳到store。
  • 簡單來說就是要求store處理資料的訊號,對於不同的訊號reducer對資料進行不同的處理。

實現加減操作,定義如下:

//實現加的increment操作:
const increment ={
    type = 'INCREMENT'
}
// 減操作的action
const decrement = {
    type: 'DECREMENT'
}
複製程式碼
  • action建立函式,就是建立action的方法,只是簡單的返回一個action。 如下,定義的increment()函式,返回的就是簡單的一個action,方法內的text可以定義每次修改的數值的多少。
//返回加操作的action
function increment(text) {
    return {
        type: 'INCREMENT',
        text
  }
}
// 減操作decrement的action
function decrement() {
    return {
        type: 'DECREMENT',
    }
}
複製程式碼

3、定義reducer

  • reducer的職責就是根據不同的action來處理資料。根據第三條規則,使用純函式來執行修改。

對於純函式必須遵守以下約束。

  • 不得改寫傳入的引數
  • 不能呼叫系統I/O的API(如DOM操作、http請求)
  • 不能呼叫不純的方法(如Date.now()或者Math.random())。

對於計數器案例,定義的reducer如下,

function reducer(state = initState, action) {
    switch (action.type) {
        case 'INCREMENT':
            return { count: state.count + action.text }
        case 'DECRMENT':
            return { count: state.count - 1 }
        default:
            return state
    }
}
複製程式碼

需要注意

  • 不能修改state,
  • 在default的情況下返回舊的state

當資料量多時需要對資料進行拆分處理,可以定義多個reducer,然後進行合併。

function reducer(state = state,action){
    return{
        //狀態:reducer(state.狀態,action),
        todos:todos(state.todos,action),
        visibility:visibility(state.todos,action)
    }
}
複製程式碼

4、建立store,並使用

store提供了三個API介面,分別是

  • getState:用於獲取狀態
  • dispatch:用於派發action
  • subscribe:當狀態發生變化時執行的回撥函式
//可在建立store時,也可在定義reducer時,初始化資料
const store = Redux.createStore(reducer,state)
複製程式碼

使用回撥函式,以及執行函式:

store.subscribe(() => {
    console.log(`現在的狀態為:${store.getState().count}`)
})
store.dispatch(increment(1))
store.dispatch(increment(3))
store.dispatch(decrment())
複製程式碼

小白路程之----初學React語法棧之redux與react-redux

react-redux

  • react-redux是連結react與redux的專用庫。在使用時需要格外安裝。學習react-redux最主要的就是學習其中的三個一,一種開發思想,一個connect方法,以及一個provide元件。
  • 1、一種開發思想:元件分離思想

react-redux提出了“智慧”元件和“笨拙”元件相分離的開發思想,

  • “智慧”元件,即容器元件,主要特點有:(1)負責管理資料和業務邏輯,不負責UI的呈現,(2)帶有內部狀態,(3)使用redux的API
  • “笨拙”元件,又稱展示元件,特點:(1)只負責UI的呈現, 不帶有任何的業務邏輯,(2)沒有狀態,(3)所有資料由引數(this.props)提供,(4)不適用redux的API。
  • 2、一個connect方法:將“笨拙”元件,變成“智慧”元件

react-redux提供了一個connect方法,用於從“笨拙”元件生成容器元件。 其中狀態,是指state資料,邏輯,是指修改state的方法.

  • 3、一個provider元件:為後代元件提供store物件。 在使用connect方法時,用到了store物件的dispatch方法,那麼此時我們就需要使用provide元件,將store傳遞過去了。

還是以counter計數器為例,但是這次使用腳手架create-react-app的方式:

//安裝腳手架create-react-app:
npm i create-react-app -g
//建立專案
create-react-app counter
//安裝react-redux
npm i redux react-redux
//然後啟動專案
npm start
複製程式碼

counter案例:

1、先定義元件

//定義Show元件
export default class Show extends React.Component{
    render() {
      return (<h2>counter:{this.props.counter}</h2>)
    }
}
//定義Add元件,Sub元件與Add元件一樣
export default class Add extends Component{
    render() {
      return (<button>+</button>)
    }
}
//定義counter元件,顯示內容
class Counter extends Component {
    render() {
        return (
            <div>
                <Show/>
                <p><Add/>&nbsp;<Sub/>&nbsp;</p>
            </div>
        )
    }
}
複製程式碼

2、建立store

  • 1、定義store以及action creators
//定義store
const state = {counter:6,}
//定義increment的action,定義decrement的action與其類似。
export function increment() {
    return {type: 'INCREMENT'}
}
複製程式碼
  • 2、定義reducer以及合併
export default function counter(state = 0, action) {
    switch (action.type) {
        case 'INCREMENT':
            return state + 1;
        case 'DECREMENT':
            return state - 1
        default:
            return state
    }
}
//合併reducer
const reducer = combineReducers({
    counter// 當名字相同時,可以省略
})
複製程式碼
  • 3、建立store
//可以在建立store時初始化資料
const store = createStore(reducer,state,)
//在使用框架,在不同的資料夾下寫模組時,要記得匯出以及匯入你需要的資料。
export default store
複製程式碼
  • 4、使用connect轉化元件 在使用connect時,需要先匯入必要的方法
import { connect } from 'react-redux'
//匯入actions內的所有方法,並命名為action
import * as actions from '../actions/films'
import { bindActionCreators } from 'redux';
複製程式碼

在匯入必要的方法後,才能使用。

// 定義mapStarteTopProps,建立從state到props物件的對映,接受state作為引數
function mapStarteTopProps(state) {
    return {counter: state.counter}}
// 定義mapDispatchToProps,用於建立笨拙元件的引數到store.dispatch方法的對映,以dispatch為引數
function mapDispatchToProps(dispatch) {
    return bindActionCreators(action, dispatch)}
// 將Counter變成容器元件並匯出,需要在頂層使用 provide元件,引入store,這樣在子元件內就可以使用store了。
export default connect(mapStarteTopProps, mapDispatchToProps)(Counter)
複製程式碼
  • 5、使用provider元件,傳遞store
//需要匯入必要的包
import { Provider } from 'react-redux';
import store from './store'
import Counter from './containers/Counter';
//再使用provider方法
ReactDOM.render(
    // 將store整合到react應用的頂層props裡面,然後使用provider元件傳遞。這樣各個子元件就能訪問到頂層的store物件了
    <Provider store={store}><Counter /></Provider>, 
    document.getElementById('root'));
registerServiceWorker();
複製程式碼
  • 6、在各個子元件使用方法和屬性

部分修改如下:

//在Add元件內使用increment方法
export default class Add extends Component{
    render() {
      return (<button onClick={()=>this.props.increment()}>+</button>)
    }
}
//修改Counter元件,顯示資料,以及給子元件傳遞方法
class Counter extends Component {
    render() {
        return (
            <div>
                <Show counter={this.props.counter} />
                <p><Add increment={this.props.increment} />&nbsp;
                <Sub decrement={this.props.decrement} />&nbsp;
                </p>
            </div>
        )
    }
}
複製程式碼
  • 測試如下:

小白路程之----初學React語法棧之redux與react-redux

在react-redux中實現非同步操作

  • 定義非同步的action creator函式

在非同步action中,返回一個函式,在該函式中去呼叫同步的action即可。 這個函式,會自動的獲取dispatch和getState。

//定義非同步操作,在點選後兩秒實現加操作
export function incrementAsync() {
    return function (dispatch, getState) {
        setTimeout(function () {
            dispatch(increment());
        }, 2000)
    }
}
複製程式碼
  • 先前已經定義好了框架,現在只需在定義好的框架內,定義方法以及呼叫方法即可。
//在Counter內傳遞方法
<AsyncAdd incrementAsync={this.props.incrementAsync}
//在Async元件內使用方法
export default class AsyncAdd extends Component {
    render() {
        return (<button onClick={() => this.props.incrementAsync()}>+async</button>)
    }
}
複製程式碼
  • redux預設是不支援非同步action的,因此我們需要使用中介軟體來強化store,讓Reducer 在非同步操作結束後自動執行。
//安裝實現非同步操作的中介軟體redux-thunk
npm i redux-thunk -S
複製程式碼
  • 使用中介軟體:
//先從redux-thunk內匯入需要的方法
import thunkMiddleware from "redux-thunk"
// 在createStore方法上使用使用thunkMiddleware方法強化store
const  createStoreWithMiddleware = applyMiddleware(
    thunkMiddleware 
)(createStore)
const store = createStoreWithMiddleware(reducer,state,)
複製程式碼

這樣就可以在redux內實現非同步操作了。

相關文章