react-native開發常見問題

Undo_03發表於2018-06-14

react-native開發中常見的問題彙總,以下是我最近在專案開發中遇到的常見的問題,做以下記錄以便後續專案中使用。

1. 監聽網狀連線狀態的變化


  componentDidMount () {
    NetInfo.addEventListener('change', this.handleConnectivityChange);
  }

  componentWillUnmount() {
    NetInfo.removeEventListener('change', this.handleConnectivityChange);
  }

  handleConnectivityChange() {
    NetInfo.isConnected.fetch().then(netConnected => {
      if (netConnected) {
        ToastAndroid.show('網路已連線', ToastAndroid.SHORT, ToastAndroid.BOTTOM)
      } else {
        ToastAndroid.show('請檢查網路連線', ToastAndroid.SHORT, ToastAndroid.BOTTOM)
      }
    })
  }
複製程式碼

2. 處理特殊頁面的回退按鈕和物理回退事件

 // navigation 的 didFocus willBlur事件
 // BackHandler 的 hardwareBackPress 事件
 // this.props.navigation.replace(RouterName) 路由替換
 // this.props.navigation.isFocused() 路由啟用狀態
  _didFocusSubscription;
  _willBlurSubscription;
  
  constructor (props) {
    super(props)
      this._didFocusSubscription = props.navigation.addListener('didFocus', payload =>
      BackHandler.addEventListener('hardwareBackPress', this.onBackButtonPressAndroid))
  }
  
  componentDidMount () {
      this._willBlurSubscription = this.props.navigation.addListener('willBlur', payload => {
      BackHandler.removeEventListener('hardwareBackPress', this.onBackButtonPressAndroid)
    })
  }

  componentWillUnmount () {
    this._didFocusSubscription && this._didFocusSubscription.remove()
    this._willBlurSubscription && this._willBlurSubscription.remove()
    BackHandler.removeEventListener('hardwareBackPress', this.onBackButtonPressAndroid)
  }
  
  onBackButtonPressAndroid = () => {
    if (this.toRouterName) {
      this.props.navigation.replace(this.toRouterName)
      return true
    } else {
      return false
    }
  };
  
複製程式碼

3. createStackNavigator, createBottomTabNavigator 路由巢狀 StackNavigator的跳轉問題

// 需要給TabNavigator 重新傳navigation屬性 重新定義TabNavigator容器元件的router為TabNavigator的router
class MainView extends Component {
  static navigationOptions = {
    header: null
  };

  render () {
    let { isMask } = this.props.global
    const { cartCount } = this.props
    return (
      <View>
        <AppTabNavigator navigation={this.props.navigation}/>
      </View>
    )
  }
}
MainView.router = AppTabNavigator.router
複製程式碼

4. tabBarOnPress 攔截tab導航的tab點選事件

  navigationOptions: ({navigation, screenProps}) => ({
	  title: '我的',
	  tabBarIcon: ({focused}) => (
	    <Image
	      source={focused ? require('../images/ic_mine_checked.png') : require('../images/ic_mine.png')}
	      style={styles.tabIcon}
	    />
	  ),
	  tabBarOnPress: () => {
	    if (!screenProps.netConnected) {
	      navigation.navigate('Home')
	    }
	  }
	})
複製程式碼

5. 解決鎖定螢幕方向 還有鍵盤頂起tab導航等問題

<activity
    android:name=".MainActivity"
    android:label="@string/app_name"
    android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
    
    新增一行:android:screenOrientation="portrait"
    設定為portrait是鎖定豎向,landscape是鎖定橫向
    
    
// screenProps  tab導航中動態渲染資料 物件中的值可以繫結redux中的值
<AppTabNavigator screenProps={{cartCount: cartCount}} navigation={this.props.navigation}/>

複製程式碼

6. 監聽路由事件,可以用來做雙擊物理回退按鈕退出App等其他功能

