Joy Kanyawee
Nov 5 · 9 min read

[Tutorial Mobile] สอนทำแอปพลิเคชันจองโรงแรม 123Hotel โดยใช้ ReactNative ft.Firebase🔥🔥🔥


1.ทำการติดตั้ง React Native

2.ทำการ Check ที่ cmd ก่อนว่ามี node,npm ,yarn หรือไม่ ถ้าไม่มีให้ทำการติดตั้ง โดยตัว npm จะมาพร้อมกับการติดตั้ง node.js อยู่แล้ว และทำการติดตั้ง yarn

3.ไปดาวน์โหลด Emulator Genymotion มา โดยเลือกตัวที่มี with VirtualBox และ Editor Atom หรืออะไรก็ได้ตามใจชอบ

4.ทำการ New Folder ที่ไหนก็ได้ ในที่นี้ได้ทำการ New Folder ชื่อ Hotel 123 ที่ไดร์ฟ D

5.ทำการสร้าง folder project ขึ้นมา โดยเข้าไปที่ folder Hotel123 และพิมพ์ react-native init ชื่อโปรเจค

6.เมื่อเพิ่ม project เสร็จจะขึ้นดังภาพด้านล่าง และเมื่อเข้าไปดูใน folder จะมีไฟล์เพิ่มเข้ามาในโปรเจค

7.ทำการเพิ่มไฟล์ local.properties ใส่ sdk.dir ที่อยู่ของ sdk ลงไป สามารถดูได้ที่ Android Studio

→เปิด Android Studio ขึ้นมา เลือก Configure ที่วงกลมแดง จากนั้นเลือก SDK Manager

นำ Android SDK Location ด้านบนมาใส่ใน ไฟล์ local.properties

→install ตัวที่เห็นในภาพเพิ่มเติม

8.เปิด Genymotion ขึ้นมาก่อน(เปิดก่อนที่จะรัน react-native run-android)

9.จากนั้นเปิด cmd ทำการ cd เขาไปใน project ชื่อ mob5921600172 และพิมพ์ react-native run-android กด enter เพื่อทำการ run

ปล.เราจะรัน react-native run-android ทุกครั้งเพื่อรันใช้งาน

10.ถ้าขึ้น Build Success ใน cmd และเปิดแอปขึ้นดังภาพด้านล่าง แสดงว่า create project สำเร็จ

11.ทำการเพิ่ม ใน cmd ดังนี้

→ yarn add react-navigation react-native-gesture-handler react-native-reanimated

→ yarn add react-navigation-stack

→เพิ่ม firebase

→ เพิ่ม DatePicker

→เพิ่ม ViewPager

→เพิ่ม react-native-picker-select

→เพิ่ม react-native-input-spinner

เปิด Atom ขึ้นมา Open Folder Project ของเราเข้ามา คลิกเปิดไฟล์ package.json จะเห็นว่าตรง dependencies มีตัวที่เราเพิ่มเข้าเรียบร้อยแล้ว

ดูตรง dependencies

12.เพิ่ม folder src ตามภาพด้านล่างที่ Project ของเรา

ปล. ที่ Folder ชื่อ img เป็นภาพห้องประเภทต่างๆ หาเองได้ตามชอบเลย

13.แก้ไขไฟล์ App.js ดังนี้

import { createAppContainer } from ‘react-navigation’;
import { createStackNavigator } from ‘react-navigation-stack’;
import HomeScreen from ‘./src/screens/HomeScreen’;
import ResultScreen from ‘./src/screens/ResultScreen’;
import BookScreen from ‘./src/screens/BookScreen’;
import ChangeScreen from ‘./src/screens/ChangeScreen’;

const navigator = createStackNavigator(
{
Home: HomeScreen,
Result: ResultScreen,
Book: BookScreen,
Change: ChangeScreen
},
{
initialRouteName: ‘Home’,
defaultNavigationOptions: {
title: ‘123 Hotel App’,
headerTitleStyle: {
fontWeight: ‘bold’,
color:’#fff’
},
headerStyle: {
backgroundColor: ‘#c5b358’,
fontFamily: ‘Vivaldi’
}
}
}
);
export default createAppContainer(navigator);


14.หน้า src/screens/HomeScreen.js

จะเป็นหน้าแรกของ App หน้านี้ไม่มีอะไรมาก จะเป็นเพียงการสร้าง หน้า App ใส่ Style ต่างๆ ละก็มีปุ่มเพื่อไปยังหน้า See BookingList(ดูรายการที่ทำการจอง) และหน้า Booking Room(จองห้องพัก)

