關於React中狀態保存的研究


在使用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;
    }
  	// ...
}

效果如下:

child-router

可以看到,當從詳情頁面返回時,點擊的激活狀態依舊可以保存,但是列表滾動的高度並不能夠保存,關於高度的恢復在下面會講到。

解決方案二:當前頁面彈窗

不占用路由,在當前頁面直接已彈窗的形式加載詳情頁面。

// 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>
  		)
	}
}

效果如下:

modal

看上去效果十分好,既能保存狀態,也能保存滾動條的高度。

解決方案三:本地存儲/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
};

效果如下:

redux

效果和字路由方式相同,依然存在滾動高度不能保存的問題。

滾動高度問題

下面來談談如何解決滾動高度的問題,綜合起來還是一種恢復現場的方式。在頁面即將離開之前,保存之前的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 });
}

效果如下:

scrollTop

嘗試方案:react-keeper

github上搜索看到了這個庫,類似於react-router的一個翻版,同時在react-router的基礎上增加了類似於vue-router中的keep-alive功能,這點暫時占坑,等做了案例之后再來填坑。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM