จัดการแหล่งข้อมูล

เนื่องจากเนื้อหาในบทความตอนนี้มีขนาดยาวมาก ดังนั้น ผมจึงได้แนบ github code มาให้ เพื่อใช้สำหรับประกอบการทำความเข้าใจ ในกรณีที่ไม่อยากจะเขียน code ตามทีละบรรทัด

https://github.com/himaeng/albums.git

จากภาพ mockup

screen-shot-2559-11-21-at-6-57-44-pm

ในส่วนของ Card มีสองงานที่เราต้องทำให้เสร็จ นั่นคือ

  1. จัดการเรื่องการดึงข้อมูลจาก remote source
  2. สร้าง list ของ album ทั้งหมด

ซึ่งเราจะมาแก้กันทีละข้อ ดังนี้

1. ดึงข้อมูลจาก remote source

สำหรับข้อมูลของตัว card นั้น ในที่นี้เราจะดึงจากตัว API สำเร็จรูป ผ่าน url ดังนี้

http://rallycoding.herokuapp.com/api/music_albums

หากลองเข้าไปดูตาม link ข้างต้น จะเห็นรายการ album data ใน format ของ json ดังรูป

screen-shot-2559-11-24-at-7-48-05-am

หมายเหตุ: หากต้องการให้ chrome แสดงผล JSON ให้สวยงามดังภาพ ให้ติดตั้ง JSONVIEW ซึ่งเป็น extensions ให้กับ chrome 

2. สร้าง List ของอัลบั้มทั้งหมด

ก่อนอื่นเรามาดูกันก่อนเลยว่า mockup ของหน้า album list มีหน้าตาเป็นแบบไหน ดังรูป

screen-shot-2559-11-24-at-7-51-57-am

จากรูปข้างต้น จะมี 1 container ที่ชื่อ AlbumList ที่ภายในบรรจุ component ที่ชื่อ AlbumDetail หลายๆ ตัว

เริ่มจัดสถาปัตย์ของ App

ก่อนจะไปต่อ เรามาดูกันก่อนดีกว่าว่า component แต่ละตัวที่เรากำลังจะสร้างต่อไปนี้ มีสายสัมพันธ์กันอย่างไรบ้าง

screen-shot-2559-11-24-at-8-57-51-am

ระบบของเรามี root component ชื่อว่า App โดย component ดังกล่าวจะบรรจุไปด้วย สอง component ย่อย นั่นคือ Header และ AlbumList ซึ่งตัว AlbumList จะทำหน้าที่ในการแสดงรายการของ AlbumDetail ออกมาทางหน้าจอ อีกทั้งยังต้องทำหน้าที่ในการดึง Album Data จาก Remote API ด้วย

ดังนั้น ขั้นแรกให้เราสร้าง AlbumList component ขึ้นมาก่อน โดยให้สร้างไฟล์ใหม่ชื่อว่า AlbumList.js ขึ้นมา ดังนั้น โครงสร้างไฟล์ของเราจะเป็นดังนี้

โดยในไฟล์นั้น ให้เขียน code ดังนี้

จากนั้นให้เปิดไฟล์ index.ios.js ขึ้นมาแล้วแก้ code ดังนี้

เราได้นำ AlbumList มาวางไว้ใน App ต่อจาก Header ตามที่เราได้ออกแบบเอาไว้ก่อนหน้านี้ แต่มีอยู่จุดหนึ่งที่ผมอยากเน้นตรงนี้คือ ผมจะครอบตัว JSX ด้วย <View> </View> สาเหตุเป็นเพราะ component แต่ละตัวนั้น เวลาจะ return JSX tag ออกมานั้น เราต้อง return ออกมาโดยมี root tag ตัวเดียวเท่านั้น ดังนั้น หากเราเขียน code แบบนี้

React จะฟ้อง error ขึ้นมาทันที เพราะมันไม่ยอมให้ return JSX แบบไม่มี root tag

จากนั้นให้ทำการ refresh หน้า emulator (หากยังไม่เปิด ก็ให้เข้าไปที่ terminal แล้วรันคำสั่ง react-native run-ios) เพื่อดูว่ามี error อะไรหรือไม่ หากไม่มีปัญหาอะไร เราจะเจอรูปดังนี้

screen-shot-2559-11-24-at-9-47-18-am

Refactor AlbumList

จากภาพสถาปัตย์คร่าวๆ ของระบบ albums จะเห็นได้ว่า ตัว AlbumList component นั้นจะต้องทำหน้าที่ในการ fetching data จาก remote api ซึ่ง ปัญหาของ AlbumList ตอนนี้ก็คือ มันถูกเขียนขึ้นมาแบบ Stateless Function ซึ่งไม่เหมาะกับการทำงานที่มีความ dynamic มากๆ อย่าง component ที่ต้องมีการ fetching data แบบนี้

ดังนั้น เราจะต้องทำการ refactor ให้ component ตัวนี้มีลักษณะของ Class component ซึ่งความแตกต่างระหว่าง stateless functional กับ class นั้น เป็นไปตามตารางเปรียบเทียบดังต่อไปนี้

Stateless Functional Class Component
  • ใช้เพื่อจุดประสงค์แสดงข้อมูลแบบ static
  • ไม่เหมาะกับการงานแบบ fetching data
  • เขียนง่าย
  • ใช้เพื่อจุดประสงค์ในการแสดงข้อมูลที่มีแหล่งที่มาแบบ dynamic
  • เหมาะกับการจัดการข้อมูลที่มีการเปลี่ยนแปลงตลอด
  • จัดการกับ life cycle ได้ง่าย
  • เขียน code มากกว่า
const Header = () => {
return Hi there!
}
class Header extends Component {
render() {
return Hi There!
}
}

ให้เปิดไฟล์ src/components/AlbumList.js ขึ้นมาแล้วแก้ไฟล์ตามนี้

ติดตั้ง axios

อย่างที่รู้กัน เมื่อใดที่เราจะ request remote api เรามักจะใช้ axios module มาเป็นตัวช่วย ดังนั้นเราจึงควรทำการติดตั้ง module ตัวนี้ให้เรียบร้อย ด้วยการรันคำสั่งต่อไปนี้ ณ ตำแหน่งตรง root project

จากนั้นให้ทำการ import axios เข้าไปที่ AlbumList ดังนี้

เป็นการ import axios เข้ามาแล้วนำมาใช้ภายใน componentDidMount ทันที โดยเมื่อ axios สามารถ get ข้อมูลจาก api ได้แล้ว ก็จะมีการสั่งรัน callback function ภายใน .then (ซึ่งเราเขียนในรูปแบบ arrow function สังเกตมั๊ยครับว่า คราวนี้ผมไม่ใส่วงเล็บที่ argument response เพราะ arrow function ไม่บังคับให้ใส่วงเล็บหากมี argument แค่ตัวเดียว) โดยภายใน callback ดังกล่าวทำการดัก console.log ไว้ เพื่อดูว่า api return อะไรมา

คำถามคือ ตัว console.log นี้หากมาอยู่บนเว็บก็ไม่มีปัญหาอะไร เพราะแค่เปิด browser แล้วดู console ก็จบแล้ว แต่พอเป็น mobile นี่เป็นอีกเรื่องนึงเลยนะ

React Native ได้เตรียมเครื่องมือให้เราสามารถแสดงข้อมูลผ่าน console ได้แบบเดียวกับ web ซึ่งสามารถทำได้โดยกดปุ่ม Command + D สำหรับ iOS Simulator หรือCommand + M สำหรับ Android Simulator เพื่อเปิดเมนู ดังรูป

screen-shot-2559-11-24-at-11-34-18-am

ให้เลือก Debug JS Remotely จากนั้นจะมีการเปิด browser ขึ้นมา เมื่อเราเปิดดู console จะเห็นข้อมูล log ดังนี้

screen-shot-2559-11-24-at-11-48-01-am

จะเห็นว่า ข้อมูลที่คืนมานั้น จะมีข้อมูลหลายตัว แต่ที่สำคัญคือ status ที่แจ้งสถานะของ request นี้ว่า success หรือไม่ และตัว data ที่เป็น array ของข้อมูลที่เราต้องการ

คราวนี้เรามาดูกันต่อว่า เวลาเราดึงข้อมูลมาแล้ว จะนำมาใช้อะไรต่อ

screen-shot-2559-11-24-at-11-55-20-am

จากรูปจะเห็นว่า เราจะ render album ตาม state ที่ชื่อว่า albums เมื่อใดที่เปลี่ยน state หน้าจอจะเปลี่ยนไปด้วย ดังนั้น เราจะนำข้อมูลที่ได้มาจาก remote api มาใช้สำหรับการ render หน้าจอ โดยให้เราแก้ code ภายใน src/components/AlbumList.js ดังนี้

