ReactNative系列(七):Redux和React-Redux詳解

ReactNative.jpg

ReactNative整理:《ReactNative系列》

內(nèi)容目錄

一、簡(jiǎn)介
二、Redux使用場(chǎng)景
三、Redux組成部分
???3.1 Action
???3.2 Reducer
???3.3 Store
四、React-Redux介紹
???4.1 React-Redux 組件分離思想
???4.2 connect 方法
???4.3 Provider
五、簡(jiǎn)單示例


一、簡(jiǎn)介

  • 什么是Redux

?Redux是 JavaScript 狀態(tài)容器,提供可預(yù)測(cè)化的狀態(tài)管理。對(duì)可預(yù)測(cè)化的個(gè)人理解:每個(gè)狀態(tài)都是由action觸發(fā)舊state更新生成新的state,生成結(jié)果可控,并且來源都只有一個(gè) -- store,所以是可預(yù)測(cè)的。

  • 為什么要用Redux

?通俗來講:由于多個(gè)路由頁(yè)面或組件之間消息傳遞或相互狀態(tài)控制太過繁瑣,需要中間頁(yè)面?zhèn)鬟f消息,通過propsstate流經(jīng)多個(gè)組件來實(shí)現(xiàn)。這樣會(huì)使通信出錯(cuò)的概率大增,頁(yè)面越復(fù)雜交互越多,這種情況就越嚴(yán)重,所以需要一個(gè)可以統(tǒng)籌全局?jǐn)?shù)據(jù)狀態(tài)的工具,用作接收與分發(fā),這時(shí)就需要用到Redux。
從網(wǎng)上找到張圖片,可以形象的表示Redux的功能。

Redux作用.png

  • Redux的三大原則

Redux的使用需要遵循三大原則,這里先簡(jiǎn)單介紹,下文會(huì)詳細(xì)解釋。

1、單一數(shù)據(jù)源
?redux的數(shù)據(jù)都存放在store中,保證數(shù)據(jù)來源的唯一性,同時(shí)更便于應(yīng)用的數(shù)據(jù)交互;
2、state是只讀狀態(tài)
?action可以去取state的值,但是不能直接對(duì)state進(jìn)行修改;
3、使用純函數(shù)修改state
?利用reducer改變?cè)械膕tate狀態(tài),并生成一個(gè)新的state返回

Redux文檔:Redux文檔地址

二、Redux使用場(chǎng)景

首先,我們需要明確:Redux是很有用的框架,但并不是非用不可。

什么場(chǎng)景下可以不用

  • 用戶使用方式簡(jiǎn)單,沒有太過復(fù)雜的交互(這個(gè)就不用多解釋了)
  • 數(shù)據(jù)來源單一:比方說,數(shù)據(jù)都是由父組件傳遞過來,只在當(dāng)前組件中使用;或者是從服務(wù)端拉取,同樣只在當(dāng)前組件中使用。
  • 不需要大量與服務(wù)器交互

上面情況都可以不用Redux,那么與之相反的情況下就需要用到Redux了。
什么情況下使用

  • 用戶使用方式復(fù)雜 -- 交互頻繁且業(yè)務(wù)邏輯復(fù)雜
  • 多個(gè)用戶之間可以協(xié)作
  • 與服務(wù)器大量交互
  • View需要從多個(gè)來源獲取數(shù)據(jù)

在多交互多數(shù)據(jù)源邏輯復(fù)雜的情況下,才需要用到Redux;具體情況要根據(jù)自身需求確定。

三、Redux組成部分

Redux是由三個(gè)重要組成部分協(xié)同工作的。包括:action、reducerstore

3.1 Action
  • Action就是一個(gè)普通Object。其中type屬性是必須包含的,用來區(qū)分action或表示action的名字。其余屬性可以自行配置。

  • Action是View發(fā)出的通知。View與用戶發(fā)生交互會(huì)引起storestate的改變,但是用戶不能直接操作state,只能看到或接觸到View層,所以需要在操作View的時(shí)候通知state發(fā)生改變,此時(shí)就要用到action。我們可以把action當(dāng)成是狀態(tài)存儲(chǔ)器store中狀態(tài)更新的觸發(fā)器。

const kAction_Add = {
  type: 'Action_Add',
  info: 'Todo_add'
}

