React通過redux快取列表資料以及滑動位置,回退時恢復頁面狀態

RaoMeng發表於2019-02-22

在使用ReactReact-router實現單頁面應用時,會有這樣一個場景:從列表頁面點選某項條目進入詳情頁,然後回退至列表頁面時,列表頁面會重新重新整理,不僅資料重新獲取了,滾動條也回到了頂部。使用者要繼續檢視剩餘資料的話,需要重新滑動到之前點選的那個條目,如果列表做了分頁的話就更麻煩了,這對於使用者體驗來說是非常不好的。

所以我們希望能做到,從二級頁面回退至列表頁面時,列表頁面能保留之前的狀態(資料和滾動條位置)。

那麼怎麼實現呢?下面分享一下React通過redux來快取列表資料以及滑動位置,以達到保留列表頁面狀態的方法。

關於redux以及react-redux的使用,這裡就不做講解了,可以參考我之前寫的 React-redux的原理以及使用 。當然網路上有很多講解得更清晰的文章,讀者可以自行搜尋。

下面直接進入正題,介紹實現需求的步驟吧

1、安裝redux以及react-redux

cnpm install redux react-redux -dev --save

2、編寫操作列表頁面相關資料的action

/**
 * Created by RaoMeng on 2018/12/10
 * Desc: 列表資料快取
 */

import {CLEAR_LIST_STATE, LIST_STATE} from "../constants/actionTypes";
import store from '../store/store'

/**
 * 儲存列表狀態
 * @param data
 * @returns {Function}
 */
export const saveListState = (data) => {
    return () => {
        store.dispatch({
            type: LIST_STATE,
            ...data
        })
    }
}

/**
 * 清除列表狀態
 * @returns {Function}
 */
export const clearListState = () => {
    return () => {
        store.dispatch({
            type: CLEAR_LIST_STATE
        })
    }
}
複製程式碼

這裡實現了兩個actionType,一個是儲存列表狀態,一個是清除列表狀態。 儲存列表狀態就是為了達到回退時不重新整理頁面的需求; 清除列表狀態則是因為:從選單頁面進入列表頁面時,是要求重新載入頁面資料的,假如不清除redux中的快取資料,頁面就會讀取快取資料而不會重新請求網路資料,所以這個action也是很有必要的。

3、實現配合action操作state的reducer

import {CLEAR_LIST_STATE, LIST_STATE} from "../constants/actionTypes";

const initListState = {
    scrollTop: 0,//列表滑動位置
    listData: [],//列表資料
    pageIndex: 1,//當前分頁頁碼
    itemIndex: -1,//點選的條目index
}

const redListState = (state = initListState, action) => {
    if (action === undefined) {
        return state
    }

    switch (action.type) {
        case LIST_STATE:
            //更新列表狀態
            return {
                ...state,
                ...action
            }
        case CLEAR_LIST_STATE:
            //清空列表狀態
            return initListState
        default:
            return state
    }

}

export default redListState
複製程式碼
/**
 * Created by RaoMeng on 2018/12/10
 * Desc: 資料處理中心
 */

import {combineReducers} from 'redux'
import redUserInfo from './redUserInfo'
import redListState from './redListState'
import redClassData from './redClassData'

const reducers = combineReducers({redUserInfo, redListState, redClassData})

export default reducers
複製程式碼

這裡解釋下為什麼要記錄分頁頁碼以及點選的條目index。 記錄分頁頁碼只是在列表資料做了分頁的情況下需要。是為了回退到列表頁面後,使用者繼續上拉載入資料時頁碼是正確的。 記錄點選的條目index則是為了能在詳情頁更新所點選的條目資料。比如說一個會議簽到列表,使用者點選某條資料進入詳情頁後,點選簽到按鈕,這時我們要根據itemIndex來呼叫action的*saveListState()()*方法更新快取中相應的資料,將該條資料的狀態改為已簽到。這樣回退至列表頁面時,該條資料的展示才會正確。

4、建立store

import {createStore} from 'redux'
import reducers from '../reducers/index'
import {persistStore, persistReducer} from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';

const persistConfig = {
    key: 'root',
    storage: storage,
    stateReconciler: autoMergeLevel2 // 檢視 'Merge Process' 部分的具體情況
};

const myPersistReducer = persistReducer(persistConfig, reducers)

const store = createStore(myPersistReducer)

export const persistor = persistStore(store)
export default store

複製程式碼

這裡用到了redux-persist來實現redux資料的持久化儲存,我在 React通過redux-persist持久化資料儲存 有做簡單講解。

5、在點選條目的回撥事件中呼叫saveListState方法儲存列表狀態

    <父佈局
        ref={el => {
               this.container = el
        }}
        > </父佈局>

    onItemClick = index => {
        console.log('scrollTop', ReactDOM.findDOMNode(this.container).scrollTop)
        saveListState({
            scrollTop: ReactDOM.findDOMNode(this.container).scrollTop,
            listData: this.state.meetingSignList,
            pageIndex: mPageIndex,
            itemIndex: index,
        })()

        const {meetingSignList} = this.state
        this.props.history.push('/meet-detail/' + meetingSignList[index].meetId)
    }
複製程式碼

通過ReactDOM.findDOMNode(this.container).scrollTop來獲取父佈局的滑動距離

6、在頁面的componentDidMount方法中獲取redux資料

首先通過react-redux的connect方法將state中的資料繫結到頁面的props中,方便訪問


let mapStateToProps = (state) => ({
    listState: {...state.redListState}
})

let mapDispatchToProps = (dispatch) => ({})

export default connect(mapStateToProps, mapDispatchToProps)(MeetingSignIn)
複製程式碼

這樣,在頁面中就可以通過this.props.listState來訪問redux中快取的列表資料了

然後,在componentDidMount中獲取快取的列表資料,如果有快取資料,則載入,如果沒有則重新請求

componentDidMount() {
        document.title = '會議管理'
        console.log('listState', this.props.listState)
        if (this.props.listState && !isObjEmpty(this.props.listState.listData)) {
            this.setState({
                meetingSignList: this.props.listState.listData,
                isLoading: false,
            }, () => {
                ReactDOM.findDOMNode(this.container).scrollTop = this.props.listState.scrollTop
            })
            mPageIndex = this.props.listState.pageIndex
        } else {
            Toast.loading('資料載入中...', 0)
            mPageIndex = 0
            this.loadMeetList()
        }
    }
複製程式碼

這樣就實現了React通過redux快取列表資料以及滑動位置,回退時恢復頁面狀態的需求。

相關文章