Form

Form

2020/07/19
2020/07/21 (更新內容)
2020/08/04 (增加連結)
2020/12/16 (補充內容)

TextInput

在javascript裡,可以利用document.getElementById取得DOM的內容,然而,在react native裡,並不建議直接去取得DOM的內容,所以,寫法就跟javascript不一樣了。

HTML裡的Form,是在submit後才利用document.getElementById取得desc及price裡的內容,在react native裡,則是在輸入時(onChangeText)就將內容放到state裡,並將value設為state。

<View>

<TextInput placeholder="產品說明" value={desc} onChangeText={text=>setDesc(text)}/>

<TextInput placeholder="價格" value={price} onChangeText={text=>setPrice(text)}/>

<Button onPress={update} title="新增"/>

</View>

在點擊按鈕時,就可以呼叫update函數。

<Button onPress={update} title="新增"/>

update函數則呼叫ProductList裡的update函數。這樣的做法稱為callback method,如果只是簡單的父對子,可以使用這樣的方式,但是,如果元件的關係很多很複雜,這樣的做法就會產生所謂的call back hell,解決的方法有:context或者使用redux(或類似的方法)。

function update(){

props.update({desc,price});

}

完整的程式

src/product/ProductAdd.js

import React, {useState} from 'react';

import { Button , TextInput, View } from 'react-native';

export default function ProductAdd(props) {

const [desc, setDesc] = useState("");

const [price, setPrice] = useState("");


function update(){

props.update({desc,price});

}


return (

<View>

<TextInput placeholder="產品說明" value={desc} onChangeText={text=>setDesc(text)}/>

<TextInput placeholder="價格" value={price} onChangeText={text=>setPrice(text)}/>

<Button onPress={update} title="新增"/>

</View>

);

}

setState (setProducts)接受兩種參數,第一種參數是個值,setState會更新state的內容,當我們給的值是陣列的時候,這種傳遞方式會有問題。所以,必須用第二種傳遞方式,就是接受一個callback函數,當參數是callback函數時,setState會將原有的值以及props傳給callback函數。 (詳參:State 和生命週期)

在下面範例裡,我們只使用原有的值,將這個值命名為oldProduct。在ProductList中,將ProductAdd傳回的newProduct,利用setProducts利用spread將收到的內容加到陣列最後。

spread語法請詳參: Day 09: ES6篇: Spread Operator & Rest Operator(展開與其餘運算符)

function update(newProduct){

setProducts(oldProducts=>[...oldProducts, newProduct]);

}

** 注意,這裡不能用push ,下面的語法有時候無法有效更新products**

function update(newProduct){

products.push(newProduct);

setProducts(products);//not working....

}

再把剛剛的ProductAdd加進來

src/product/ProductList.js

import React, {useState} from 'react';

import {FlatList, View, Text, TouchableOpacity} from 'react-native';import ProductAdd from './ProductAdd';

import styles from '../styles';

/*

const data =[

{name:"iPhone 7", price:12000},

{name:"iPhone 8", price:10000},

{name:"iPhone X", price:20000},

]

*/


export default function ProductList() {

const [selected, setSelected] = useState(null);

const [products, setProducts] = useState([

{desc:"iPad", price:20000},

{desc:"iPhone X", price:30000}

]);

const renderItem = ({ item, index }) => {

const backgroundColor = index === selected ? "#f9c2ff" : "#00ffff";

return(

<TouchableOpacity onPress = {()=>setSelected(index)} style={[styles.item, {backgroundColor}]}>

<Text style={styles.title}>{item.desc}</Text>

<Text>{item.price}</Text>

<Text>/{index}</Text>

</TouchableOpacity>

)

};


function update(newProduct){

setProducts(oldProducts=>[...oldProducts, newProduct]);

}


return (

<View style={styles.container}>

<FlatList

data={products}

renderItem = {renderItem}

keyExtractor={item => item.desc}

>

</FlatList>

<ProductAdd update={update}/>

</View>

);

}

作業

Modal

  • 試試看輸入的部分改為利用Modal。修改ProductAdd.js的內容

<Modal>

<TextInput placeholder="產品說明" value={desc} onChangeText={text=>setDesc(text)}/>

<TextInput placeholder="價格" value={price} onChangeText={text=>setPrice(text)}/>

<Button onPress={update} title="新增"/>

</Modal>

利用visible來控制顯示與否,建議在ProductList裡增加一個state變數modalVisible,並且將modalVisible傳到ProductAdd:

<Modal visible={props.modalVisible}>

<TextInput placeholder="產品說明" value={desc} onChangeText={text=>setDesc(text)}/>

<TextInput placeholder="價格" value={price} onChangeText={text=>setPrice(text)}/>

<Button onPress={update} title="新增"/>

</Modal>

當按了新增之後,modal必須被隱藏,做法就是將ProductList裡的modalVisible設為false。

NativeBase

  • 試試看不同的UI套件 (如: NativeBase)

  • NativeBase (for React Native and Vue Native)

  • **由於版本相容的問題,最近有些同學會遇到這個問題

C:/myproj/pokesearch/node_modules/@codler/react-native-keyboard-aware-scroll-view/lib/KeyboardAwareHOC.js 13:12

Module parse failed: Unexpected token (13:12)

先安裝 NativeBase

expo install native-base

expo 要安裝字型

expo install expo-font

** 如果不是使用expo,要安裝相依的套裝(packages)

The peer dependencies included from any npm packages does not automatically get installed. Your application will not depend on it explicitly.

react-native link

Floating Action Buttons (FAB)

    • FAB (Floating Action Buttons)

import {Icon, Fab} from 'native-base';

在ProductList裡加上Fab及Icon:

<Fab onPress={()=>setModalVisible(true)}>

<Icon ios='ios-add' android="md-add"/>

</Fab>

參考資料

感謝張晴惠同學提供以下範例:

import React, { useState } from 'react';

import { CheckBox, Text, StyleSheet, View } from "react-native";


export default App = () => {

const [data] = useState(['hello','haha','cool','excellent']); // 你要的選擇

const [isSelected, setIsSelected] = useState([]) //用來存放你選的



// 當按框框所執行的function

const handleOnSelect = (item) => {

const index = isSelected.indexOf(item)

const isExisted = index > -1

const tempArr = [...isSelected]


// 如果存在就刪除,反之則push進去

if(isExisted){

tempArr.splice(index, 1)

} else {

tempArr.push(item)

}

setIsSelected(tempArr)

}


return (

<View style={styles.container}>

{

data.map((item)=>(

<View style={styles.checkboxItem}>

<Text style={{fontSize: 20, marginRight: 6}}>{item}</Text>

<CheckBox

value={isSelected.includes(item)} //如果在陣列裡面就會回傳true(checked)

onChange={()=>handleOnSelect(item)}

style={styles.checkbox}

/>

</View>

))

}

<Text>

所選擇 : {isSelected.map((item, index)=>{

let str = '、'+item

if(index === 0 ) str = item

return str

})}

</Text>

</View>

);

};


const styles = StyleSheet.create({

container: {

flex: 1,

padding: 16,

justifyContent: "center",

},

checkboxItem: {

margin: 6,

flexDirection: 'row',

alignItems: 'center',

}
});

常見問題

  • **由於版本相容的問題,最近有些同學會遇到這個問題

C:/myproj/pokesearch/node_modules/@codler/react-native-keyboard-aware-scroll-view/lib/KeyboardAwareHOC.js 13:12
Module parse failed: Unexpected token (13:12)