1. 程式人生 > 其它 >redux及react-redux基礎使用

redux及react-redux基礎使用

redux應該是react開發中最為大家熟知的一個進行統一狀態管理的庫了。不過值得一提的是,redux並非必須配合react去使用,它是可以單獨使用去進行狀態管理的,或者配合vue等框架去使用。

redux應該是react開發中最為大家熟知的一個進行統一狀態管理的庫了。不過值得一提的是,redux並非必須配合react去使用,它是可以單獨使用去進行狀態管理的,或者配合vue等框架去使用。

基本的redux

整個流程可以理解為在元件中通過dispatch一個攜帶要修改的資料的型別和引數的action交給store,然後store交給reducer去執行。reducer將資料處理後再更新store中的資料,元件就可以通過getState()獲取共享的狀態值。

包括一下幾個主要部分的定義:

1、reducer

通常可以被命名為xxx_reducer.js。檔案export一個方法,通過該方法對state進行修改,方法接收兩個引數:之前的state值和action物件。action物件包含兩部分,一是操作型別,二是操作攜帶的引數。通常會給第一個值指定一個預設值,作為該state的初始值。

const countRecucer = (preState = 0, action)=>{
    const { type, data } = action;
    switch(type){
        case "increment":
            return preState + data;
        default:
            return preState;
    }
}

這裡要注意,reducer要是一個純函式。所謂純函式是這樣的一類函式:只要是同樣的輸入,必定會得到同樣的輸出,有以下幾個特徵:

  1. 不得改寫引數。
  2. 不會產生副作用,如網路請求、輸入和輸出裝置等。
  3. 不能呼叫Date.now()或者Math.random()等不純的方法。

這裡如果違背reducer是一個純函式的要求,輸出的物件仍是輸入的preState,比如往陣列中push值,往物件中增加新key等等,redux是無法更新檢視的。

另外值得一提的是,在元件中呼叫store.getState()獲取值的時候,redux會自動幫我們調reducer去做初始化,會發送一個如下的action物件進行初始化。

{type: '@@redux/INIT0.g.h.f.l'}

2、store.js

store是整個redux的核心,元件中正是使用這個檔案export出去的store物件。這裡需要藉助redux提供的createStore()

方法去建立該物件。

import { createStore } from 'redux'
import countReducer from './count_reducer'

export default createStore(countReducer)

3、在元件中使用

剩下的就是在元件中使用了,通過以下程式碼就能在元件中dispatch一個action,從而修改共享的狀態值。

store.dispatch({type:'xxx',data:xxx})

元件中也可以再通過store.getState()獲取存在redux中的共享狀態值。

補充

以上就是一個最簡單的redux使用的流程了,但也需要再注意以下幾個問題:

狀態修改,元件更新

僅僅修改state的值是不能再頁面上體現出來的。redux只是幫我們做了資料的存取修改,並沒有幫我們呼叫render。

所以需要我們手動觸發render,可以在鉤子函式中做監聽,如下:

componentDidMount(){
    store.subscribe(()=>{
        this.setState();
    })
}

或者在全域性的index.js中去重新整理所有元件:

store.subscribe(()=>{
    ReactDOM.render(<App />,document.getElementById('root'))
})

ActionCreator

可以通過一個方法,返回一個物件的方式去建立action,而不是直接寫action物件,這樣也方便接下來實現非同步action。

export const createIncrementAction = (data)=>{
    return {type:'increment', data}
}

非同步action

通過方法建立action,不返回一個物件而是返回一個方法,就能實現非同步action,在action中執行一些非同步操作。

export const createIncrementAsyncAction = (data, time)=>{
    return (dispatch)=>{
        setTimeout(()=>{
            dispatch(createIncrementAction(data))
        }, time)
    }
}

注意,如果使用方法作為返回值的方式去建立action,需要在store.js中用一箇中間件配合:

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import countReducer from './count_reducer'

export default createStore(countReducer, applyMiddleware(thunk))

以上這兩種借用方法建立action的方式,可以在元件中這樣使用:

store.dispatch(createIncrementAction(data))
store.dispatch(createIncrementAsyncAction(data,1000))

常量 constant.js

