今天在Demo這樣一個項目的時候,首先遇到的第一個問題就是,每次通過dialog選擇【本周、本月、本天】時,伴隨着內容重新渲染的時候,tab navigator每次都重新創建和渲染,造成性能浪費和用戶體驗差的問題
問題分析:
由於tab navigator是在render()函數中動態創建的,因此每次隨着狀態的改變,render都會重新渲染,從而導致的問題是,每次都要重新動態創建tab navigator。
解決方案
將創建好的tabNav給綁定到當前this上,如果當前tabNav已經存在,則直接返回。
偽代碼
export default class TrendingPage extends Component<Props> {
constructor(props) {
super(props);
this.tabNames = ['All', 'C', 'C#', 'PHP', 'JavaScript'];
}
/**
* 優化tabNav 避免每次render的時候都要重新動態的創建nav
*
* 優化效率:根據需要選擇是否重新創建TabNavigator,通常tab改變后才重新創建
* @returns
* @memberof TrendingPage
*/
_tabNav() {
if (!this.tabNav) {
this.tabNav = createMaterialTopTabNavigator(
this._genTabs(), {
tabBarOptions: {
tabStyle: styles.tabStyle,
upperCaseLabel: false, //是否標簽大寫,默認為true
scrollEnabled: true, // 選項卡排不下的時候,可以滾動,默認為false
style: {
backgroundColor: '#678',
height: 30,
},
indicatorStyle: typeAlias.indicatorStyle, // 標簽指示器的樣式
labelStyle: styles.labelStyle
}
}
);
}
return this.tabNav;
}
_genTabs() {
const tabs = {};
this.tabNames.forEach((item, index) => {
tabs[`tab${index}`] = {
// screen: PopTab,
screen: props => <TrendingTabPage {...props} timeSpan={this.state.timeSpan} tabLabel={item} />,
navigationOptions: {
title: item
}
}
})
return tabs;
}
/* 省略其他代碼*/
}
但是隨之出現了接下來的問題,就是當我們通過在dialog選擇完【本周】后,導航欄不變的情況下不重新動態創建了,下面的數據也不刷新了,從而導致該功能失效。
問題分析
由於帶有信息list的screen是綁定在創建tab navigator中,因此當tab navigator不重新創建時,screen內容將不會重新渲染,那么我們又要保持當tab不改變的時候不重新創建和渲染,又想要當前tab下的內容根據用戶操作而改變。
解決方案
使用react-native官方的一個API-----DeviceEventEmitter
使用方法
componentDidMount() {
this.loadData();
this.timeSpanChangeListener = DeviceEventEmitter.addListener(EVENT_TYPE_TIME_SPAN_CHANGE, (timeSpan) => { //將監聽事件綁定到當前this上的目的是在組件卸載時能撤銷事件
this.timeSpan = timeSpan;
this.loadData();
})
}
componentWillUnmount() {
if (this.timeSpanChangeListener) {
this.timeSpanChangeListener.remove();
}
}
DeviceEventEmitter.addListener()注冊上事件,第一個參數是事件,第二個參數是事件觸發時候的回調。注意在組件卸載時撤銷事件的綁定,避免性能浪費。
/**
* 當從selection中選擇一個觸發的回調
*
* @param {*} tab
* @memberof TrendingPage
*/
onSelectTimeSpan(tab) {
this.dialog.dismiss();
this.setState({
timeSpan: tab
});
DeviceEventEmitter.emit(EVENT_TYPE_TIME_SPAN_CHANGE, tab)
}
/**
* 創建一個TrendingDialog
*
* @returns
* @memberof TrendingPage
*/
renderTrendingDialog() {
return <TrendingDialog
ref={dialog => this.dialog=dialog}
onSelect={tab=>this.onSelectTimeSpan(tab)}
/>
}
然后,我們給【本周、本天、本月】添加上回調onSelectTimeSpan,當用戶操作的時候,觸發回調,然后通過DeviceEventEmitter.emit()方法觸發綁定的事件,執行回調,更新內容。