import { createStackNavigator, NavigationActions } from 'react-navigation'
import { ToastAndroid, BackHandler } from 'react-native'
const AppNavigator = createStackNavigator(routes, stackConfig)const defaultStateAction = AppNavigator.router.getStateForAction
let lastBackPressed = 0
AppNavigator.router.getStateForAction = (action, state) => {
  const backRouteName = state && state.routes[state.routes.length - 1].routeName
  if (state && (action.type === NavigationActions.BACK) && (backRouteName === 'MainView')) {
    if ((lastBackPressed + 2000) < Date.now()) {
      ToastAndroid.show('再按一次退出', ToastAndroid.SHORT)
      lastBackPressed = Date.now()
      return { ...state }
    }
    BackHandler.exitApp()
  }
  return defaultStateAction(action, state)
}

複製程式碼

7. 解決 isMounted(...) is deprecated warning

import { YellowBox } from 'react-native'
YellowBox.ignoreWarnings(['Warning: isMounted(...) is deprecated', 'Module RCTImageLoader'])

複製程式碼

8. 合理使用 componentWillReceiveProps, shouldComponentUpdate

  componentWillReceiveProps (nextProps) {
    if (!nextProps.xxx) {
      dosomething
    }
  }
  
   shouldComponentUpdate (newProps, newState) {
    if (this.props.navigation.isFocused()) {
      return true // render
    } else {
      return false // not render
    }
  }
  
複製程式碼

9. 工具類logger 和 建立store

	import { createStore, applyMiddleware, compose } from 'redux'
	import screenTracking from './screenTrackingMiddleware'
	import reducer from '../reducers'
	import logger from 'redux-logger'
	
	const middlewares = []
	
	if (__DEV__) {
	  middlewares.push(logger)
	}
	
	middlewares.push(screenTracking)	
	const store = compose(applyMiddleware(...middlewares))(createStore)(reducer)
	
	export default store
複製程式碼

10. 比較好用的第三方元件

	react-native-swiper 				swiper元件
	react-native-linear-gradient        漸變元件
	react-native-swipe-list-view		側滑元件
	react-native-image-crop-picker   圖片裁剪、呼叫相機和手機相簿
	react-native-cookies				管理Cookie
    
複製程式碼

11. ScrollView 元件監聽滾動到底部

  _contentViewScroll (e) {
    let offsetY = e.nativeEvent.contentOffset.y // 滑動距離
    let contentSizeHeight = e.nativeEvent.contentSize.height // scrollView contentSize高度
    let oriageScrollHeight = e.nativeEvent.layoutMeasurement.height // scrollView高度
    if (offsetY + oriageScrollHeight >= contentSizeHeight) {
    	// do something  
    }
  }
  
  <ScrollView 
        onMomentumScrollEnd={this._contentViewScroll.bind(this)}
        showsVerticalScrollIndicator={false}> 
  </ScrollView>
    
複製程式碼

12. Image 使圖片按寬度或者高度自適應

	class ImageAdaptive extends Component {
	  state = {
	    width: 0,
	    height: 0
	  }
	
	  componentDidMount () {
	    Image.getSize(this.props.uri, (width, height) => {
	      let h = 330 * height / width
	      this.setState({height: h})
	    })
	  }
	
	  render () {
	    return (
	      <Image source={{uri: this.props.uri}} style={[{width: 330, height: this.state.height}, this.props.style]} />
	    )
	  }
	}
    
複製程式碼

13. 根據Swiper元件改裝的圖片預覽元件

  <Swiper
    loop={false}
    index={initialSlide}
    renderPagination={(index, total) => (
      <View style={styles.paginationStyle}>
        <Text style={styles.paginationText}>{index + 1}/{total}</Text>
        {
          delImage
            ? <TouchableOpacity onPress={() => { delImage && delImage(index) }} style={styles.del}>
              <Image source={require('../../images/personal/ic_preview_delete.png')} style={styles.imageDel} />
            </TouchableOpacity> : null
        }
      </View>
    )}>
    {
      images && images.map((image, index) => (
        <TouchableOpacity style={styles.imagesItemWrapper} key={index} activeOpacity={1} onPress={this.closeModal}>
          <Image resizeMode='contain' style={styles.imagesItem} source={{ uri: image }} />
        </TouchableOpacity>
      ))
    }
  </Swiper>    
複製程式碼

14. StatusBar.currentHeight 可以從純前端的角度相容劉海屏沉浸式狀態列

相關文章