ในที่นี้ เราจะทำการ initial state ที่ชื่อว่า albums ให้มีค่าเป็น empty array ก่อน จากนั้นหลังจากที่เราได้รับการ return จาก remote api แล้ว ก็จะทำการ setState ให้ albums state มีค่าเท่ากับ response.data

เมื่อเราทำการ refresh หน้าจอ emulator ตัว console ใน browser ก็จะแสดงข้อมูลของ albums จาก response ออกมา

Rules of State

ก่อนจะไปต่อ เรามาทำความเข้าใจกฎการใช้งาน State ภายใน React กันก่อนดีกว่า

  1. state คือ object ที่ใช้สำหรับเก็บข้อมูล และสามารถเปลี่ยนแปลงไปตามปฏิสัมพันธ์ที่เกิดขึ้นระหว่างผู้ใช้ กับอุปกรณ์
  2. เมื่อใดที่เราต้องการจะแก้ไขข้อมูลภายใน state ให้ใช้คำสั่ง this.setState ห้ามใช้คำสั่ง this.state = blah เด็ดขาด

Album Rendering

ได้เวลาเอาข้อมูลใน state.albums มาใช้งานแล้ว วิธีการก็คือ เราจะนำ album แต่ละตัวภายใน state.albums มาใช้สำหรับ render รายการแต่ละตัวภายใน AlbumList component ดังแผนภาพต่อไปนี้

screen-shot-2559-11-24-at-1-08-47-pm

จะเห็นว่า เราจะทำการ map ข้อมูล album หนึ่งตัว ต่อการ render หนึ่ง component ดังนั้น เราจะต้องทำการนำ state.albums มา map ตามแนวทางข้างต้น โดยการเปิดไฟล์ src/components/AlbumList.js ขึ้นมาแล้วแก้ code ดังนี้

โดยฟังก์ชั่น renderAlbums จะทำการนำ state.albums มา map ให้คืนค่าเป็น JSX ทีละ record ซึ่งหากสังเกตดีๆ ฟังก์ชั่น map จะทำการแสดง album ทีละตัวให้เราโดยอัตโนมัติเพียงแค่เรียกใช้งานฟังก์ชั่น renderAlbums ภายใน <View></View>

ซึ่งเมื่อทำการ refresh หน้าจอ emulator ใหม่ เราจะเห็นภาพดังนี้

screen-shot-2559-11-24-at-1-18-36-pm

แต่จะเห็นว่า ด้านล่างของ emulator มีคำเตือนไว้ว่า

ซึ่งเตือนให้เรารูว่า เราต้องใส่ค่า key ให้กับค่า JSX ทุกตัวที่ return มาจากฟังก์ชั่น renderAlbums และที่สำคัญ value ของ key แต่ละตัวต้องไม่ซ้ำกันด้วย สิ่งนี้จะช่วยให้ performance ของ React ดียิ่งขึ้น ดังนั้น React จึงกำหนดไว้เป็นมาตรฐานของ JSX เพื่อคอยเตือนความจำให้กับเรา

ให้เราเปิดไฟล์ src/components/AlbumList.js ขึ้นมาแล้วแก้ code ดังนี้

ในที่นี้ เราใส่ key attribute เข้าไปที่ <Text> แล้วกำหนดค่าให้เป็น album.title เพื่อการันตีว่า ค่าที่ใส่เข้าไปใน key จะไม่ซ้ำกับ record ตัวอื่น

คราวนี้เมื่อเราลอง refresh emulator ตัว warning จะหายไป

จากนั้นให้เราสร้าง component ตัวใหม่ที่ชื่อว่า AlbumDetail.js ขึ้นมา ใน path ดังนี้

จากนั้นให้เพิ่ม code ดังนี้

เราสร้าง AlbumDetail component ด้วยวิธี stateless functional เพราะจุดประสงค์ของ component ตัวนี้มีเพียงแค่ใช้เพื่อ render static data จาก props เท่านั้น ดังนั้น stateless functional จึงเหมาะกับมันแล้ว

จากนั้นให้เปิดไฟล์ src/components/AlbumList.js ขึ้นมาแล้วแก้ code เพื่อนำ AlbumDetail มาใช้งานภายในตัวของมัน ดังนี้

เราเปลี่ยนจากการใช้ <Text> มาเป็น <AlbumDetail> แทน

