RN:React-Redux

目錄

一. React-Redux是什么
?1. React-Redux的組件拆分規(guī)范
??1.1 UI組件
??1.2 容器組件
?2. React-Redux的API
??2.1 connect()方法
??2.2 Provider組件
二. 為什么要使用React-Redux
三. 怎么使用React-Redux

需要導(dǎo)入的組件:

  • redux
  • react-redux
  • redux-thunk
  • redux-devtools(可選):Redux開(kāi)發(fā)者工具,支持熱加載、action重放、自定義UI等功能。
  • redux-persist(可選):支持store本地持久化。
  • redux-observable(可選):實(shí)現(xiàn)可取消的action。

yarn add redux
yarn add react-redux
yarn add redux-thunk



一. React-Redux是什么


React-Redux,是Redux的作者為了輔助我們使用Redux而編寫(xiě)的一個(gè)庫(kù),沒(méi)有提供什么新功能。

通常情況下,我們寫(xiě)的一個(gè)組件內(nèi)部既負(fù)責(zé)UI渲染,也負(fù)責(zé)管理數(shù)據(jù)和處理業(yè)務(wù)邏輯。而React-Redux秉承一個(gè)理念,就是要把一個(gè)組件拆分成UI組件和容器組件,UI組件只負(fù)責(zé)UI的渲染,容器組件只負(fù)責(zé)管理數(shù)據(jù)和處理業(yè)務(wù)邏輯,而容器組件的這些邏輯又完全是交給Redux來(lái)處理的,所以就可以使得我們編寫(xiě)的組件完全成了一個(gè)UI組件,組件內(nèi)部會(huì)更加清爽。

1. React-Redux的組件拆分規(guī)范

使用React-Redux時(shí)要對(duì)組件進(jìn)行拆分,React-Redux規(guī)定所有的UI組件都由用戶(hù)提供,容器組件則是由React-Redux自動(dòng)生成,生成后我們直接容器組件,而不再使用UI組件。容器組件其實(shí)是包在UI組件外面的,所以它即具備渲染能力,也具備數(shù)據(jù)管理和業(yè)務(wù)處理的能力,只不過(guò)我們看不到而已,我們只能看到我們編寫(xiě)的UI組件,那些東西都在容器組件內(nèi)部。

1.1 UI組件

UI 組件有如下特征。

  • 只負(fù)責(zé)UI的呈現(xiàn),不帶有任何業(yè)務(wù)邏輯。
  • 所有的數(shù)據(jù)都由this.props屬性提供。
  • 內(nèi)部沒(méi)有狀態(tài),即不使用this.state這個(gè)屬性。
  • 不使用任何Redux的API。

下面就是一個(gè)UI組件的例子。

class CustomNavigator extends Component {
    render() {
        return(
            <View>
                <Text>{this.props.headerTitle}<Text/>
            <View/>
        );
    }
}

因?yàn)椴缓袪顟B(tài),所以UI組件又稱(chēng)為“純組件”,即它純函數(shù)一樣,純粹由參數(shù)決定它的值。

1.2 容器組件

容器組件的特征恰恰相反。

  • 負(fù)責(zé)處理業(yè)務(wù)邏輯,不負(fù)責(zé)UI的呈現(xiàn)。
  • 有負(fù)責(zé)管理數(shù)據(jù)。
  • 內(nèi)部帶有狀態(tài)。
  • 使用Redux的API。

2. React-Redux的API

2.1 connect()方法

React-Redux提供了connect()方法,用來(lái)生成一個(gè)UI組件對(duì)應(yīng)的容器組件,即把組件和應(yīng)用State建立映射關(guān)系。如果我們想讓一個(gè)組件響應(yīng)應(yīng)用State的變化(即想讓該組件的state由Redux來(lái)關(guān)系),就把搞成一個(gè)容器組件使用就可以了。

import {connect} from 'react-redux'
const VisibleTodoList = connect()(TodoList);

上面代碼中,TodoList是UI組件,VisibleTodoList就是由React-Redux通過(guò)connect()方法生成的容器組件。

但是,因?yàn)闆](méi)有定義業(yè)務(wù)邏輯,上面這個(gè)容器組件毫無(wú)意義,只是UI組件的一個(gè)單純的包裝層,實(shí)際開(kāi)發(fā)中我們需要為每個(gè)容器組件定義兩種業(yè)務(wù)邏輯,即:

  • 輸入邏輯:UI組件的輸入邏輯類(lèi)props要和應(yīng)用的state建立映射關(guān)系。
  • 輸出邏輯:UI組件的輸出邏輯類(lèi)屬性要和dispatch(addAction)建立映射關(guān)系。

因此,使用connect()方法從UI組件生成容器組件的完整API如下。

import { connect } from 'react-redux'

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList);

上面代碼中,connect()方法接收了兩個(gè)參數(shù),其中mapStateToProps就負(fù)責(zé)輸入邏輯的映射,mapDispatchToProps就負(fù)責(zé)輸出邏輯的映射。

(1)mapStateToProps函數(shù)

mapStateToProps是一個(gè)函數(shù),它用來(lái)建立UI組件的輸入邏輯類(lèi)props和應(yīng)用state的映射關(guān)系,以此決定改組件想要使用應(yīng)用state里的哪一部分?jǐn)?shù)據(jù)。它可以得到應(yīng)用的state一個(gè)參數(shù)。

該函數(shù)執(zhí)行后立即返回一個(gè)JS對(duì)象,該JS對(duì)象里的每一個(gè)key都是該UI組件的某個(gè)props(必須同名),而每一個(gè)key對(duì)應(yīng)的value則來(lái)自于應(yīng)用state里數(shù)據(jù)的計(jì)算。這樣就建立起了映射關(guān)系,請(qǐng)看下面的例子。

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case 'SHOW_ALL':
      return todos;
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed);
    case 'SHOW_ACTIVE':
      return todos.filter(t => !t.completed);
    default:
      throw new Error('Unknown filter: ' + filter);
  }
}

上面代碼中,mapStateToProps函數(shù)接受應(yīng)用的state作為參數(shù),返回一個(gè)JS對(duì)象。這個(gè)JS對(duì)象有一個(gè)todos屬性,是UI組件的同名參數(shù),后面的getVisibleTodos也是一個(gè)函數(shù),用來(lái)從state算出todos的值。

請(qǐng)注意:

mapStateToProps函數(shù)就像我們上一篇所說(shuō)到的store.subscribe(callback)的那個(gè)回調(diào)函數(shù)一樣,它會(huì)監(jiān)聽(tīng)?wèi)?yīng)用State的變化,所以每當(dāng)state更新的時(shí)候,它就會(huì)自動(dòng)執(zhí)行,重新賦值UI組件的props,從而觸發(fā)UI組件的重新渲染。

(2)mapDispatchToProps函數(shù)

mapDispatchToPropsconnect()函數(shù)的第二個(gè)參數(shù),它用來(lái)建立UI組件的輸出邏輯類(lèi)props和應(yīng)用dispatch(addAction)的映射關(guān)系。它可以得到dispatchownProps(容器組件的props對(duì)象)兩個(gè)參數(shù)。

該函數(shù)執(zhí)行后立即返回一個(gè)JS對(duì)象,該JS對(duì)象里的每一個(gè)key都是該UI組件的某個(gè)事件類(lèi)props(必須同名,onPressonClick等),而每一個(gè)key對(duì)應(yīng)的value就是我們要做的dispatch(addAction)。這樣就建立起了映射關(guān)系,請(qǐng)看下面的例子。

const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        onClick: () => {
            dispatch({
                type: 'SET_VISIBILITY_FILTER',
                filter: ownProps.filter
            });
        }
    };
}
2.2 Provider組件

