商品管理介面-MerchandiseList

修改程式剛執行的初始畫面

src/navigator.js新增一個MerchandiseList 畫面

import { StackNavigator } from 'react-navigation';

import Home from './containers/Home';
import Counter from './containers/Counter';
import Camera from './components/Camera';
import PersonalInfo from './components/PersonalInfo';
import MerchandiseList from './components/MerchandiseList';
import NewMerchandise from './components/NewMerchandise';

const AppNavigator = new StackNavigator(
  {
    Home: { screen: Home },
    Counter: { screen: Counter },
    Camera: { screen: Camera },
    PersonalInfo: { screen: PersonalInfo },
    MerchandiseList: { screen: MerchandiseList },
    NewMerchandise: { screen: NewMerchandise }
  },
  {
    headerMode: 'screen',
    navigationOptions: {
      header: null
    }
  }
);

export default AppNavigator;

src/reducers/nav.jsroutes 中的routeName 來決定初始畫面

import AppNavigator from '../navigator';

const initialState = {
  index: 0,
  routes: [{ key: 'Init', routeName: 'MerchandiseList', params: {} }]
};

export default (state = initialState, action) =>
  AppNavigator.router.getStateForAction(action, state);

使用Touchable 元件來增加回饋

使用src/components/__data__/merchandise_list.json 作為測試資料

import MOCKDATA from './__data_/merchandise_list.json

  • 測試資料的資料結構

[
  {
    "picture_url": "https://cfshopeetw-a.akamaihd.net/file/08af7e4f7c83b53ee4d3acb2d1f19b88_tn",
    "discount": 0.8,
    "price": 200,
    "stars": 6819,
    "title":
      "[免運] 多件優惠 MIT台灣製-防潑水多功能桌上螢幕架 桌上架 鍵盤架 收納架 電腦架 ㄇ型架 ST004 居家大師",
    "count": 12
  },
  ...
]
  • FlatList

    • data (資料來源)

    • renderItem (資料如何顯示)

    • keyExtractor (設置每個Item 的 Id)

  • 使用TouchableOpacity 來實現Touch 的回饋與前往特定的頁面

  renderItem = ({ item }) => (
    <TouchableOpacity onPress={this.newMerchandise} activeOpacity={0.7}>
      <View style={styles.listItem}>
        <Image style={styles.thumbnail} source={ { uri: item.picture_url } } />
        <Text numberOfLines={1} ellipsizeMode="tail" style={styles.itemText}>
          {item.title}
        </Text>
      </View>
    </TouchableOpacity>
  );
  • 在最下方增加新建商品的按鈕

 state = {
   data: MOCKDATA
 }
 render(){
   return (
     <View style={styles.container}>
        <FlatList
          style={styles.list}
          keyExtractor={this.keyExtractor}
          data={this.state.data}
          renderItem={this.renderItem}
        />

      <Button onPress={this.newMerchandise} title="新增商品" />
    </View>
   )
 }
  • 圖文排版比例的設置 為 2:8

const styles = StyleSheet.create({
  itemText: {
    color: 'black',
    fontSize: 22,
    flex: 0.8
  },
  thumbnail: {
    width: 72,
    height: 72,
    flex: 0.2
  },

});

實現長按 item 的 ContextMenu,使用react-native 內建Modal 元件

  • animationType 動畫類型,

    • slide 由下而上出現,而消失則由上而下的消失

    • ·fade淡入淡出的方式

  • visible 是否顯示。

  • onRequestClose 當Android 上的 back 鍵觸發。

   <Modal
      animationType="slide"
      transparent
      visible={this.state.visible}
      onRequestClose={() => {
        this.setState({
          visible: false
        });
      }}
    >
    {// Modal的內容,可以是個兩個按鈕的確認視窗 }
  </Modal>
  • High Order Component 封裝了元件共用的行為,對行為做reuse ,而非對畫面做reuse

  • 實現connectModal hoc,並把openModal 與 closeModal 傳入,方便WrappedComponent 使用

  • openModal 讓WrappedComponent 可以傳入自定義客製化的modalView

  • openModal 也讓WrappedComponent 當下的context,譬如說當下的list item

  • 此Modal 並非blocking Modal,所以也實現了讓使用者點任意地方可以關掉此Modal,參考TouchableOpacity的部分

    • 建立Modal 的 HOC

    • 加入Context

    • 使用方開始客製化他的畫面

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
  StyleSheet,
  View,
  ViewPropTypes,
  Text,
  Modal,
  TouchableWithoutFeedback,
  TouchableOpacity
} from 'react-native';

const styles = StyleSheet.create({
  container: {
    flex: 1
  },
  modalView: {
    flex: 1,
    backgroundColor: '#333a'
  },
  closeModalView: {
    position: 'absolute',
    top: 0,
    left: 0,
    bottom: 0,
    right: 0
  }
});

const EmptyComponent = () => {};
export default function connectModal(WrappedComponent) {
  return class AppModal extends Component {
    state = {
      visible: false,
      modalView: EmptyComponent
    };
    openModal = ({ modalView, context }) => {
      const ModalView = modalView || EmptyComponent;
      this.setState({
        modalView: () => <ModalView context={context} />,
        visible: true
      });
    };
    closeModal = () => {
      this.setState({
        visible: false
      });
    };
    render() {
      const WrappedContextModalView = this.state.modalView;
      return (
        <View style={styles.container}>
          <WrappedComponent
            {...this.props}
            openModal={this.openModal}
            closeModal={this.closeModal}
          />
          <Modal
            animationType="slide"
            transparent
            visible={this.state.visible}
            onRequestClose={() => {
              this.setState({
                visible: false
              });
            }}
          >
            <View style={styles.modalView}>
              <TouchableOpacity onPress={this.closeModal} style={styles.closeModalView} />
              <WrappedContextModalView context={this.state.context} />
            </View>
          </Modal>
        </View>
      );
    }
  };
}

在需要使用此Modal 的Modal 在export 之前,掛上connectModal ,並在item 按下後,開啟Modal ,並傳入自定義的畫面。

import connectModal from './hoc/connectModal';

class MerchandiseList {
  onMerchandisePress = (item) => {
    this.props.openModal({ modalView: this.modal, context: item });
  };
  modal = ({ context }) => (
    <View style={styles.modal}>
      <View style={styles.buttonContainer}>
        <Button
          color="red"
          title="刪除"
          onPress={() => {
            this.removeItem(context);
            this.props.closeModal();
          }}
        />
      </View>
    </View>
  );
  ...
}

export default connectModal(MerchandiseList);

在按鈕被按下後,呼叫closeModal 的動作

Last updated