AlbumDetail component design

หลังจากที่เราได้เห็นไปแล้วในตอนต้นว่า หน้าตาของ AlbumDetail component มีหน้าตาประมาณไหน ตอนนี้ได้เวลาเขียน code เพื่อสร้างมันขึ้นมา โดยเราสามารถออกแบบโครงคร่าวๆ ได้ดังนี้

screen-shot-2559-11-24-at-6-54-20-pm

เราจะแบ่ง component ตัวนี้ออกเป็น 3 ส่วน ดังภาพข้างต้น แล้วแต่ละส่วนก็ใช้ <View> ครอบเอาไว้

แต่เพื่อเป็นการทำให้ component ของเรามีความ reusability มากขึ้น เราจะปรับให้องค์ประกอบของ component ตัวนี้เป็นดังนี้

screen-shot-2559-11-24-at-7-00-06-pm

แทนที่เราจะใช้ <View> ไปตรงๆ เราเลือกใช้ <CardItem> และ <Card> เพื่อประโยชน์ในเรื่องของการปรับ style เพราะเราจะปรับแค่ครั้งเดียว แล้วจากนั้นก็เอาไปใช้ได้ทันทีโดยไม่ต้องปรับ style อีก

นั่นแปลว่า ภายใน AlbumDetail จะประกอบไปด้วย <Card> และ <CardItem> component

CardItem component

ให้เราสร้างไฟล์ Card.js เข้าไปใน albums/src/components/Card.js ทำให้ตอนนี้โครงสร้างไฟล์เราจะเป็นดังนี้

โดยให้เติม code ลงไปในไฟล์นี้ ดังนี้

นี่คือเหตุผลที่ทำไมเราจึงต้องสร้าง component ขึ้นมาเพื่อเก็บ style ไว้กับตัวเอง เพราะใน React Native เวลาจะสร้าง style ต้องสร้างภายในไฟล์เดียวกับ component นั้นไปเลย ดังนั้น เพื่อความสะดวกในการนำ style มา reuse เราจึงมักนิยมใช้ component ในการเก็บ style

ให้เปิดไฟล์ albums/src/components/AlbumDetail.js แล้วปรับ code ดังนี้

ถือเป็นการ import Card มาใช้ใน AlbumDetail component

จากนั้นให้กลับมาที่ Card.js แล้วใส่ code มหัศจรรย์ ดังต่อไปนี้

งานนี้คือจะเป็นการนำ ทุกๆ สิ่งที่อยู่ภายใต้ <Card></Card> ที่เขียนไว้ใน AlbumDetail component มาแสดง ซึ่งในที่นี้ก็คือ

คราวนี้ได้เวลาไปทดสอบกับ emulator จริงกันแล้ว

หลังจาก refresh แล้ว คุณจะเห็นภาพดังต่อไปนี้

screen-shot-2559-11-24-at-7-59-11-pm

CardSection component

ให้เพิ่มไฟล์ components ใหม่ชื่อว่า CardSection.js ขึ้นมา ทำให้ตอนนี้โครงสร้างไฟล์ของโปรเจ็กท์จะเป็นดังนี้

จากนั้นให้เติม code เข้าไปดังนี้

จะเห็นว่ารายละเอียดของ code นั้นไม่ต่างจาก Card component ซักเท่าไหร่ ดังนั้นผมขอเว้นไว้ไม่อธิบายนะครับ ยกเว้น code หนึ่งบรรทัด

อันนี้เป็น code สำหรับ flexbox ในการเปลี่ยนทิศทางของการจัดตำแหน่งในหน้าจอ

หากยังจำกันได้ flexbox ภายใน React Native นั้นจะมี property สองตัว คือ justifyContent สำหรับจัดตำแหน่ง component ในแนวดิ่ง และ alignItems สำหรับจัดตำแหน่ง component ในแนวระนาบ

flexDirection นั้น จะเซตได้สองค่า กล่าวคือ ‘column’ และ ‘row’ ซึ่ง โดย default แล้วค่าของ flexDirection นั้นคือ ‘column’ แต่หากเมื่อใดเราเซตให้ค่าของ flexDirection มีค่าเป็น ‘row’ เมื่อนั้น จะเกิดการสลับคุณสมบัติของ justifyContent และ alignItems ดังนี้

justifyContent จะกลายเป็นตัวจัดตำแหน่ง component ในแนวระนาบ (จากเดิมแนวดิ่ง)
alignItems จะกลายเป็นตัวจัดตำแหน่ง component ในแนวดิ่ง (จากเดิมแนวระนาบ)

ใช้งาน CardSection

เปิดไฟล์ albums/src/components/AlbumDetail.js แล้วปรับ code ดังนี้

ถือเป็นการทดลองใช้งาน CardSection เพื่อตรวจสอบขั้นต้นว่า ใช้งานได้อย่างไม่มีปัญหาหรือไม่ ดังนั้น ให้ลองเปิด emulator แล้วลอง refresh ดู เพื่อตรวจสอบว่ามันทำงานได้อย่างปรกติหรือไม่

Flexbox ต่อ…

ก่อนหน้านี้เราเคยพูดถึงการจัดตำแหน่งของ Flexbox สำหรับ component ตัวเดียว คราวนี้เราลองมาดูกันว่า หากเอา Flexbox มาจัดตำแหน่ง component ภายใน CardSection แล้วจะมีหน้าตาเป็นยังไง

เริ่มต้นจาก ตัว CardSection นั้น จะมี design ดังนี้

screen-shot-2559-11-24-at-8-57-17-pm

คือจะมี image และ text สองตัว โดยเรียงตัวกันตามรูปข้างบน

คำถามคือ หากเอา flexbox มาจัดตำแหน่ง component ทั้งสามตัวภายใต้ CardSection แล้วหน้าตาจะเป็นไง

screen-shot-2559-11-24-at-9-05-52-pm

หากไม่จัดอะไรเลย flexbo จะจัดทุกอย่างไปอัดกันอยู่ที่มุมบนซ้าย ซึ่งหากเราเซตให้เป็น ‘flex-start’ ก็จะได้ผลลัพธ์เหมือนกัน

screen-shot-2559-11-24-at-9-06-19-pm

หากเซต justifyContent ให้มีค่าเป็น center ก็จะเป็นแบบภาพที่เห็น

screen-shot-2559-11-24-at-9-06-33-pm

และแน่นอนว่า หากเซตค่าเป็น flex-end ทุกอย่างก็จะลงมากองกันอยู่ที่มุมล่างขวา

screen-shot-2559-11-24-at-9-07-39-pm

ยังมี space-between ที่จะแทรกช่องว่างระหว่าง component โดย flexbox จะแบ่งให้แต่ละ component วางในตำแหน่งห่างเท่าๆ กัน

screen-shot-2559-11-24-at-9-08-06-pm

และตัวสุดท้าย space-around ซึ่งจะมีคุณสมบัติคล้ายๆ กับ space-between ต่างกันตรงที่ flexbox จะเสริมช่องว่างด้านบน และล่างของ container ดังรูป

ยังจำ flexDirection กันได้มั๊ยครับ เรามาดูตัวอย่างจริงกันเลยว่าเจ้า flexDirection ตัวนี้ใช้งานกันอย่างไร

screen-shot-2559-11-24-at-9-29-48-pm

โดยปรกติแล้ว justifyContent นั้นจะใช้สำหรับจัดตำแหน่ง component ต่างๆ ในแนวดิ่ง แต่หากเมื่อใดที่เราเซตให้ flexDirection มีค่าเท่ากับ row มันจะเปลี่ยนไปจัดการกับแนวระนาบทันที

คราวนี้ลองเอาความรู้ข้างต้นมาวางแผนการสร้าง CardSection กันดูหน่อยเป็นไร

screen-shot-2559-11-24-at-9-30-31-pmจากภาพคือ ภายใน CardSection จะวาง View ไว้สองตำแหน่ง โดยตำแหน่งแรกไว้สำหรับ ครอบ Image ส่วนตำแหน่งที่สองไว้สำหรับครอบ Text สองตัว

การจัดตำแหน่งของ View ทั้งสองตัวนี้ เราจะจัดแบบแนวนอน ดังนั้น เราก็จะต้องปรับค่า flexDirection ให้เท่ากับ row แล้วเซตค่า justifyContent ให้มีค่า flex-start เพื่อให้ component ทั้งสองเรียงตัวไปทางซ้ายของจอภาพ

screen-shot-2559-11-24-at-9-31-26-pm

คราวนี้ภายใน View ที่ครอบ Text ทั้งสองตัว เราจะจัดระเบียบให้ Text ทั้งสองเรียงกันในแนวดิ่ง ดังนั้น flexDirection ต้องมีค่าเท่ากับ column และเนื่องจากเราต้องการให้มี space ล้อมรอบ text ทั้งสองตัว จึงเซตค่า justifyContent ให้เท่ากับ space-around

ลุยสร้างของจริง

ได้เวลาเขียน code กันแล้ว เปิดไฟล์ AlbumDetail.js ขึ้นมาเลยครับ แล้วแก้ code ดังนี้

จุดพิเศษของ code ข้างบนนี้คือ เรามีการทำ destructuring กันเยอะหน่อย ตั้งแต่แตกเอา album จาก props โดยตรง จากนั้นเอา album มาแตกเอา { title, artist, thumbnail_image } อีกทั้งเรายังแตก styles ออกมาเพื่อลดจำนวน code ที่ต้องเขียนด้วย

เมื่อแก้เสร็จแล้ว ให้ลองไปที่ emulator แล้ว refresh ดู จะได้ตามรูปดังต่อไปนี้

screen-shot-2559-11-24-at-10-10-36-pm

ตกแต่งเพิ่มเติม

ยังอยู่ที่ไฟล์เดิมคือ AlbumDetail.js ให้ปรับ code ดังนี้

ตรงนี้เราตกแต่งเพิ่มสองอย่าง กล่าวคือ ทำให้เกิดช่องว่างระหว่าง thumbnail image กับ text ทั้งสองตัว และขยายขนาดของ font ของ album.title

แต่จุดพิเศษที่อยากเน้นจริงๆ คือตัว code ดังต่อไปนี้

งานนี้เราต้องการ render ภาพเต็ม บนหน้าจอ โดยต้องการกำหนดให้ด้านซ้ายและขวาของภาพอยู่ชิดติดขอบจอ วิธีการก็คือ การเซตค่า flex: 1 จะมีผลทำให้ความกว้างของภาพยืดไปจนสุดขอบของหน้าจอ และเราต้องเซตค่าให้ width เป็น null ประกอบด้วย

เมื่อปรับ code จนแล้วเสร็จ หลังจาก refresh emulator จะได้ภาพดังที่เห็น

screen-shot-2559-11-24-at-10-43-31-pm

ปัญหาคือ ตอนนี้หน้าจอไม่สามารถ scholl ได้ ดังนั้น ต้องทำการแก้ไข

ใช้ ScrollView เพื่อช่วยให้สามารถ scroll ได้

เนื่องจากเราต้องการทำให้หน้าจอ scroll ได้ ดังนั้น เราต้องจัดการที่ component ที่ทำหน้าที่เป็น container ของ component ทั้งหมดนั่นคือ AlbumList

ให้เปิดไฟล์ AlbumList.js ขึ้นมาแล้วแก้ไข code ดังนี้

code ข้างต้นได้ทำการเปลี่ยนจากการใช้ <View> มาเป็น <ScrollView> เพียงเท่านั้นหน้าจอของเราก็ scroll ได้แล้ว

หลังจากที่ refresh emulator แล้ว หากต้องการ scroll หน้าจอ ให้คลิกเม้าส์ค้างไว้ แล้วลากขึ้นลง ก็สามารถ scroll ได้

Button component

ฟีเจอร์สุดท้ายของโปรเจ็กท์ album คือ การเพิ่มปุ่มไปใน CardSection ส่วนสุดท้าย ดังนั้น ให้เราเพิ่มไฟล์ Button.js เข้าไปที่ src/components/Button.js ดังนั้น ตอนนี้ โครงสร้าง directory ของโปรเจ็กท์จะเป็นดังนี้

จากนั้นให้เพิ่ม code เข้าไปดังนี้

โดยครั้งนี้ เราใช้ component ที่ชื่อ TouchableOpacity เข้ามาทำหน้าที่เป็น ปุ่มที่เมื่อ user กดบนหน้าจอ ตัวปุ่มจะจางให้เห็นทันที ถือเป็น native component ตัวนึงที่แถมมากับ react-native module

จากนั้นให้เพิ่ม Button เข้าไปใน AlbumDetail component ดังนี้

จากนั้นให้ทำการ refresh emulator เพื่อดูว่ามันปรากฎตามอย่างรูปดังต่อไปนี้หรือไม่

screen-shot-2559-11-24-at-11-06-06-pm

ตกแต่ง Button

ได้เวลาแต่ง button ให้ดูดีขึ้นหน่อย โดยแก้ code ใน Button.js ดังนี้

เป็นการเพิ่ม style เข้าไปให้ปุ่ม โดยจุดที่น่าสนใจก็คือ attribute ที่ชื่อว่า alignSelf ที่ใช้ปรับให้ component นั้นๆ ยืดขยาย หรืออยู่ตรงกลางได้ เป็นอีก attribute หนึ่งที่มาพร้อมกับ Flexbox ของ React Native

ให้ทำการ Refresh emulator แล้วจะเห็นรูปนี้

screen-shot-2559-11-24-at-11-22-03-pm

Add Button’s onPress callback

ได้เวลาทำใส่ฟังก์ชั่นเข้าไปที่ปุ่มนี้เพื่อให้มันใช้งานจริงได้ซักที เปิดไฟล์ src/components/Button.js ขึ้นมาแล้วปรับ code ดังนี้

จาก code ข้างต้น เรารับ props เข้ามาใน Button โดยรับแบบ destructuring แล้วก็ตามระเบียบ ทุกครั้งที่เรารับ props เข้ามา ก็ควรจะทำ document ผ่านการใช้ PropTypes โดยเราจะรับ onPress เป็น function เพื่อนำมาใช้สำหรับ เป็น callback ของ onPress ของ TouchableOpacity component

ด้วยเหตุนี้ Button component จะเป็น component ที่มีความ generic ค่อนข้างมาก เพราะเราเปิดรับค่า prop onPress จาก component ที่เรียกใช้ ซึ่งก็ขึ้นอยู่กับว่า component ตัวนั้นจะส่ง callback อะไรมาเป็น prop

คราวนี้เรามาแก้ code ที่ต้นทางกันดีกว่า ให้เปิดไฟล์ src/components/AlbumDetail.js แล้วแก้ code ดังนี้

ในที่นี้เราจะใช้ module สำเร็จรูปของ React Native ที่ชื่อว่า Linking ซึ่งเป็นตัวช่วยให้เราสามารถ route ไปตาม url ต่างๆ ได้อย่างง่ายดาย โดยในที่นี้เราจะใช้ฟังก์ชั่น openURL() เพื่อเปิด link ขาย album ใน Amazon.com

ให้เราลอง refresh emulator แล้วคลิกปุ่มดูเพื่อทดสอบว่ามัน route ไปยัง amazon หรือไม่

Make Button to be more generic component

สังเกตมั๊ยครับว่า ตอนนี้เรายัง hard-coded ตัวอักษรบนปุ่มภายใน Button อยู่เลย งานนี้เรามาทำให้ Button มีความ generic มากขึ้นไปอีก ด้วยการทำให้ text บนปุ่มมีความ dynamic มากกว่านี้

ให้เปิดไฟล์ src/components/Button.js ขึ้นมา แล้วแก้ code ดังนี้

สังเกตมั๊ยครับว่า เราสามารถ destructuring เพื่อดึงเอาค่า children ออกมาจาก props ของ component ได้โดยตรง และงานนี้เราก็เอา props.children มาวางเป็นสิ่งที่ใช้แสดงบนปุ่ม

จากนั้นให้เปิดไฟล์ src/components/AlbumDetail.js ขึ้นมาแล้วแก้ไข ดังนี้

เพียงเท่านี้ albums app. ก็จะมีหน้าตาเป็นดังนี้

screen-shot-2559-11-25-at-4-02-49-pm

สรุป

จบไปแล้วนะครับสำหรับ app ตัวแรกของคอร์สนี้ (ยังมี app ตามมาอีกหลายตัว) จุดประสงค์ของ app ตัวนี้ก็คือ เพื่อให้ผู้อ่านทำความคุ้นเคยกับสภาพแวดล้อมของ React Native ด้วยการเขียน React ตามหลักการที่เราทุกคนเข้าใจอยู่แล้ว จากคอร์ส React Basic และ ES6 for React หากใครยังไม่ได้อ่านเนื้อหาจากทั้งสองคอร์สดังกล่าว ผมแนะนำให้กลับไปอ่านนะครับ เพราะคอร์ส React Native นี้ มีสมมติฐานว่า ผู้อ่านเข้าใจในหลักพื้นฐานของ React อยู่แล้ว ดังนั้น หาก concept ใดซ้ำซ้อนกับเนื้อหาใน Basic course จะได้รับการอธิบายแบบพอสังเขปเท่านั้น