我們使用connect()方法生成容器組件以后,容器組件它自己也沒(méi)有應(yīng)用State啊,那它怎么完成應(yīng)用State和UI組件的映射呢?所以我們還需要做一步,就是讓讓容器組件拿到應(yīng)用State。

React-Redux提供Provider組件,可以讓容器組件拿到應(yīng)用State。做法很簡(jiǎn)單,只需要在應(yīng)用根組件外面包一層Provider組件,那App的所有子組件就都默認(rèn)拿到應(yīng)用State了,所以容器組件作為根容器的子組件,自然也就拿到了。

import {Provider} from 'react-redux';

export default class App extends Component<Props> {
    render() {
        return (
            <Provider store={store}>
                <TestPage/>
            </Provider>
        );
    }
}


二. 為什么要使用React-Redux


首先說(shuō)明,React-Redux也不是必須使用的,它只是Redux的一個(gè)輔助庫(kù),可以幫助我們使得組件的內(nèi)部更簡(jiǎn)潔。

因?yàn)镽eact-Redux秉承一個(gè)理念,就是要把一個(gè)組件拆分成UI組件和容器組件,UI組件只負(fù)責(zé)UI的渲染,容器組件只負(fù)責(zé)管理數(shù)據(jù)和處理業(yè)務(wù)邏輯,而容器組件的這些邏輯又完全是交給Redux來(lái)處理的,所以就可以使得我們編寫(xiě)的組件完全成了一個(gè)UI組件,組件內(nèi)部會(huì)更加清爽。


三. 怎么使用React-Redux


我們依舊以一個(gè)最簡(jiǎn)單的計(jì)數(shù)器為例。

  • 導(dǎo)入相關(guān)的東西
-----------TestPage.js-----------

// 導(dǎo)入Redux相關(guān)的東西
import {createStore, combineReducers} from 'redux';
// 導(dǎo)入React-Redux相關(guān)的東西
import {connect, Provider} from 'react-redux';
  • 先做Redux部分

三個(gè)關(guān)鍵詞:store、reducer、action。

-----------TestPage.js-----------

// 第2步:
// 創(chuàng)建根reducer,合并所有子reducer
// 請(qǐng)注意,這個(gè)根reducer是個(gè)極其重要的東西,因?yàn)檎撬喜⒆觬educer的過(guò)程,決定了應(yīng)用state里到底存放著什么東西,即什么組件的state要被放在它里面
// 什么組件的state想要交由應(yīng)用的state來(lái)統(tǒng)一管理,我們就為該組件編寫(xiě)一個(gè)對(duì)應(yīng)的子reducer來(lái)描述它state具體變化的過(guò)程并返回一個(gè)state,然后把這個(gè)reducer作為value存放在應(yīng)用state里(即合并子reducer的時(shí)候)
// 剛創(chuàng)建根reducer時(shí),我們可能不知道將來(lái)會(huì)有那些組件的state會(huì)被放在應(yīng)用state里來(lái)統(tǒng)一管理,所以可以先空著,什么時(shí)候需要什么時(shí)候往這里添加就可以

// 這里我們假設(shè)計(jì)數(shù)器的state想要交由應(yīng)用的state統(tǒng)一管理,計(jì)數(shù)器可以改變數(shù)字和背景色
// 所以我們編寫(xiě)一個(gè)計(jì)數(shù)器子reducer,專(zhuān)門(mén)用來(lái)完成計(jì)數(shù)器state的變化,并返回新的state
// 計(jì)數(shù)器的初始state
const defaultState = {
    number: 100,
    backgroundColor: 'pink',
}
// 計(jì)數(shù)器的state具體變化的過(guò)程
const counterReducer = (state = defaultState, action) => {
    switch (action.type) {
        case 'ADD_NUMBER':
            return {
                ...state,
                number: state.number + 1,
            };
        case 'CHANGE_BACKGROUND_COLOR':
            return {
                ...state,
                backgroundColor: action.backgroundColor,
            };
        default:
            return state;
    }
}
// 接下來(lái)第3步,就是結(jié)合該組件reducer里action.type的規(guī)定,為該組件創(chuàng)建對(duì)應(yīng)的action,預(yù)備好action,到時(shí)候組件一被觸摸就dispatch一個(gè)action

