React Native 101 從打造一個線上商城開始
  • 環境安裝
  • 專案架構介紹與開始前的準備
  • 個人資訊-PersonalInfo
    • Layout 概念
    • View, Image, TextInput 元件介紹
    • 使用react-native-fetch-blob 來上傳照片
  • 商品管理介面-MerchandiseList
    • 元件介紹
  • 新建商品頁面-NewMerchandise
  • 商城頁面-MerchandiseGrid
  • 查看商品詳細頁面-MerchandiseDetail
  • 購物車頁面-ShoppingCart
  • 設定畫面Navigation
  • 使用Appetize.io
  • 修改專案名稱
  • 使用Expo
Powered by GitBook
On this page
  • 壓上折扣UI
  • 自製TabBar
  • Styles v.s ContentContainerStyle
  • 偵測程式是在背景(background)還是前景(foreground)
  • 加入PullToRefresh 的機制
  • 使用onEndReached 實現Pagination
  • NetInfo 偵測網路狀態 與 ListEmptyView

Was this helpful?

商城頁面-MerchandiseGrid

  • 使用numColumns 為 2 欄

 <FlatList
   keyExtractor={this.keyExtractor}
   numColumns={2}
   data={this.state.data}
   renderItem={this.renderItem}
/>

再來用flex 0.5 的方式把list item 做平分

const styles = StyleSheet.create({
  listItem: {
    margin: 10,
    flex: 0.5
  },
})

..
renderItem = ()=> {
  return (
   <TouchableOpacity onPress={this.goMerchandiseDetail} style={styles.listItem}>
        {...}
    </TouchableOpacity>
  )
}

壓上折扣UI

  • Image 上 疊加Text

  • 對Text 設定背景色與字體顏色

自製TabBar

  • 利用ScrollView 做水平捲移動,並關掉ScrollIndictator...

  • 在裡面包裹 TouhableHighlight做選中處理 ,可以是更改背景色或是加上框線

  • 根據儲存的state 來得知有無被按下,並設定框線

  • 使用`contentContanierStyle 教item 排列設為flexDirection:'row'

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row'
  },
})

export default class TabBar {

  static propTypes = {
    tabs: PropTypes.array.isRequired,
    onTabPress: PropTypes.func.isRequired
  }
  render(){
    return (
      <ScrollView
        contentContainerStyle={styles.container}
        horizontal
        showsHorizontalScrollIndicator={false}
      >
         <TouchableHighlight
            underlayColor="transparent"
            activeOpacity={1}
            onPress={() => {
              this.onTabPress(tab);
            }}
            style={ [ styles.tab, selected[tab.type] && { borderColor: 'white' } ] }
            key={tab.type}
          >

          </TouchableHighlight>
      </ScrollView>
    )
  }
}

Styles v.s ContentContainerStyle

  • ScrollView 與 FlatList 都利用 contentContainer. height > style.height 來判斷是否需要scrollBar

  • 所以不能把contentContainerStyle 設為{flex: 1},否則失去scroll 效果

  • e.g 畫面高長的width 為 640,但contentContainer 因item 超過範圍而變成 1024,這時把contentContainerStyle 設為 {flex: 1},則contentContainer 的width 變為 640 ,導致無法進行scroll

偵測程式是在背景(background)還是前景(foreground)

  • 在商品總覽中,利用此項機制,當使用者切回App時,重新刷新一次list,讓使用者看到最新的商品。

  • 在componentDidMount 進行註冊

  • 由於該listener 是從Native 曾回呼的,所以當該listener 發生錯誤後,在開發時會導致App Crash 而非 JS 層 的unhandled error

import {
  StyleSheet,
  View,
  Text,
  FlatList,
  Image,
  TouchableOpacity,
  TextInput,
  AppState
} from 'react-native';

export default class Example extends Component  {
   state = {
    appState: AppState.currentState
  };

  refresh = ()=>{
    //刷新list
  }
  handleAppStateChange = (nextAppState) => {
    if (nextAppState === 'active') {
      this.refresh();
    }
  };
  componentDidMount() {
    AppState.addEventListener('change', this.handleAppStateChange);
  }

  componentWillUnmount() {
    AppState.removeEventListener('change', this.handleAppStateChange);
  }

}

加入PullToRefresh 的機制

  • 新增refreshing與 onRefresh屬性

  • 在即將開始刷新之前,變更this.state.refreshing

  • 再刷新之後,再變更this.state.refreshing為false

  • 因為是測試資料,利用shuffle的方式改變測試資料的順序,模擬資料不同的情況

  export default class Example {

    shuffle = arr => arr.sort(() => Math.random() - 0.5);
    onRefresh = () => {
      this.setState({
        refreshing: true
      });
      setTimeout(() => {
        this.setState({
          refreshing: false,
          data: this.shuffle(this.state.data)
        });
      }, 1000);
    };

   render(){
     return (
       <FlatList
         refreshing={this.state.refreshing}
         onRefresh={this.onRefresh}
         {...}
       />
     )
   }

}

使用onEndReached 實現Pagination

  • API (pageNum: 1, numberOfItems: 20)

  • nextPageToken, maxResult

  • <FlatList
        onEndReached={() => {
          const nextPageToken = 'ABC' // {pageNum: this.state.pageNum++, numberOfItems: 20}
          this.refresh(nextPageToken);
        }}
    />
  • 使用onEndReachThreshold (0 ~ 1, 0.5 )

NetInfo 偵測網路狀態 與 ListEmptyView

  • ListEmptyView的三種狀態

    • Wifi/3G 未連上

    • 連上了,但獲取不到資料

    • 本來就是空的

  • 設置NetInfo 偵測網路狀態並在ListEmptyComponent 加入自製EmptyView

import {
  StyleSheet,
  View,
  Text,
  FlatList,
  NetInfo
} from 'react-native';

const CONNECTION_ERROR = {
  offline: '網路斷線了',
  unknown: '未知的錯誤'
};
export default class Example {

  componentDidMount() {
    NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectivity);
  }

  componentWillUnmount() {
    NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectivity);
  }

  emptyView = () => {
      const { error } = this.state;
      return (
        <View style={[styles.container, styles.center]}>
          <Text>{CONNECTION_ERROR[error]}</Text>
        </View>
      );
  };
  handleConnectivity = (isConnected) => {
      if (!isConnected) {
        this.setState({
          data: [],
          error: 'offline'
        });
      } else {
        // refetch your data
        this.refresh();
      }
      this.setState({
        isConnected
      });
  };

  render(){
    return (
      <FlatList
        ListEmptyComponent={this.emptyView}
        refreshing={this.state.refreshing}
        onRefresh={this.refresh}
        contentContainerStyle={styles.merchandiseGrid}
        keyExtractor={this.keyExtractor}
        numColumns={2}
        data={this.state.data}
        renderItem={this.renderItem}
      />
    )
  }
}
  • 但是emptyView 的文字居然沒有垂直置中...

    • Center EmptyView's PR which intend to release react-native 0.56

  • 自己來

    • 當data 是空的時候,隱藏FlatList,然後切換至EmptyView

Previous新建商品頁面-NewMerchandiseNext查看商品詳細頁面-MerchandiseDetail

Last updated 5 years ago

Was this helpful?

https://developers.google.com/youtube/v3/guides/implementation/pagination
https://github.com/facebook/react-native/pull/18206