react-native 之 state 和 props 以及 redux 和 react-redux

學術袁發表於2018-07-11

Component 中 state 和 props 的區別;

元件Component中狀態state和屬性props的區別

state props
state是在元件內部定義的一個特殊物件{}
既起到元件內部的一種快取作用,
也具備由於state變化而激發生命週期中渲染的方法
render()被回撥的作用。且使用域僅限於元件內部。
props是元件屬性,連線了外部父元件和元件內部的使用域。
它的改變激發宣告週期方法componentWillReceiveProps(nextProps)
和渲染的方法render()逐次被回撥。連線外部父元件,
可以通過父元件向其傳遞value、function等,連線內部元件,
this.props快取了當前元件的所有props屬性內容
{value, function}=this.props;

程式碼解釋 props

 /**
   * 通過封裝 FlatList 自定義一個列表元件
   */
export default class MyFlatList extends Component {
    constructor(props) {
        super(props);

    }

   ...
    /**
     *象元件FlatList中的
     *ref、ListHeaderComponent、ItemSeparatorComponent、
     *data、keyExtractor、onRefresh、refreshing等都是props屬性;
     *
     * 象這種樣式的定義 this.props.itemSeparator、
     * this.props.onRefresh、this.props.refreshing、this.props.onLoadMore等
     * 屬於自定義元件MyFlatList的屬性,是我們依照FlatList所定義的。
     * 類似象外部開放的介面一樣,就像這裡FlatList使用自己的
     * 屬性ListHeaderComponent、ItemSeparatorComponent、data等一樣來使用;
     * @returns {XML}
     */
    render() {
        return (<View style={{flex: 1, backgroundColor: Colors.bg}}>
            <FlatList
                ref={(flatlist) => this.flatlist = flatlist}
                ListHeaderComponent={this._header} 
                renderItem={this._renderItem}
                ItemSeparatorComponent={this.props.itemSeparator}
                data={this.props.data}
                keyExtractor={this._keyExtractor}
                onRefresh={this.props.onRefresh}
                refreshing={this.props.refreshing}
                onEndReachedThreshold={0.1}
                onEndReached={this.props.onLoadMore}
                initialNumToRender={3}
                getItemLayout={(data, index) => ({
                length: 250, offset: (250 + 10) * index, index
                })}
            />
        </View>);
    }
}

這是使用MyFlatList,來看下它的這個props

export default class FlatlistScreen extends Component {
 ...
    /**
     *這裡MyFlatList的 itemSeparator、data、onRefresh 、refreshing 、onLoadMore 
     *就是FlatList中使用this.props.xxx來定義的。 
     * @returns {XML}
     */
    render() {
        return (<View style={styles.container}>
           ...
            <MyFlatList
                {...this.props}
                itemSeparator={() => this.separator()}
                data={this.state.dataSource}
                onRefresh = {()=>this.onRefresh()}
                refreshing = {this.state.isRefresh}
                onLoadMore = {()=>this.onLoadMore()}
            />
        </View>);
    }
}

除了以上,如果是對資料內容的props屬性的變化,如上面程式碼的data變化,必然會激發MyFlatList元件生命週期方法componentWillReciveProps回撥,這裡我們可以在render方法回撥前,對資料再次進行判斷處理。
**這就是props屬性功能和作用!!**

程式碼解釋 state

export default class FlatlistScreen extends Component {
    constructor(props) {
        super(props);
        this.unmount = false;
        this.state = ({
            dataSource : [],
            isRefresh: true,
        });
    }
    ...
    /**
     * 功能:使用箭頭函式,不使用bind;因為bind函式每呼叫一次就會建立一個新的函式
     */
    onRefresh() {
        this.setState({
            isRefresh: true,
        });

        //功能:製造重新整理效果
        this.interval = setInterval(() => {
            clearInterval(this.interval);
            //功能:製造上拉載入更多的效果
            const data = [];
            for (let i = 0; i < 8; i++) {
                data.push({id: i, title: '親子旅遊日帶娃兒玩' + i + '折起', state: '已過期', date: '2018/06/0' + (i - 8)},)
            }
            this.setState({
                dataSource: data,
                isRefresh: false,
            });

        }, 2000)
    }

   ...

    render() {
        return (<View style={styles.container}>
           ...
            <MyFlatList
                {...this.props}
                itemSeparator={() => this.separator()}
                data={this.state.dataSource}
                onRefresh = {()=>this.onRefresh()}
                refreshing = {this.state.isRefresh}
                onLoadMore = {()=>this.onLoadMore()}
            />
        </View>);
    }
}

看構造方法這裡

this.state = ({
            dataSource : [],
            isRefresh: true,
        });

這就是state的定義方式。通過改變dataSourceisRefresh的值就能激發render再次渲染元件。
比如這裡的onRefresh方法使用了

 this.setState({
      dataSource: data,
      isRefresh: false,
 });

來控制改變state的時機,來控制元件渲染的時機。
**這就是狀態state的使用!!**


react-native 與 Redux 的配合使用;

Redux使用中,一些必知的概念

Redux state action reducers
Redux 是 JavaScript 狀態容器,
提供可預測化的狀態管理。
使用 Redux 的一個益處,
就是它讓 state 的變化過程變的可預知和透明。
以一個物件樹的形式儲存在於一個單一的 store 中,
惟一改變 state 的辦法是觸發 action。
一個描述發生什麼的指示器物件。
action 內必須使用一個字串型別的 type 欄位,
來表示將要執行的動作。
且應該儘量減少在 action 中傳遞的資料;
描述 action 如何改變 state 樹。
reducer 就是一個純函式,接收舊的 state 和 action,返回新的 state。