因為在reducer和action中都要寫同一個action的type,故可以單獨提一個檔案,維護常量供二者使用,防止修改手誤,一般放在constant.js中。

export const INCREMENT = Symbol();

多組狀態

我們可能會遇到不同模組的元件共用不同的物件,所以就有了多組元件分別儲存自己的state到redux中這種需求。

首選需要明確的是,全域性只有一個redux,一個store,故所有的內容都應白存在這一個裡。之前只有一個元件,故只有一個reducer,所以建立store第一個引數就直接是這個reducer。但是如果有多個reducer,store.js就需要如下的操作了:

import { createStore, applyMiddleware, combineReducer } from 'redux'
import thunk from 'redux-thunk'

import countReducer from './count_reducer'
import personReducer from './person_reducer'

const allReducer = combineReducer({
    count: count_reducer,
    person: person_reducer
})
export default createStore(allReducer, applyMiddleware(thunk))

注意,這個時候儲存在redux中的store就變成allReducer中這個物件,也就是:

{
    count: countReducer中返回的值,
    person: personReducer中返回的值
}

在元件中取值,store.getState()取到的也是這個值。

react-redux

這是一個react提供的元件,配合redux實現狀態的管理,能夠讓我們無需手動觸發render(),它會在state改變之後,自動幫我們更新檢視。

react-redux需要我們罷元件分成容器元件和UI元件,它們是父子關係,容器元件才真正跟redux打交道,裡面可以隨意使用redux的api,但UI元件不能夠使用任何redux的api。

容器元件會傳給UI元件:1、redux中所儲存的狀態,2、用於操作狀態的方法。以上兩者都是通過props傳遞的。原理圖如下:

容器元件

最關鍵的部分就是容器元件的建立,容器元件的建立使用的是react-redux提供的connect()()這個方法。這是一個高階方法,第二個括號中的引數就是UI元件,假設我們有UI元件<Count />,則使用方式是:

import { connect } from 'react-redux'

export default connect()(Count)

而第一個括號中,傳遞的是兩個方法。分別是將state對映成props的方法和將dispatch對映成props的方法。通過這兩個方法,UI元件就能使用redux中存的狀態和對狀態操作的方法了。

import {connect} from 'react-redux'
import {crateIncrementAction, crateDecrementAction } from '../redux/count_action'

const mapStateToProps = (state)=>{
    return {
        count: state
    }
}
const mapDispatchToProps = (dispatch)=>{
    return {
        increment: (value)=>dispatch(createIncrementAction(value)),
        decrement: (value)=>dispatch(createDecrementAction(value))
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Count)

如上,建立好容器元件後,在UI元件中只需要通過使用this.props就可以使用count這個狀態和increment、decrement這兩個操作狀態的方法去跟redux進行互動了。

而容器元件的使用,可以在使用的地方直接呼叫,同時傳入state就行,假設我們有容器元件Count,則:

import store from '../redux/store'

<Count store={store} />

優化mapDispatchToProps

值得一提的是,mapDispatchToProps這個方法是可以進行優化的。它可以不使用一個方法,轉而使用一個物件,有一個對映到生成action的物件就行了,以上提到的mapDispatchToProps可以優化成如下:

import {crateIncrementAction, crateDecrementAction } from '../redux/count_action'

const mapDispatchToProps = {
    increment: createIncrementAction,
    decrement: createDecrementAction
}

Provider

在使用容器元件時,需要傳入store。實際上如果每個容器元件都需要傳一遍就會比較麻煩,我們可以使用Provider進行優化:

import Count from './container/Count'
import { Provider } from 'react-redux'

export default class App extends Component {
  render() {
    return (
      <div id="app">
        <Provider store={store}>
          <Count />
        </ Provider>
      </div>
    );
  }
}

這樣,在Provider中的每一個容器元件,都不需要再傳入store了。

Redux DevTools

可以使用chrome的一個外掛來幫助我們在開發中監控redux中的狀態。

  1. 首先需要安裝chrome的外掛Redux DevTools
  2. 在專案中npm install redux-devtools-extension
  3. 在store.js中做如下修改
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import allReducer from './reducers'
import { composeWithDevTools } from 'redux-devtools-extension'

export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)))