在使用react
搭配react-router
做應用的時候,你可能遇到這樣的問題,當我從第一個頁面過渡到第二個頁面,然后返回之后,發現之前的頁面的狀態全部不見了,即回到了初始的狀態。
這點在頁面存在多個TAB頁或者多條件篩選的時候體驗會更加明顯,這時候我又不得不點擊我之前選擇的頁簽,重新選擇篩選條件,然后再進行搜索。因此,在這種情況下,保存之前的狀態顯得尤為亟待解決,下面是自己實踐出來的幾種方法,做一下分享,同時希望和各位一起探討,看能不能有什么更好的辦法。
代碼:github
解決方案一:子路由方式
// normal/routers/Books/Books.js
module.exports = {
path: 'books',
getComponent(nextState, cb) {
require.ensure([], (require) => {
cb(null, require('./components/Books'))
})
},
getChildRoutes(partialNextState, cb) {
require.ensure([], (require) => {
cb(null, [
require('./book')
])
})
}
};
// normal/routers/Books/book.js
module.exports = {
path: 'book/:id',
getComponent(nextState, cb) {
require.ensure([], (require) => {
cb(null, require('./components/Book'))
})
}
};
配置圖書列表下的嵌套路由可以查看圖書詳情。具體的路由跳轉如下:
// normal/routers/Books/components/Books.js
onLookDetail(id, book, index) {
this.setState({ activeIndex: index });
this.props.router.push({ pathname: `books/book/${id}`, query: book });
}
render() {
const { children } = this.props;
// ...
// 如果有字路由組件,就渲染子組件
if (children) {
return children;
}
// ...
}
效果如下:
可以看到,當從詳情頁面返回時,點擊的激活狀態依舊可以保存,但是列表滾動的高度並不能夠保存,關於高度的恢復在下面會講到。
解決方案二:當前頁面彈窗
不占用路由,在當前頁面直接已彈窗的形式加載詳情頁面。
// normal/routers/Books/components/Books.js
constructor(props) {
super(props);
this.state = {
activeIndex: -1,
books: [],
modal: false
};
}
onLookDetail(id, book, index) {
this.setState({ activeIndex: index, modal: true });
}
onDetailBack() {
this.setState({ modal: false });
}
render() {
{
// 根據state中的modal值來判斷當前彈窗是否顯示
// 其實就是Book.js中的代碼
modal && (
<div style={ styles.modal }>
<Flex direction="column" style={ styles.wrapper }>
<div style={ styles.header }>
<NavBar
mode="dark"
leftContent="返回"
icon={<Icon type="left" />}
onLeftClick={ this.onDetailBack.bind(this) }>
圖書詳情
</NavBar>
</div>
<div style={ styles.content }>
<Card>
<Card.Header
title="標題"
thumb="xxx"
extra={ <span>{ book.title }</span> }/>
<Card.Body>
<div>{ book.description }</div>
</Card.Body>
<Card.Footer
content="footer content"
extra={<div>{ book.price }</div>} />
</Card>
</div>
</Flex>
</div>
)
}
}
效果如下:
看上去效果十分好,既能保存狀態,也能保存滾動條的高度。
解決方案三:本地存儲/redux數據倉庫/參數傳遞
我把這三種方案歸結為一種,因為實際上是在離開列表組件的時候保存當前的狀態,然后在回到頁面的時候根據之前保存的狀態來進行現場恢復而已。
// src/routers/Books/components/Books.js
// 配合shouldComponentUpdate聲明周期函數,避免不必要的渲染
shouldComponentUpdate(nextProps, nextState) {
return !is(fromJS(this.props.books), fromJS(nextProps.books))
|| !is(fromJS(this.state), fromJS(nextState));
}
// 更新當前選中的activeIndex值,將其同步至redux中,然后再進行路由跳轉
onLookDetail(id, book, index) {
const { actions } = this.props;
actions.updateBooks({ activeIndex: index });
this.props.router.push({ pathname: `book/${id}`, query: book });
}
// 從redux中取值進行現場恢復
render() {
const { books } = this.props;
const list = books.books;
const activeIndex = books.activeIndex;
// ...
}
// src/reudx/reudcers/books.js
const initialState = {
books: [],
activeIndex: -1
};
效果如下:
效果和字路由方式相同,依然存在滾動高度不能保存的問題。
滾動高度問題
下面來談談如何解決滾動高度的問題,綜合起來還是一種恢復現場的方式。在頁面即將離開之前,保存之前的scrollTop
值,然后再次回到這個頁面的時候,恢復滾動高度即可。
// src/reudx/reudcers/books.js
const initialState = {
books: [],
activeIndex: -1,
// 添加scrollTop
scrollTop: 0
};
// src/routers/Books/components/Books.js
componentDidMount() {
const { actions, books } = this.props;
const content = this.refs.content;
const scrollTop = books.scrollTop;
if (scrollTop > 0) {
content.scrollTo(0, scrollTop);
}
setTimeout(() => {
actions.getBooks();
}, 150);
}
componentWillUnmount() {
const content = this.refs.content;
const { actions } = this.props;
actions.updateBooks({ scrollTop: content.scrollTop });
}
效果如下:
嘗試方案:react-keeper
在github
上搜索看到了這個庫,類似於react-router
的一個翻版,同時在react-router
的基礎上增加了類似於vue-router
中的keep-alive
功能,這點暫時占坑,等做了案例之后再來填坑。