const rootReducer = combineReducers({// 這個(gè)對(duì)象就是應(yīng)用的state
    // 應(yīng)用state里的屬性名當(dāng)然可以隨便起,但是為了好理解,我們就起做xxxState,為什么這么起呢?
    // 因?yàn)閼?yīng)用state的定義就是,它里面存放著項(xiàng)目中所有想被統(tǒng)一管理state的組件的state,所以我們起做xxxState,將來(lái)使用時(shí)很方便理解,比如state.counterState,就代表從應(yīng)用state里取出counterState
    // 而且它的值就是對(duì)應(yīng)的該組件的那個(gè)子reducer嘛,而reducer函數(shù)又總是返回一個(gè)state,這樣xxxState = 某個(gè)state值,也很好理解
    counterState: counterReducer,
});


// 第1步:
// 創(chuàng)建項(xiàng)目唯一的store,發(fā)現(xiàn)需要一個(gè)reducer
const store = createStore(rootReducer);
// 所以接下來(lái)第2步,我們?nèi)?chuàng)建一個(gè)reducer,回過(guò)頭來(lái)填在這里


// 第3步:
// 為該組件創(chuàng)建對(duì)應(yīng)的action,預(yù)備好action,到時(shí)候組件一被觸摸就dispatch一個(gè)action
// 負(fù)責(zé)描述state要做什么變化以及變化所需的原料,用來(lái)dispatch
const addNumberAction = {type: 'ADD_NUMBER'};
// Action Creator
function changeBackgroundColor(backgroundColor) {
    // 返回一個(gè)action
    return {
        type: 'CHANGE_BACKGROUND_COLOR',
        backgroundColor,
    }
}
// 好,完成了這三步,Redux部分就算完成了,我們可以開(kāi)始React-Redux的部分
  • 再做React-Redux部分

四個(gè)關(guān)鍵詞:UI組件、容器組件、connect方法、Provider組件。

-----------TestPage.js-----------

// 第1步:編寫(xiě)UI組件
// 僅負(fù)責(zé)UI的渲染工作,所需數(shù)據(jù)只能通過(guò)props獲得
// 不能處理業(yè)務(wù)邏輯,不能使用Redux的API
class Counter extends Component {
    render() {
        // 該UI組件有三個(gè)屬性,下面添加映射關(guān)系的時(shí)候會(huì)為該組件添加上這些屬性
        // number:輸入邏輯類(lèi)屬性
        // backgroundColor:輸入邏輯類(lèi)屬性
        // addNumber:輸出邏輯類(lèi)屬性
        // changeBackgroundColor:輸出邏輯類(lèi)屬性
        const {number, backgroundColor, addNumber, changeBackgroundColor} = this.props;

        return (
            <View style={[styles.counterViewStyle, {backgroundColor: backgroundColor}]}>
                <Button
                    title={'變色'}
                    // 第5步:在需要發(fā)出action的地方,通過(guò)調(diào)用props里方法的形式發(fā)出一個(gè)action就可以了
                    // 注意,onPress必須接收一個(gè)函數(shù),而不能接收一個(gè)函數(shù)的調(diào)用
                    // 所以這里不能寫(xiě)成:onPress={changeBackgroundColor('cyan')}
                    // 而應(yīng)該給它賦值一個(gè)函數(shù):
                    onPress={() => changeBackgroundColor('cyan')}
                />

                <Text style={{fontSize: 24}}>{number}</Text>

                <View>
                    <Text
                        style={{color: 'black', fontSize: 20}}
                        // 注意,onPress必須接收一個(gè)函數(shù),而不能接收一個(gè)函數(shù)的調(diào)用
                        // 因?yàn)閍ddNumber函數(shù)不需要參數(shù),所以可以直接賦值
                        onPress={addNumber}
                    >{'+'}</Text>
                </View>
            </View>
        );
    };
}