如上:Action的名稱為Action_Add,跟隨該Action攜帶的信息為info,值為Todo_add,將整個(gè)對(duì)象定義為常量,命名為kAction_Add

  • View會(huì)發(fā)送很多種消息,因此就會(huì)有很多種Action,或者是同一種Action,但是攜帶的信息不同。我們不可能都手寫成常量,所以就會(huì)想到用函數(shù)的方式來生成Action,這就是Action Creator。
const kAction_Update = 'kAction_Update';

export function update(info) {
  return {
    type: kAction_Update,
    info
  }
}

const updateAction = update('information');

上面例子中:定義常量kAction_Update代表Action的名稱,應(yīng)用中Action種類比較多的時(shí)候可以將常量統(tǒng)一用一個(gè)常量JS文件管理,并導(dǎo)出方便引用。聲明方法update(info),可能沒接觸過React或JS的同學(xué)比較困惑對(duì)象中info是什么意思,這里代表的含義是info: info,前一個(gè)info是key值,后一個(gè)info是方法傳進(jìn)來的參數(shù)。

  • 明白了Action的創(chuàng)建之后,就需要了解Action的發(fā)送。store.dispatch()是View發(fā)出Action的唯一方式。
import { createStore } from 'redux';
// 這里的 func 是指函數(shù) -- reducer
const store = createStore(func);
store.dispatch({ type: 'kAction_Update', info: 'information'})

結(jié)合上面的代碼可以寫成:

store.dispatch(update('information'));
3.2 Reducer
  • View發(fā)送actionstore,引起了state的改變,使得View發(fā)生改變。那么action是如何鏈接到store中狀態(tài)的,又是如何引起狀態(tài)改變的?這就用到了reducer。我們可以將reducer看作是actionstore之間的紐帶。

  • Reducer是一個(gè)函數(shù),它需要兩個(gè)參數(shù):一個(gè)是默認(rèn)的state;另一個(gè)是action。最終經(jīng)過reducer處理過后會(huì)返回一個(gè)新的state

import { combineReducers } from 'redux';
import * as Types from '../constants/type';

const initState = {
  info: ''
};

function handleAction(state = initState, action) {
  switch (action.type) {
    case Types.kAction_Update:
      return {
        ...state,
        info: 'new' + action.info
      };
    case Types.kAction_Add:
      return {
        ...state,
        todo: 'new' + action.todo
      };
    default:
      return state;
  }
}

// handleAction 是一個(gè) Reducer 函數(shù),可以有多個(gè)類似handleAction這樣的 Reducer 函數(shù),用 combineReducers 整合成一個(gè)大的 Reducer
export default combineReducers({
  handleAction
});
  • Redux提供combineReducers方法,用來將Reducer拆分。可以定義多個(gè)Reducer函數(shù),最終用該方法將它們組合成一個(gè)大的Reducer。
  • 由于Reducer是純函數(shù),函數(shù)的返回結(jié)果是由actionstate決定的。正是Reducer這個(gè)特性決定了函數(shù)中不能修改state,只能返回一個(gè)全新對(duì)象。

Q:什么是純函數(shù)?
A:純函數(shù)是函數(shù)式編程的概念,需要滿足以下約束條件:
??1. 不得改寫參數(shù)
??2. 不能調(diào)用系統(tǒng) I/O 的 API
??3. 不能調(diào)用Data.now()或Math.random()等不純的方法,因?yàn)槊看蔚玫降慕Y(jié)果不一樣

3.3 Store
  • Store是存儲(chǔ)數(shù)據(jù)的地方,一個(gè)應(yīng)用只能有一個(gè)store。我們可以把它看成是浮在所有頁(yè)面之上的一個(gè)數(shù)據(jù)存儲(chǔ)容器,不同的組件和頁(yè)面都可以從這個(gè)容器中拿到需要的數(shù)據(jù)。
  • Redux提供createStore函數(shù)來生成store。
import { createStore } from 'redux';
import Reducers from '../reducers/commonReducer';

const commonStore = createStore(Reducers);

會(huì)接收一個(gè)Reducer作為參數(shù),返回生成的Store對(duì)象。

