React通過redux緩存列表數據以及滑動位置,回退時恢復頁面狀態

在使用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緩存列表數據以及滑動位置,回退時恢復頁面狀態的需求。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章