這裡是我基於Redux實現的已給簡單操作流程原始碼
結合原始碼+圖例,分析下redux的執行操作流程,解讀redux是怎樣原理!

登入操作執行圖

現在定位在登入頁面,處於未登入狀態,需要點選登入操作。
這裡寫圖片描述

圖並結合原始碼分析

登入頁面的部分原始碼,展示了登入頁面的UI
這裡寫圖片描述
從原始碼中50行看到,點選登入按鈕,則呼叫方法login() ,而login()這個方法是從this.props中解構賦值拿出來的,就是原始碼37行所示。
疑問1? this.props中的login()是從哪裡來的,怎麼會在this.props中?!
接著看同一js檔案中的程式碼片段
這裡寫圖片描述
這裡有一個方法非常的重要connect(),她是幹什麼的,這裡可以解答。
簡單的說是,就我們所看到的程式碼從64到74行,connect()裡面有兩個回撥函式,第一個回撥的是state(Login頁面的state),第二回撥的是dispatch(Login頁面用來進行分發登入操作的Action)。通過connect()()實現了 回撥函式中 status、isSuc、user 和 login() 他們與當前的元件(登入頁面)的this.props繫結,也就是他們被注入到了this.props中。
所以,點選登入的執行流程是這樣的:
點選登入按鈕——>呼叫this.props中的login方法——>派發登入操作的action ——>.. reducer處理…導致store的state樹中登入元件的state發生變化 … ——>原始碼中65行執行回撥,同時UI將會執行重新的重新整理、渲染——>渲染過程中需要的內容,從回撥中的state中獲取、賦值。

繼續深入…
登入功能的Action中有兩類內容,一是需要傳遞的使用者資料物件
這裡寫圖片描述
二是派發的Action構建函式
這裡寫圖片描述
圖中可以清晰看到,Action有兩種實現方式,其中最後一種是非同步的Action構建函式,前面則是同步的Action構建函式;通常非同步的Action中會呼叫同步的Action。而同步的Action有一個特點就是有一個約定成俗的欄位叫type,標識著Action的通知型別。
當執行登入操作,使用redux進行登入操作的action進行派發的時候,呼叫了非同步的Action,非同步的通知由在內部執行非同步操作,呼叫同步的action。執行到這裡,action就會被分發到哪裡呢??reducer
reducer也分為兩部分,一是登入頁面內容展示登入元件的state
這裡寫圖片描述
是一個登入操作在執行到reducer時的處理,進行復制、賦值、填充的state模型的資料物件,store樹中的一個物件。
reducer的處理也很簡單,就是根據action的指定處理方式type,進行處理。處理完成之後,返回一個新的state物件。看原始碼是不是這樣的?!
這裡寫圖片描述

執行處理到這裡,想到你已經發起疑問了。疑問2?總覺得,到reducer處理之後與元件容器那兒回撥,直到頁面再次渲染,沒有什麼關聯??!它們到底是怎麼建立關係的呢?
當然是通過Redux,以及處理非同步action需要用到的中介軟體(標準的做法是使用 Redux Thunk 中介軟體)。
看這裡,首先是集合管理action的處理,對reducer的管理
這裡寫圖片描述
然後把對reduer的管理,以及處理結果再交給redux
這裡寫圖片描述
並且也同時,把上面的兩個reduer對應的處理結果state,作為store樹的分支,繫結到了store中,進行統一管理和處理。而且還有一點很關鍵、很重要!也是急需解決的重要疑問——store樹長什麼樣子?? 經過我的實踐和測試對比得出結論, store樹 中的登入元件的state,其資料內容的樣子是和上圖中原始碼第10行 rootReduer 中的資料結構LoginIn是對應的。也就是說他們是互相對映的。即, store與rootReduer 在資料結構上是一致的, store樹 就是通過這樣的rootReduer 中一條條 {key:value} 資料拼湊到一起所組成的物件。如果有點疑問,自己可以動手試一試,並用下圖再加深下印象
這裡寫圖片描述
store樹是用來管理元件state資料的,好,因為我們使用了全域性提供store的方式進行了處理,這裡寫圖片描述
store中的屬性,我們可以在專案中進行全域性使用。
所以,在connect()()程式碼塊中,當state發生變化,產生回撥,我們可以通過state.LoginIn.status、state.LoginIn.isSuc、state.LoginIn.user來指定拿到當前元件對應的state資料。而state.LoginIn的呼叫方式就是從store樹中取出LoginIn對應的state,而LoginIn是哪來的?也許內容有點多,蒙圈了,其實就在原先這裡第11行程式碼。
這裡寫圖片描述
所以,我之前總結的結論,store樹中的登入元件的state,其資料內容的樣子是和上圖中原始碼第10行rootReduer中的資料結構LoginIn是對應的。也就是說他們是互相對映的。 store與rootReduer 在資料結構上是一致的, store樹 就是通過這樣的rootReduer 中一條條 {key:value} 資料拼湊到一起所組成的物件。是正確的!到這裡則回答了上面的疑問2?,同時也解決了Redux的使用以及執行原理。

相關文章