Store有以下職責(zé)

  1. store.getState() 方法獲取state;
  2. store.dispatch(action) 方法更新state;
  3. store.subscribe(listener) 注冊(cè)狀態(tài)監(jiān)聽器;
  4. store.subscribe(listener) 返回的函數(shù)注銷監(jiān)聽器。
  • Store允許使用 store.subscribe 方法設(shè)置監(jiān)聽函數(shù),一旦state發(fā)生變化,就自動(dòng)執(zhí)行這個(gè)函數(shù)。
import { createStore } from 'redux';
const store = createStore(reducer);

store.subscribe(listener);

store.subscribe方法返回一個(gè)函數(shù),調(diào)用這個(gè)函數(shù)就可以解除監(jiān)聽。

let unsubscribe = store.subscribe(() =>
  console.log(store.getState())
);

unsubscribe();

四、React-Redux介紹

ReactRedux原本是兩個(gè)相互獨(dú)立的框架,為了使用起來更高效更靈活,Redux官方針對(duì)React封裝了一套數(shù)據(jù)狀態(tài)管理框架 -- React-Redux。

4.1 React-Redux 組件分離思想

?React-Redux涉及到容器組件和展示組件分離的思想,在React-Redux中將組件分為 UI組件容器組件

  • UI組件

什么是UI組件?可以理解為只負(fù)責(zé)頁(yè)面顯示。UI組件有如下特征:

1、只負(fù)責(zé)UI呈現(xiàn),不包含任何邏輯處理;
2、沒有狀態(tài)(即不使用this.state);
3、所有數(shù)據(jù)都由props提供;
4、不使用Redux的API。

  • 容器組件

容器組件包含以下特征:

1、負(fù)責(zé)數(shù)據(jù)處理和業(yè)務(wù)邏輯;
2、包含內(nèi)部狀態(tài);
3、會(huì)使用Redux的API。

可能很多人比較困惑:我們自己寫的組件里面也有很多業(yè)務(wù)邏輯和數(shù)據(jù)處理,難道要都拆開嗎?其實(shí)不用,React-Redux規(guī)定,UI組件由用戶提供,容器組件由 React-Redux 自動(dòng)生成。
我們可以理解為用戶自己創(chuàng)建的組件都是UI組件,和React-Redux生成的容器組件是互不干擾的,它能提供組件,使整個(gè)應(yīng)用可以訪問到Redux Store。

4.2 connect 方法

React-Redux提供connect方法,用于從UI組件生成容器組件;該方法不會(huì)改變用戶創(chuàng)建的組件,而是返回新的已與 Redux store 連接之后生成的 connect 過的組件。

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

connect可以有四個(gè)參數(shù),但是常用的只有前兩個(gè)mapStateToProps,mapDispatchToProps。

  • mapStateToProps(state, [ownProps]): stateProps

mapStateToProps:從字面意思也可以理解,該函數(shù)的作用就是建立從(外部的)state到UI組件的props的映射關(guān)系。定義該參數(shù),組件就會(huì)監(jiān)聽 Redux Store 的數(shù)據(jù)變化,只要 Store 中對(duì)應(yīng)數(shù)據(jù)發(fā)生變化,就會(huì)調(diào)用該函數(shù),函數(shù)中的對(duì)象會(huì)與組件的props合并。如果指定了該回調(diào)函數(shù)中的第二個(gè)參數(shù) ownProps,則該參數(shù)的值為傳遞到組件的 props,而且只要組件接收到新的 props(例如:父組件中參數(shù)修改導(dǎo)致當(dāng)前組件props變動(dòng)),mapStateToProps 也會(huì)被調(diào)用。

const mapStateToProps = (state, ownProps) => {
  return {
    count: state.counter.count,
    imageUrl: state.load.imageUrl === ownProps.imageUrl
  }
}
  • mapDispatchToProps(dispatch, [ownProps]): dispatchProps

mapDispatchToProps:是connect的第二個(gè)參數(shù),作用是用來建立UI組件和store.dispatch之間的映射。如果傳遞的參數(shù)是一個(gè)對(duì)象,那么每個(gè)定義在該對(duì)象中的函數(shù)都會(huì)被當(dāng)作Action Creator;
函數(shù)寫法舉例:

/**
 * 各個(gè)Action可以分開表示
 * 需要用到 redux 中的方法 bindActionCreators 將 dipstch 與自定義action綁定到一起
 * reduceCount 和 increaseCount 為action對(duì)應(yīng)的屬性值,使用時(shí)
 * 只需要調(diào)用 this.props.reduceCount 或 this.props.increaseCount 即可
 */
const mapDispatchToProps = dispatch => ({
  reduceCount: bindActionCreators(countReduceAction, dispatch),
  increaseCount: bindActionCreators(countIncreaseAction, dispatch)
})
/**
 * action統(tǒng)一用 dispatch 發(fā)送
 * 使用時(shí),調(diào)用 this.props.dispatch(自定義action creator)
 */
const mapDispatchToProps = dispatch => ({
  dispatch
})
/**
 * 將所有action組合成一個(gè)大的 action creator 對(duì)象
 * actions 為組合后的對(duì)象屬性key
 * 使用時(shí),調(diào)用 this.props.actions.countReduceAction() 或 this.props.actions.countIncreaseAction()
 */
const mapDispatchToProps = dispatch => ({
  actions: bindActionCreators(Object.assign({}, {
    countReduceAction,
    countIncreaseAction
  }, ), dispatch)
})
  • mergeProps(stateProps, dispatchProps, ownProps): props

mergeProps函數(shù)會(huì)將前兩個(gè)參數(shù)mapStateToPropsmapDispatchToProps的執(zhí)行結(jié)果和組件自身的props傳入到該回調(diào)函數(shù)中,有特殊數(shù)據(jù)需要處理的可以在該方法里處理,函數(shù)的返回結(jié)果將會(huì)作為props傳遞到包裝過的組件中,一般情況下可以不用傳。

  • options

如果指定該參數(shù),可以定制connector的行為?;静挥茫肓私獾目梢匀ゲ榭?code>redux文檔。

4.3 Provider

<Provider store>可以使組件層級(jí)中的所有connect方法,都能夠鏈接到Redux Store。一般情況下需要將根組件嵌套在<Provider>中才能使用connect方法。

render() {
    return (
      <Provider store={this.state.store}>
        <SwitchConstruct
          onNavigationStateChange={this.handleNavigationChange}
        />
      </Provider>
    );
  }

五、簡(jiǎn)單示例

1、首先創(chuàng)建Action Creators,將action的標(biāo)識(shí)統(tǒng)一用一個(gè)文件管理

import * as Types from '../../constant/types';


export function countReduceAction(count) {
  return {
    type: Types.kCount_Reduce,
    count
  }
}

export function countIncreaseAction(count) {
  return {
    type: Types.kCount_Increase,
    count
  }
}

2、創(chuàng)建Reducer,注意需要有初始化數(shù)據(jù);多個(gè)Reducer的情況下可以用combineReducers方法合并成一個(gè)大的Reducers。

/**
 * 計(jì)數(shù)reducer
 */
import * as Types from '../../constant/types';

const countInitReduce = {
  count: -1
}

export default function handleCount(state = countInitReduce, action) {
  switch (action.type) {
    case Types.kCount_Reduce:
      return {
        ...state,
        count: action.count - 1
      };
    case Types.kCount_Increase:
      return {
        ...state,
        count: action.count + 1
      };
    default:
      return state;
  }
}
import { combineReducers } from 'redux';
import handleCount from './countReducer';
import handleLoadImage from './loadImageReducer';

/**
 * reducers組合返回
 */
const reducers = combineReducers({
  countStore:  handleCount,
  loadImageStore: handleLoadImage
});

export default reducers;

3、生成Store,存儲(chǔ)全局狀態(tài)數(shù)據(jù)

import reducers from '../reducers/baseReducer';
import { createStore } from 'redux';

export function configStore() {
  const store = createStore(reducers);
  return {
    store
  };
}

4、用Provider包裹根組件,并將其connect到組件中,這樣在組件中就可以調(diào)用action了。

const mapStateToProps = (state) => ({
    count: state.countStore.count,
    imageUrl: state.loadImageStore.imageUrl
});

const mapDispatchToProps = dispatch => ({
  dispatch
})

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

?以上就是ReduxReact-Redux的理解和簡(jiǎn)單使用,有問題的地方歡迎指出,喜歡的話可以點(diǎn)贊關(guān)注。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容