// 第2步:建立UI組件的props與外界的映射關(guān)系
// 你可能會(huì)問(wèn)“這個(gè)UI組件是什么添加上這些props的呢”?沒(méi)錯(cuò),正是這個(gè)添加映射關(guān)系的過(guò)程為該UI組件添加上了這些props
// 輸入邏輯類(lèi)屬性要和應(yīng)用state里該組件state里的屬性建立映射關(guān)系
function mapStateToProps(state) {
    return {
        // UI組件的屬性:應(yīng)用state.該組件的state.屬性
        // 注意這里要保證,該組件props里的屬性,和該組件state里(這里只是姑且這么稱(chēng)呼,其實(shí)counterState并非真正該組件的state)的屬性同名
        number: state.counterState.number,
        backgroundColor: state.counterState.backgroundColor,
    }
}
// 輸出邏輯類(lèi)屬性要和dispatch(action)建立映射關(guān)系
function mapDispatchToProps(dispatch) {
    return {
        // UI組件的屬性:dispatch(action)
        addNumber: () => dispatch(addNumberAction),
        changeBackgroundColor: (color) => dispatch(changeBackgroundColor(color)),
    }
}

// 第3步:使用connect方法生成UI組件對(duì)應(yīng)的容器組件
// 將來(lái)我們就是使用UI組件的容器組件了,而不是使用UI組件
const CounterContainer = connect(
    mapStateToProps,
    mapDispatchToProps,
)(Counter);

export default class TestPage extends Component {
    render() {
        return (
            // 第4步:在整個(gè)項(xiàng)目的根組件外面包一層Provider組件(注意,這一步只針對(duì)整個(gè)項(xiàng)目的根組件,小組件不需要這一步,完成第3步之后直接使用就可以了),記得一定要把store={store}傳遞進(jìn)去
            <Provider store={store}>
                <View style={styles.container}>
                    <CounterContainer/>
                </View>
            </Provider>
        );
    }
}
  • 樣式部分
-----------TestPage.js-----------

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },

    counterViewStyle: {
        width: 200,
        height: 60,

        flexDirection: 'row',
        alignItems: 'center',
        justifyContent: 'space-around',
    },
});

參考博客:

阮一峰:Redux入門(mén)教程——React-Redux 的用法


?著作權(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)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • Redux 教程 Redux 是 JavaScript 狀態(tài)容器,提供可預(yù)測(cè)化的狀態(tài)管理。 (如果你需要一個(gè) Wo...
    IT老馬閱讀 3,468評(píng)論 1 23
  • React 只是 DOM 的一個(gè)抽象層,并不是 Web 應(yīng)用的完整解決方案,面對(duì)復(fù)雜的應(yīng)用時(shí)需要Redux/Mob...
    老鼠AI大米_Java全棧閱讀 990評(píng)論 0 4
  • 前言 總括: 本文采用react+redux+react-router+less+es6+webpack,以實(shí)現(xiàn)一...
    秦至閱讀 4,363評(píng)論 3 56
  • 前言 React認(rèn)為每個(gè)組件都是一個(gè)有限狀態(tài)機(jī),狀態(tài)與UI是一一對(duì)應(yīng)的。我們只需管理好APP的state就能控制U...
    遠(yuǎn)方的楓葉閱讀 19,562評(píng)論 2 50
  • 前幾天再次讀到木心先生的《從前慢》,不禁又感慨了一番。似乎每次讀到這首小詩(shī),都會(huì)讓人產(chǎn)生不同的感覺(jué),但總能讓人熱淚...
    安人閱讀 308評(píng)論 0 1

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