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>
);
}
作業
嘗試使用React native的這些元件
嘗試利用其他的UI library提供的元件
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>
參考資料
Input (React Native TextInput)
Item
Label (React Native Text)
ListItem
How to Create Forms for Your React/React Native App
Controlled vs Uncontrolled Forms
How to choose a form library?
Popular Form Libraries
Formik -React and React Native
Formsy React -React and React Native
React Hook Form -React and React Native
Redux Forms -React and React Native
How to Validate Forms in React Native — a Personal Approach
with react-native-form-validation
Validating Forms in React Native
with validate.js
感謝張晴惠同學提供以下範例:
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)