import React from ‘react’;
import { Text, View, StyleSheet, Button, TouchableOpacity, ImageBackground } from ‘react-native’;

const HomeScreen = ({ navigation }) => {
return (
<ImageBackground source={{uri: ‘https://tse2.mm.bing.net/th?id=OIP.iG21Dk2quVDV--AiRZy-TgAAAA&pid=15.1'}} style={{width:’100%’, height:’100%’}}>

<View style={styles.viewStyle}>
<View style={{margin:40, marginBottom:-30}}>
<Button
title=”See BookingList”
onPress={() => navigation.navigate(‘Change’)}
color=’#c5b358'
/>
</View>

<View style={{margin:40}}>
<Button
title=”Booking Room”
// onPress={() => console.log(‘Button Pressed’)}
onPress={() => navigation.navigate(‘Book’)}
color=”#c5b358"
/>
</View>
<Text>{`\n`}</Text>
</View>
</ImageBackground>
);
}

const styles = StyleSheet.create({
textStyle: {
fontSize: 20,
textAlign: ‘center’
},
viewStyle: {
justifyContent: ‘center’,
paddingTop: 450,
paddingBottom: 140,
shadowColor: ‘#000’,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
elevation: 2
},
titleStyle: {
fontSize: 30,
textAlign: ‘center’
}
});

export default HomeScreen;

HomeScreen.js

15.หน้า src/screens/BookScreen.js

หน้านี้เป็นหน้า ที่จะทำการจองห้องพัก จะมีการ import Components หลายๆตัวเข้ามา ได้แก่ DatePicker, ViewPager, PickerSelect, InputSpinner เป็นต้น โดยจะมีการ set วันปัจจุบันใน DatePicker มีการคำนวณช่วงวัน Check-in, Check-out ว่าเป็นเวลากี่คืน และคำนวณค่าห้องสัมพันธ์กับจำนวนคืนที่พัก โดยประเภทห้องจะมี 3 ประเภทคือ Superior,Deluxe และSuite ราคาแต่ละประเภทจะแตกต่างกัน

import React, { Component } from ‘react’;
import { View, Text, Button, ImageBackground, Alert, StyleSheet, TextInput } from ‘react-native’;
import DatePicker from ‘react-native-date-picker’;
import ViewPager from “@react-native-community/viewpager”;
import RNPickerSelect from ‘react-native-picker-select’;
import moment from ‘moment’;
import InputSpinner from “react-native-input-spinner”;
import BookDetails from ‘../Components/BookDetails’;

let today = new Date();
const oneDay = 24 * 60 * 60 * 1000;

export default class BookScreen extends Component {
static navigationOptions = {
title: ‘123 Hotel App’,
headerStyle: {
backgroundColor: ‘#666699’
},
headerTitleStyle: {
fontWeight: ‘bold’,
color:’#deba7e’
}
};

constructor(props){
super(props)

this.state = {
indate: new Date().setDate(today.getDate()),
outdate: new Date().setDate(today.getDate()+1),
type: ‘Superior’,
guest: 1,
room: 1,
tel: ‘’,
name: ‘’,
price: 0
}
}

calPrice() {
if(this.state.type === ‘Superior’) {
return this.state.room*1600* Math.round(Math.abs((this.state.indate — this.state.outdate) / oneDay));
} else if (this.state.type === ‘Deluxe’) {
return this.state.room*2500* Math.round(Math.abs((this.state.indate — this.state.outdate) / oneDay));
} else if (this.state.type === ‘Suite’) {
return this.state.room*4000* Math.round(Math.abs((this.state.indate — this.state.outdate) / oneDay));
}
}

render = () =>
<ViewPager style={{flex:1}} initialPage={0}>
<View>
<ImageBackground style={{width:’100%’, height:’100%’, backgroundColor:’#101c4c’}}>
<View style={{alignItems: ‘center’,flex:1 ,margin:50, backgroundColor:’#e6e6e6',borderRadius: 10,borderWidth: 5,borderColor:’#deba7e’}}>
<Text>{`\n`}</Text>
<Text style={styles.textStyle}>Check — In</Text>
<DatePicker
style={{height: 80, width: 250, opacity: 1}}
date={this.state.indate}
onDateChange={(date) => this.setState({ indate:date })}
mode=’date’
fadeToColor=’#330066'
textColor=’#deba7e’
backgroundColor=’#24273a’
/>
<Text style={styles.textStyle}>{`\n`+’Check — Out’}</Text>
<DatePicker
style={{height: 80, width: 250, opacity: 1}}
date={this.state.outdate}
onDateChange={(date) => this.setState({ outdate:date })}
mode=’date’
fadeToColor=’#330066'
textColor=’#deba7e’
backgroundColor=’#24273a’
/>
<Text style={styles.textStyle}>{`\n`+’Guest’}</Text>
<InputSpinner
value={this.state.guest}
min={1}
max={30}
onMax={() => {
alert(“onMax reached!”);
}}
onMin={() => {
alert(“onMin reached!”);
}}
colorMax={“#f04048”}
colorMin={“#f04048”}
color={“#bfbfbf”}
height={40}
onChange={(num) => {
this.setState({
guest: num
})
}}
/>
<Text style={styles.textStyle}>{`\n`+’Room’}</Text>
<InputSpinner
value={this.state.room}
min={1}
max={30}
onMax={() => {
alert(“onMax reached!”);
}}
onMin={() => {
alert(“onMin reached!”);
}}
colorMax={“#f04048”}
colorMin={“#f04048”}
color={“#bfbfbf”}
rounded={false}
height={35}
onChange={(num) => {
this.setState({
room: num
})
}}
/>
</View>
</ImageBackground>
</View>

<View>
<ImageBackground style={{width:’100%’, height:’100%’, backgroundColor:’#e6e6e6'}}>
<RNPickerSelect
value={this.state.type}
onValueChange={(value) => this.setState({type:value})}
items={[
{ label: ‘Superior (1,600 baht/night)’, value: ‘Superior’ },
{ label: ‘Deluxe (2,500 baht/night)’, value: ‘Deluxe’ },
{ label: ‘Suite (4,000 baht/night)’, value: ‘Suite’ },
]}
/>
<ViewPager style={{width:412, height:250, backgroundColor:’#deba7e’}}>
<View>
<BookDetails type=”Superior” imgSource={require(‘../img/superior.jpg’)} />
</View>
<View>
<BookDetails type=”Deluxe” imgSource={require(‘../img/deluxe3.jpg’)} />
</View>
<View>
<BookDetails type=”Suite” imgSource={require(‘../img/suite.jpg’)} />
</View>
</ViewPager>

<View style={{flex:1 ,margin:10,borderRadius: 10,borderWidth: 5,borderColor:’#101c4c’}}>
<Text>{`\n`}</Text>
<View style={{flexDirection:’row’,justifyContent:’center’,marginTop:-15}}>
<Text style={{fontFamily: ‘Vivaldi’, fontSize:24}}>Name:</Text>
<TextInput
value={this.state.name}
onChangeText={name => this.setState({ name })}
placeholder={‘Enter Your Name’}
style={styles.textInput}
/>
</View>
<View style={{flexDirection:’row’,justifyContent:’center’}}>
<Text style={{fontFamily: ‘Vivaldi’, fontSize:24}}>Phone:</Text>
<TextInput
value={this.state.tel}
onChangeText={tel => this.setState({ tel })}
placeholder={‘Enter Your Phone’}
style={styles.textInput}
keyboardType=”number-pad”
/>
</View>
</View>

<View style={{margin:40}}>

<Button
title=”Submit”
onPress={() =>
{this.props.navigation.navigate(‘Result’, {
indate: this.state.indate,
outdate: this.state.outdate,
type: this.state.type,
guest: this.state.guest,
room: this.state.room,
name: this.state.name,
tel: this.state.tel,
night: Math.round(Math.abs((this.state.indate — this.state.outdate) / oneDay)),
price: this.calPrice()
})}
}
color=”#f04048"
/>
</View>
</ImageBackground>
</View>
</ViewPager>
}

const styles = StyleSheet.create({
textStyle: {
fontSize: 23,
textAlign: ‘center’,
fontFamily: ‘Vivaldi’
},
textInput: {
height: 40,
width: “50%”,
borderColor: “#deba7e”,
borderWidth: 3,
fontSize:16,
margin:8
}
});

BookScreen.js(1)
BookScreen.js(2)

16.หน้า src/screens/ResultScreen.js

หน้านี้จะเป็นหน้าที่แสดงรายละเอียดรายการที่จะทำการจอง มีปุ่ม Comfirm Booking เพื่อยืนยันการจอง เมื่อกดปุ่มจะแสดง Alet ว่า จองสำเร็จ โดยรายการจะถูกเพิ่มเข้าไปใน database

import React from ‘react’;
import { StyleSheet, View, Text, Button, Alert, ImageBackground } from ‘react-native’;
import moment from ‘moment’;
import Firebase from ‘../Components/Firebase’;
import firebase from ‘firebase’;

export default class ResultScreen extends React.Component {
static navigationOptions = {
title: ‘Confirm Booking’,
headerStyle: {
backgroundColor: ‘#666699’
},
headerTitleStyle: {
fontWeight: ‘bold’,
color:’#deba7e’
}
};

constructor(props){
super(props)
const { navigation } = this.props;
this.state = {
indate: navigation.getParam(‘indate’, ‘null value’),
outdate: navigation.getParam(‘outdate’, ‘null value’),
type: navigation.getParam(‘type’, ‘null value’),
guest: navigation.getParam(‘guest’, ‘null value’),
room: navigation.getParam(‘room’, ‘null value’),
name: navigation.getParam(‘name’, ‘null value’),
tel: navigation.getParam(‘tel’, ‘null value’),
night: navigation.getParam(‘night’, ‘null value’),
price: navigation.getParam(‘price’, ‘null value’)
}
}

componentDidMount() {
Firebase.init();
// if (!firebase.apps.length) {
// firebase.initializeApp(firebaseConfig);
// }
}
writeUserData(){
firebase.database().ref(‘BookingList/’).push({
name: this.state.name,
phone: this.state.tel,
checkin: moment(this.state.indate).format(“YYYY MMM DD”),
checkout: moment(this.state.outdate).format(“YYYY MMM DD”),
type: this.state.type,
guest: this.state.guest,
room: this.state.room,
night: this.state.night,
price: this.state.price
}).then(()=>{
//success callback
console.log(‘inserted’)
}).catch((error)=>{
//error callback
console.log(error)
});
Alert.alert(‘Booking successfully!’);
}

render() {
const { navigation } = this.props;
const type = navigation.getParam(‘type’, ‘some default value’);
const indate = navigation.getParam(‘indate’, ‘some default value’);
const outdate = navigation.getParam(‘outdate’, ‘some default value’);
const guest = navigation.getParam(‘guest’, ‘some default value’);
const room = navigation.getParam(‘room’, ‘some default value’);
const name = navigation.getParam(‘name’, ‘some default value’);
const tel = navigation.getParam(‘tel’, ‘some default value’);
const night = navigation.getParam(‘night’, ‘null value’);
const price = navigation.getParam(‘price’, ‘null value’);
return (
<ImageBackground style={{width:’100%’, height:’100%’, backgroundColor:’#101c4c’}}>
<View style={{alignItems: ‘center’,flex:1 ,margin:50, backgroundColor:’#e6e6e6',borderRadius: 10,borderWidth: 5,borderColor:’#deba7e’}}>
<View style={{ flex: 1, alignItems: “center”, justifyContent: “center” }}>
<Text style={{ marginTop: 16,marginBottom:16,fontSize: 28, fontFamily:’Vivaldi’}}>
— Your Booking -
</Text>
<Text style={styles.textStyle}>Check-in: {moment((indate)).format(“YYYY MMM DD”)}</Text>
<Text style={styles.textStyle}>Check-out: {moment((outdate)).format(“YYYY MMM DD”)}</Text>
<Text style={styles.textStyle}>Type: {JSON.stringify(type)}</Text>
<Text style={styles.textStyle}>Guest: {JSON.stringify(guest)}</Text>
<Text style={styles.textStyle}>Room: {JSON.stringify(room)}</Text>
<Text style={styles.textStyle}>Night: {JSON.stringify(night)}</Text>
<Text style={styles.textStyle}>Total Price: {JSON.stringify(price)}</Text>
<Text style={styles.textStyle}>Name: {JSON.stringify(name)}</Text>
<Text style={styles.textStyle}>Phone: {JSON.stringify(tel)}</Text>

<Text style={{ marginTop: 16,fontSize: 20, fontFamily:’Vivaldi’}}>
Please confirm this booking for successfully
</Text>
<View style={{margin:40, marginBottom:-20}}>
<Button
title=”Confirm Booking”
onPress={() => this.writeUserData()}
color=’#f04048'
/>
</View>
<View style={{margin:40, marginBottom:-20}}>
<Button
title=”HomeScreen”
onPress={() => navigation.navigate(‘Home’)}
color=’#bb33ff’
/>
</View>
<View style={{margin:40}}>
<Button
title=”Go Back”
onPress={() => this.props.navigation.goBack()}
color=’#00cc99'
/>
</View>
</View>
</View>
</ImageBackground>
);
}

}
const styles = StyleSheet.create({
textStyle: {
fontSize: 16,
textAlign: ‘center’,
color: ‘#000’,

}
});

ResultScreen.js

เมื่อกด Confirm Booking ข้อมูลการจองจะถูกเพิ่มเข้าไปใน database


17.หน้า src/screens/ChangeScreen.js

หน้านี้จะเป็นหน้าแสดงรายการที่ทำการจองห้อง โดยได้ import ตัว Components ListDetails เพื่อมาแสดงรายการ การจองห้อง

import React, { Component } from ‘react’;
import { View, Text, StyleSheet } from ‘react-native’;
import ListDetails from ‘../Components/ListDetails’;
import firebase from ‘firebase’;
import Firebase from ‘../Components/Firebase’;

export default class ChangeScreen extends Component {
static navigationOptions = {
title: ‘Change Booking’,
headerStyle: {
backgroundColor: ‘#666699’
},
headerTitleStyle: {
fontWeight: ‘bold’,
color:’#deba7e’
}
};

state = {
items: []
};
componentDidMount() {
Firebase.init();
firebase.database().ref(‘/BookingList’).on(‘value’, snapshot => {
let data = snapshot.val();
let items = Object.values(data);
this.setState({ items });
});
}

render() {
return (
<View style={styles.container}>
{this.state.items.length > 0 ? (
<ListDetails items={this.state.items} />
) : (
<Text>No items</Text>
)}
</View>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: ‘center’,
backgroundColor: ‘#ebebeb’
},
textStyle: {
fontSize: 16,
textAlign: ‘center’,
color: ‘#000’,

}
});

ChangeScreen.js

18.หน้า src/Components/BookingDetails.js

เป็นหน้าที่ส่งพวก View ไปยังหน้า BookScreen จะได้เรียกใช้ Components ที่เดียวได้เลย

import React from ‘react’;
import { Text,View,StyleSheet ,Image ,TouchableOpacity} from ‘react-native’;

const BookDetails = (props) => {
return (
<View>
<Text style={styles.txtTitle}>{props.type}
</Text>
<Image source={props.imgSource}
style={styles.imgStyle}
/>
</View>
);
}
const styles = StyleSheet.create({
txtTitle:{
fontSize: 30,
fontFamily:’Vivaldi’,
textAlign:’center’,
color:’#101c4c’
},
txtColor:{
fontSize: 15,
textAlign: ‘center’,
color: ‘#ff3333’
},
imgStyle:{
width: 412,
height: 245,
resizeMode: “contain”
}
});

export default BookDetails;


19.หน้า src/Components/Firebase.js

ก่อนอื่นไปที่ Web Firebase ก่อน

→เลือกตัวที่วงกลมสีแดง

→ทำการลงทะเบียนแอป

→ Copy SDK เพื่อไปใส่ในไฟล์ของเรา

→ตั้งกฏเป็นดังภาพด้านล่าง

→เพิ่ม code ตามด้านล่างแต่เปลี่ยนเป็น Firebase ของเราเอง

import firebase from ‘firebase’;
var firebaseConfig = {
apiKey: “XXXX”,
authDomain: “XXXX”,
databaseURL: “https://XXXX",
projectId: “XXXX”,
storageBucket: “”,
messagingSenderId: “XXXX”,
appId: “XXXXX”
};
export default class Firebase {
static init(){
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
}
}


20.หน้า src/Components/ListDetails.js

เป็นหน้ารูปแบบที่จะแสดงรายละเอียดรายการจองห้องที่เรียกมาจาก database

import React, { Component } from ‘react’;
import { View, Text, StyleSheet, ScrollView } from ‘react-native’;
import PropTypes from ‘prop-types’;
import firebase from ‘firebase’;
import Firebase from ‘../Components/Firebase’;

export default class ListDetails extends Component {
static propTypes = {
items: PropTypes.array.isRequired
};
componentDidMount() {
Firebase.init();
}

render() {
return (
<ScrollView>
<View>
{this.props.items.map((item,index) => {
return (
<View style={styles.itemsList} key={item.name}>
<Text style={styles.itemtext}>Name : {item.name}</Text>
<Text style={styles.itemtext}>Check-In,Check-Out : {item.checkin},{item.checkout}</Text>
<Text style={styles.itemtext}>Type: {item.type}</Text>
<Text style={styles.itemtext}>Room: {item.room}</Text>
<Text style={styles.itemtext}>Guest: {item.guest}</Text>
</View>
);
})}
</View>
</ScrollView>
);
}
}

const styles = StyleSheet.create({
itemsList: {
flex: 1,
flexDirection: ‘column’,
justifyContent: ‘space-around’,
borderWidth: 3,
borderRadius: 5,
margin:10,
padding:5,
borderColor: ‘#deba7e’
},
itemtext: {
fontSize: 16,
fontWeight: ‘bold’,
textAlign: ‘center’
}
});


Joy Kanyawee

Written by

KanV eat easy easy🐻

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade