> For the complete documentation index, see [llms.txt](https://zhang699-2.gitbook.io/react-native/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://zhang699-2.gitbook.io/react-native/shang-pin-guan-li-jie-mian.md).

# 商品管理介面-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.js`的 `routes` 中的`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
  * <https://reactjs.org/docs/higher-order-components.html>
* 實現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 的動作
