React通过redux缓存列表数据以及滑动位置,回退时恢复页面状态

51次阅读

共计 3858 个字符,预计需要花费 10 分钟才能阅读完成。

在使用 React 和 React-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 缓存列表数据以及滑动位置,回退时恢复页面状态的需求。

正文完
 0