在使用React和React-router實現單頁面應用時,會有這樣一個場景:從列表頁面點擊某項條目進入詳情頁,而後回退至列表頁面時,列表頁面會從新刷新,不只數據從新獲取了,滾動條也回到了頂部。用戶要繼續查看剩餘數據的話,須要從新滑動到以前點擊的那個條目,若是列表作了分頁的話就更麻煩了,這對於用戶體驗來講是很是很差的。react
因此咱們但願能作到,從二級頁面回退至列表頁面時,列表頁面能保留以前的狀態(數據和滾動條位置)。npm
那麼怎麼實現呢?下面分享一下React經過redux來緩存列表數據以及滑動位置,以達到保留列表頁面狀態的方法。redux
關於redux以及react-redux的使用,這裏就不作講解了,能夠參考我以前寫的 React-redux的原理以及使用 。固然網絡上有不少講解得更清晰的文章,讀者能夠自行搜索。緩存
下面直接進入正題,介紹實現需求的步驟吧網絡
cnpm install redux react-redux -dev --save
/** * 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也是頗有必要的。佈局
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()()方法更新緩存中相應的數據,將該條數據的狀態改成已簽到。這樣回退至列表頁面時,該條數據的展現纔會正確。this
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持久化數據存儲 有作簡單講解。spa
<父佈局 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來獲取父佈局的滑動距離code
首先經過react-redux的connect方法將state中的數據綁定到頁面的props中,方便訪問component
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緩存列表數據以及滑動位置,回退時恢復頁面狀態的需求。