# 新建商品頁面-NewMerchandise

### Icon 元件

* 由`react-native-vector-icons` 提供

```
import Icon from 'react-native-vector-icons/FontAwesome'

...

<Icon name="plus" size={24} color='#ccc' />
```

* `name`該icon 顯示名稱，由網站上查詢而得 [https://fontawesome.com/icons?d=gallery](https://fontawesome.com/icons?d=gallery)
* `color` 設定icon的顏色

### Icon.Button 元件

* 實現點擊Icon 執行指定動作
* `borderRadius`
* `activeOpacity`
* `underlayColor`

```
 <Icon.Button borderRadius={0} onPress={this.addToCart} name="plus" size={56} />
```

*   為Icon.Button 加入文字

    ```
     <Icon.Button
          color="#ccc"
          style={styles.commentAction}
          backgroundColor="transparent"
          name="thumbs-up"
          size={36}
          activeOpacity={0.7}
          onPress={this.onLike}
     >
      <Text style={styles.actionText}>給予賣家好評</Text>
    </Icon.Button>
    ```

## 使用AsyncStorage

* 儲存UI 的 local state，譬如說因表單過於複雜，防止UI state reset 或等資料消失因素，讓使用者必須重新輸入，造成不便
  * 若存放的是物件，使用`JSON.stringify(object)` 將其轉成字串
  * 取出時再用`JSON.parse` 抓回
  * AsyncStorage 底層是用資料庫的方式實現，所以會有寫檔與讀檔的等待時間，所以在`getItem` 時必須使用await 等待讀檔完成
  * 由於setItem 並不想要知道存檔成功或失敗的結果，所以不使用await等待setItem結束

```
import { AsyncStorage } from 'react-native';
const STORE_KEY = 'NewMerchandise';

export default class Example extends Component {

  async componentWillMount() {
    const form = await AsyncStorage.getItem(STORE_KEY);
    if (form) {
      this.setState({
        ...this.state,
        ...JSON.parse(form)
      });
    }
  }

  composeStringForm = () => {
    const { description, title, photos } = this.state;
    return JSON.stringify({
      description,
      title,
      photos
    });
  };

  onTitleChange = (text) => {
    this.setState(
      {
        title: text
      },
      () => {
        AsyncStorage.setItem(STORE_KEY, this.composeStringForm());
      }
    );
  };  
}
```

### 實現上傳多張照的UI

* 自製PhotoItem component 並且設計是否顯示＋號的Property

```
export default class PhotoItem extends Component {
  static propTypes = {
    displayAdd: PropTypes.bool,
    onPress: PropTypes.func.isRequired
  };

  static defaultProps = {
    displayAdd: false
  };
  ....
}
```

* 利用`react-native-vector-icons`顯示加號

```
      <TouchableOpacity
        onPress={this.props.onPress}
        style={[styles.defaultSize, styles.dashBorder, styles.itemContainer]}
      >
        {displayAdd && <Icon size={58} name="plus" />}
      </TouchableOpacity>
```

* 當按下後將onPress delegate回 parent component (NewMerchandise) 去開啟相機
* 使用相機的key值來區分是該上傳的照片是在畫面三格中的哪一格

```
  goMerchandiseCamera = (i) => {
    const { photos } = this.state;

    const isContainEmpty = photos[i].empty;
    if (isContainEmpty) {
      const onTakePicture = (key, uri) => {
        const newPhotos = [...photos];
        newPhotos[i].empty = false;
        newPhotos[i].uri = uri;
        this.setState({
          photos: newPhotos
        });
      };
      this.props.navigation.navigate('Camera', {
        to: 'NewMerchandise',
        key: i,
        onTakePicture
      });
    }
  };
```

### 使用TextInput 加自製Icon Button 實現簡易加入連結的功能

* 偵測使用者選擇的範圍 `onSelectionChange` 並記錄起來

```
  onDescriptionSelectionChange = ({ nativeEvent }) => {
    const { start, end } = nativeEvent.selection;
    this.setState({
      descriptionStart: start,
      descriptionEnd: end
    });
  };
```

*   將選取的範圍在`onChangeText`中替換成`<a href={連結網址}>{連結文字}</a>`來達到連結效果

    ```
    attachLink = () => {
      console.warn('attach link');
      const { description, descriptionStart, descriptionEnd } = this.state;

      const selectionString = description.substring(descriptionStart, descriptionEnd);
      const ANCHOR_LINK = `<a href="http://www.google.com.tw">${selectionString}</a>`;

      this.setState({
        description: description.replace(selectionString, ANCHOR_LINK)
      });
    };
    ```
* 將TextInput 利用 `ScrollView`包裹住，讓使用者可以輸入長篇的商品簡介(mutiline=true)，並且只在輸入description 時跳出自製toolbar
* 設置`multiline=true`讓使用者可以按下enter鍵進行換行

```
<ScrollView
  ref={(ref) => {
    this.descriptionScrollRef = ref;
  }}>
    <TextInput
      onFocus={() => {
        this.setState({
          showToolBar: true
        });
      }}
      onBlur={() => {
        this.setState({
          showToolBar: false
        });
      }}
      multiline
      placeholder="請輸入商品敘述"
      value={this.state.description}
      onChangeText={this.onDescriptionChange}
      onSelectionChange={this.onDescriptionSelectionChange}
      ref={(ref) => {
        this.description = ref;
      }}
    />
</ScrollView>
```

* \[錯誤更正]並不存在要使用scrollToEnd的情況，~~若將整個版面以ScrollView 包裝起來則不用使用~~`scrollToEnd，`

因為版面的ScrollView 在TextInput增長的過程中，會往下進行scroll

&#x20;{photos.map((photo, i) => ( { this.goMerchandiseCamera(i); \}} displayAdd={photo.empty} uri={photo.uri} /> ))} { this.setState({ showToolBar: true }); \}} onBlur={() => { this.setState({ showToolBar: false }); \}} multiline placeholder="請輸入商品敘述" value={this.state.description} style={styles.description} onChangeText={this.onDescriptionChange} onSelectionChange={this.onDescriptionSelectionChange} ref={(ref) => { this.description = ref; \}} />
