การสร าง project reactt native ท ม อย แล ว

หลังจากหายไปนานกับงานเขียนเนื่องด้วยภาระหลายๆอย่าง วันนี้มีเวลาว่างนิดหน่อยจึงมีโอกาสได้มาเขียนสรุปการสร้าง Project mobile cross platform ด้วย ReactNative + MobX + Router flux โดยจะเขียนยาววว เลยไม่แบ่งย่อยนะครับ(เป็นบทความที่ยาวที่สุดที่เคยเขียนมาครับ)

Concept

สำหรับในบทความจะเป็นการสร้างและตั้งค่าProject โดยจะมีการจำลองการทำงานในส่วนต่างๆบ้าง เพื่อเป็นพื้นฐานในการต่อยอดทำแอพอื่นๆต่อไปในอนาคต ซึ่งในบทความนี้จะใช้

  1. ReactNative 0.50.4
  2. MobX 3.3.2
  3. MobX-React 4.3.5
  4. Router-Flux 4 (Based on React Navigation)
  5. Xcode 8.3.3
  6. MacOS 10.12.5

สำหรับใครที่ยังไม่เคยมีพื้นฐานด้านการทำ ReactNative เลยแนะนำให้ไปอ่านบทความเก่าๆก่อนนะครับ เพื่อปูพื้นให้เข้าใจมากยิ่งขึ้นครับ

สิ่งที่ควรศึกษาเบื้องต้น

  1. Javascript ES6
  2. ความรู้เบื้องต้นเกี่ยวกับ โครงสร้างการทำงานของ ReactNative
  3. แนะนำว่าควรลง Yarn ซึ่งเป็น package manager for JavaScript เช่นเดียวกับ npm ซึ่งในความรู้สึกส่วนตัว เราคิดว่า Yarn ทำงานเร็วกว่า npm มาก.. สำหรับการติดตั้งสามารถดูได้ที่นี่ครับ Yarn Install

Create Project

เริ่มจาก เปิด Terminal หรือ Cmd.exe แล้วไปที่ Path ที่ต้องการสร้าง Project

หลังจากนั้นทำการสร้างProject ด้วยคำสั่ง react-native init ตามด้วยชื่อProject เช่น

react-native init CellSpeedTest

เมื่อทำการสร้าง Project เสร็จแล้วจะแสดงดังรูป

จากนั้นทำการเข้าไปที่กล่องProject ที่เราสร้าง โดยใช้คำสั่ง cd ตามด้วยชื่อProject

ตรงนี้เป็นอันเสร็จในการสร้างProject ReactNative แล้วใช้เวลาไปไม่ถึง 30วินาที

Install Package

ตรงนี้เราจะทำการลงตัว Package ต่างๆที่จะให้เป็นพื้นฐานของแอพที่จะสร้างนะครับโดยจะทำการลงทั้งหมด 8Package

ซึ่งเมื่อก่อนนั้นส่วนใหญ่เวลาเราจะลง Package เราจะใช้

1.Router-Flux

router-flux เป็น Package ที่ช่วยในการจัดการเรื่องกระบวนการในการเปลี่ยนหน้าจอ คิดง่ายๆว่า เวลาจะต้องการเปลี่ยนจาก หน้าจอLogin ไปเป็นหน้าจอหลัก ในการใช้งานปกติแล้ว จะมีกระบวนการทำานวุ่นวายมากๆ ซึ่งเจ้า router-flux จะมาช่วยให้กระบวนการดังกล่าว ง่ายและเป็นระบบมากขึ้น

จัดการลง Package router-flux โดยใช้คำสั่ง yarn add

yarn add react-native-router-flux

ใช้เวลาประมาณ 8 วินาทีนิดๆก็เป็นอันเสร็จ

2.MobX

สำหรับ MobX เป็น Package ที่ช่วยในการจัดการเรื่องข้อมูลภายในแอพเรานะครับสำหรับรายละเอียดเพิ่มเติมสามารถอ่านได้ ตรงนี้ครับ

จัดการลง Package MobX โดยใช้คำสั่ง yarn add

yarn add mobx

3.Mobx-React

สำหรับ MobX-React เป็น Package ที่ช่วยให้ตัว ReactNative เองสามารถใช้งาน MobX ได้อย่างสมบูรณ์

จัดการลง Package MobX-React โดยใช้คำสั่ง yarn add

yarn add mobx-react

รอจนลงเสร็จใช้เวลาประมาณ 5 วินาที

4.react-native-elements

สำหรับ react-native-elements เป็น Package ที่รวบรวม UI Toolkit ต่างๆไว้ เพื่อให้เราสามารถนำมาใช้งานได้สะดวกรวดเร็ว(ไม่ต้องมาทำเอง 555) เช่นการออกแบบ button ต่างๆ โดยสามารถดู elements ทั้งหมดได้ที่นี่ครับ react-native-elements

จัดการลงอย่างไว

yarn add react-native-elements

5.react-native-vector-icons

สำหรับใครที่รู้สึกว่าเวลาทำแอพครั้งนึงการหา Icon เป็นเรื่องวุ่นวานมาก เราขอแนะนำ react-native-vector-icons เป็น Package ที่รวบรวม Icons ต่างๆไว้ เพื่อให้เราสามารถนำมาใช้งานได้สะดวกรวดเร็ว(ไม่ต้องมาทำเองอีกแล้วววว) โดยสามารถดู Icons ทั้งหมดได้ที่นี่ครับ react-native-vector-icons

ทำการลง package

yarn add react-native-vector-icons

**ข้อสำคัญ หลังจากทำการลง react-native-vector-icons เสร็จแล้ว ต้องทำการเชื่อมตัว Binary ของ react-native-vector-icons เข้ากับตัวproject เราด้วยคำสั่ง

react-native link react-native-vector-icons

6.prop-types

สำหรับ prop-types เป็น Package ที่ช่วยทำการ validators ประเภทของข้อมูลให้ครับ

yarn add prop-types

7.autobind-decorator

autobind-decorator เป็น Package ที่ช่วยในการจัดการเรื่องกระบวนการในการ bind(this) ให้อัตโนมัติ ซึ่งหลายคนอาจจะสงสัยว่า bind(this) คืออะไร

ต้องย้อนกลับไปถึงตัว javascript(js) เองจะมี this ให้เรียกใช้งานโดย this นั้นแทนค่าบางอย่างซึ่งขึ้นอยู่กับว่าใครเป็นคนเรียกใช้งานมัน เช่น

<button id="1" onClick="alert(this.id)">B1</button> <button id="2" onClick="alert(this.id)">B2</button>

จะเห็นว่า Code ทั้ง 2 บรรทัดมีการเรียกใช้งาน this.id เหมือนกัน

แต่เมื่อ B1ถูกคลิก this ตัวนี้จะหมายถึง Tag button B1 ทั้งหมด เนื่องจาก มันถูกเรียกใช้งานโดยการกดผ่าน Event onClick บน button B1 ดังนั้นเมื่อ แสดงค่า this.id มาจะเป็นค่า id ของตัว Tag button B1 เองคือ 1

แล้วทีนี้มันก็มีปัญหาตามมาคือ แล้วถ้าเป็นรูปแบบนี้ล่ะ

let obj={ a:5, abc(){ return this.a } }function fgh(fn) { alert(fn()) }alert(obj.abc()) fgh(obj.abc)

จากด้านบนเมื่อเรียก alert(obj.abc()) หมายความว่าเราเป็นคนที่เรียก functionผ่านทาง obj เองดังนั้น this จะหมายถึง obj เมื่อทำการแสดงค่า จึงแสดงค่า 5 ออกมากลับกันเมื่อเรียก function fgh โดยส่งค่า obj.abc เพื่อให้ fgh เป็นผู้เรียกใช้งาน this จะไม่เท่ากับกับ obj จึงทำให้ค่าที่แสดงออกมากเป็น undefined

จึงทำให้ต้องมีการทำการ bind() ขึ้นมาเพื่อให้รู้ว่า this หมายถึงใคร

yarn add autobind-decorator

8.babel-plugin-transform-decorators-legacy

babel-plugin-transform-decorators-legacy เป็น Package ที่ช่วยทำให้ Package ที่เขียนในรูปแบบเก่าสามารถใช้งานได้ไม่มีปัญหา

yarn add babel-plugin-transform-decorators-legacy

หลังจากนั้นทำการตั้งค่า

โดยทำการแก้ไขไฟล์ .babelrc ในproject

จาก

{

“presets”: [“react-native”]

}

เป็น

{

“presets”: [

“react-native”

],

“plugins”: [“transform-decorators-legacy”],

“sourceMaps”: true

}

Project structure

การวางแผนโครงสร้างของproject แต่ละคนจะมีการวางโครงสร้างในรูปแบบขอตัวเอง อาจจะเหมือนหรือไม่เหมือนกันก็ได้ สำหรับเบื้องต้นจะวางโครงสร้างโดยการแยก components,model,image ไว้คนละส่วนครับ

เริ่มต้นสร้างกล่อง app ก่อน

ภายในกล่อง app สร้าง กล่อง components,model,images

สร้าง Model ด้วย MobX

Model ตรงนี้มีไว้จัดเก็บโครงสร้างและกระบวนการต่างๆที่กระทำกับข้อมูลภายในแอพ ตัวอย่างเช่น แอพนี้เป็นเก็บที่ไว้ทดสอบความเร็ว Internet อย่างน้อยที่สุด ควรจะมีการออกแบบโครงสร้างข้อมูลประกอบไปด้วย

  1. เครือข่ายที่ใช้
  2. ระบบฎิบัติการ (Android,iOS)
  3. ความเร็วที่ทดสอบได้

ทีนี้เวลาจะมาสร้างเป็น Model รูปแบบ MobX จะทำได้ดังนี้

1.สร้างไฟล์ personal.js

2.ทำการเรียกใช้งาน lib ของ package MobX และ autobind-decorator

import {observabl} from ‘mobx’; import autobind from ‘autobind-decorator’

3.เรียกใช้ autobind

@autobind

4.สร้างClass

class PersonalStore { }

5.สร้างโครงสร้างข้อมูล

@observable carrier = ‘’

@observable os = ‘’

@observable speed_now = 0

6.สร้างfunction สำหรับจัดการข้อมูล โดยมีทั้งหมด 3 function คือ

  • การใส่ค่าให้ตัว carrier เมื่อมีการเรียกใช้งานจะส่งค่า carrier มาให้เพื่อให้จัดเก็บไว้
  • การใส่ค่าให้ตัว os เมื่อมีการเรียกใช้งานจะส่งค่า os มาให้เพื่อให้จัดเก็บไว้
  • การใส่ค่าให้ตัว speed_now เมื่อมีการเรียกใช้งานจะส่งค่า speed มาให้เพื่อให้จัดเก็บไว้
    setCarrier(c){

this.carrier=c

}

setOS(o){

this.os=o

}

setSpeed(s){

this.speed_now=s

}

7. กระบวนการสุดท้ายทำการ export class ไปใช้งาน

export default new PersonalStore()

สร้างหน้าหลัก

ต่อมาเราจะมาสร้างหน้าแรกของแอพเรากัน โดยปกติแล้วหน้าแรกจะถูกสร้างมาอยู่แล้วตอนสร้างproject โดยสร้างไว้ที่ไฟล์ App.js ซึ่งตรงนี้เราจะย้ายข้อมูลในไฟล์นี้มาสร้างใหม่ภายในกล่อง components

เริ่มด้วยการสร้างไฟล์ชื่อ PageA.js

หลังจากนั้นทำการ คัดลอก ข้อความทั้งหมดในไฟล์ App.jsมาใส่

ทำการเปลี่ยนชื่อ Class เป็น PageA

หลังจากนั้น ไปที่ไฟล์ index.js แก้ไขส่วน import

จากเดิมชี้ไปที่ไฟล์ App.js ให้ชี้มาที่ไฟล์ PageA.js ที่เราสร้าง

ของเดิมของใหม่

จากนั้นเปลี่ยนข้อความส่วนของ Render ใน PageA ให้เหลือแค่

return (

<View style={styles.container}>

<Text style={styles.welcome}>

Welcome to PageA!

</Text>

</View>

);

หลังจากนั้นทดสอบ run บน simulator ด้วยคำสั่ง

สำหรับ MacOS

react-native run-ios

สำหรับ Windows

react-native run-android

สำหรับ Windows ต้องทำการเปิด AVD ไว้ก่อนนะครับแล้วค่อยรันคำสั่ง โดยต้องเป็น Version ดังภาพนะครับ

https://facebook.github.io/react-native/docs/getting-started.html

โดยเมื่อrun สำเร็จแล้วจะได้ดังภาพ

ตั้งค่า Navigation

มาถึงส่วนนี้จะเป็นการสร้าง Class หลักมีหน้าที่คอยจัดการ Navigationโดยการนำ Package router-flux มาช่วยในการทำ Navigation

1.สร้างไฟล์ MainApp.js

ทำการสร้างไฟล์ MainApp.js ในกล่อง components

2.เรียกใช้ Lib ที่จำเป็น

ทำเรียกใช้ lib ต่างๆที่จำเป็นจาก Package ต่างๆ

import React, { Component } from ‘react’;

import { AppRegistry,StyleSheet,Text,View } from ‘react-native’;

import { Router, Stack, Scene, Actions, ActionConst } from ‘react-native-router-flux’;

import { observer } from ‘mobx-react/native’

3.สร้างClass และ extends Component

class CellSpeedTest extends Component { }

4.ปรับให้ Class เป็น observer

ที่ต้องปรับให้ class เป็น observer เพราะว่า เมื่อมีค่าใดๆใน Model PersonalStore มีการเปลี่ยนแปลง ค่าที่เราดึงมาใช้ภายในหน้านี้ก็จะเปลี่ยนไปเป็นค่าเดียวกันโดยอัตโนมัติ

ถ้าไม่ปรับเมื่อครั้งแรกหน้านี้แสดงผลค่าใดออกมาจากModel จะแสดงค่านั้นไปตลอดไม่เปลี่ยนแปลง

observer(class CellSpeedTest extends Component { })

5.Export Class ไปใช้งาน

เนื่องจากClassนี้ เป็นClassหลักจึงทำการ Export เป็นค่า default ไว้นะครับ

export default observer(class CellSpeedTest extends Component { })

6.Function พื้นฐาน

มาถึงตรงนี้เราจะสร้าง function พื้นฐานสำหรับการใช้งานนะครับ โดยจะประกับไปด้วย

  • componentDidMount จะทำงานเมื่อ class นี้ถูกเรียกใช้งาน
  • componentWillUnmount จะทำงานเมื่อออกจาก class นี้
  • render ทำหน้าที่นำ code ต่างๆที่เราเขียนขึ้น ไปแสดงผลบนหน้าจอ จะทำงานส่วนนี้ทุกครั้งเมื่อค่าของ Modelที่เรียกใช้ และค่าของ state เปลี่ยนแปลง
    componentDidMount() {

}

componentWillUnmount() {

}

render() {

return (null)

}

6.ตั้งค่า Navigation ในส่วน Render

ตรงนี้เราจะมาตั้งค่าเบื้องต้นของการทำ Navigation

ทำการแก้ส่วน render โดยทำการเพิ่ม ส่วนจัดการ navigation เข้าไปดังนี้

render() {

return (

<Router wrapBy={observer} duration={200}>

<Stack key=”root”>

</Stack>

</Router>

)

}

ส่วนนี้ประกอบไปด้วย

1.Router เป็นส่วน การเรียกใช้ lib ของ router-flux โดยมีการตั้งค่า 2 ส่วน

  • ส่วนแรก wrapBy={observer} จำเป็นต้องมีเพื่อให้หน้าต่างๆสามารถเข้าถึงการเปลี่ยนแปลงของ Model ได้
  • ส่วนที่สอง duration={200} เป็นความเร็วในการเปลี่ยนหน้าจอแต่ละหน้า

2.Stack เป็นส่วนที่ใช้จัดกลุ่มของหน้าจอ

3.Scene เป็นส่วนหน้าจอแต่ละหน้าที่มีในแอพ

ต่อมาจะทำการเพิ่ม Scene โดยจะใช้ PageA เป็น Scene หลัก

เริ่มต้นด้วยการ import หน้า PageA

ต่อมาทำการสร้าง Scene

โดยใน Scene ประกอบไปด้วย

  1. key ชื่อเวลาจะใช้เรียกเปิดหน้านี้ขึ้นมา สามารถตั้งอะไรก็ได้
  2. component เป็นการตั้งค่าว่า Scene จะนำหน้าใดมาแสดงผล ซึ่งตรงนี้เรา import PageA โดยใช้ชื่อว่า pagea จำนำค่า pagea มาใส่ใน component
  3. title เป็นส่วนใช้ตั่งค่า title ของหน้าจอนั้นว่าจะให้แสดงค่าอะไร
  4. initial เป็นการกำหนดว่า Scene เป็น หน้าแรกเมื่อเปิดแอพมาใช่หรือไม่

ต่อมาทำการกลับไปแก้ไขไฟล์ PageA.js เนื่องจากตอนนี้มี MainApp มาเป็น default Class แล้ว

โดยทำการแก้จาก

export default

export default

เป็น

module.exports =

สุดท้ายทำการแก้ไฟล์ index.js ให้ชี้ไปที่ MainApp.js แทน PageA.js

จากนั้นทดลอง Run จะเป็นดังภาพ

ใช้งาน Navigation

ในส่วนนี้เราจะทดลองใช้งาน navigation เพื่อเปลี่ยนจากหน้า PageA ไป หน้า PageB

สร้างหน้าจอใหม่

ทำการคัดลอกไฟล์ PageA.js แล้วเปลี่ยนชื่อเป็น PageB.js

จากนั้นทำการแก้ไขชื่อ Class แล้วข้อความ จาก PageA เป็น PageB

ทำการบันทึกไฟล์เป็นอันจบ

ตั้งค่า Navigation

เมื่อมีการเพิ่มหน้าจอใหม่ แล้วต้องการนำหน้าจอนั้นมาใช้งาน ต้องมาทำกระบวนการดังต่อไปนี้เสมอ

1.ทำการเปิดไฟล์ MainApp.js มาแก้ไข

2.ทำการ Import ไฟล์หน้าจอใหม่เข้ามา

import pageb from ‘./PageB’;

3.ทำการเพิ่ม scene ในส่วนของ Router

<Scene key=’pageb’ component={pageb} title=’Demo PageB’ />

ทำการบันทึก

ทดสอบเรียกใช้งาน หน้า PageB

การทดสอบนั้นเราจะทำการสร้าง button อันนึงบนหน้า PageA เมื่อทำการกด button นั้นแล้วจะทำการเปลี่ยนหน้าจอจาก PageA ไป PageB

1.ทำการเปิดไฟล์ PageA.js มาแก้ไข

2.ทำการ import ตัว Actions,ActionConst จาก lib router-flux เพื่อไว้ใช้ในการเปลี่ยนหน้าจอ

import { Actions,ActionConst } from ‘react-native-router-flux’

3.สร้าง function เพื่อทำการ เรียกใช้งาน Actions เพื่อเปลี่ยนจากหน้าจอ PageA ไป PageB

goToB() {

Actions.pageb()

}

จากด้านบนจะเห็นว่า เราได้สร้าง function ชื่อ goToB โดยfunctionนี้ ทำการเรียกใช้คำสั่ง Actions

คำสั่ง Actions เป็นคำสั่งที่ใช้ในการเปบี่ยนหน้าจอ ซึ่งมีวิธีการเรียกใช้คือ พิมพ์ Actions.ตามด้วย key ของ scene ที่ต้องการจะไป

ซึ่งตัวอย่างด้านบนคือเราต้องการจะไปที่ scene ที่มี key คือ pageb ดังรูปด้านล่าง

4.import Button จาก lib elements มาใช้

import { Button } from ‘react-native-elements’

5.ทำการสร้าง button เพื่อเรียกใช้ function goToB

<Button

title={`ไปหน้า B`}

onPress={this.goToB}

/>

ทำการบันทึกแล้วทดสอบกด button ดูครับ

เมื่อกด button จะได้ดังภาพ

นำ Model personal มาใช้งาน

เมื่อตอนช่วงต้นเรามีการสร้าง model personal ไว้ ตอนนี้เราจะนำmodel นี้มาใช้งานโดยจะเอาค่า speed_now มาแสดงผล และทำ button ไว้สำหรับเพิ่มค่า speed_now เพื่อทดสอบการทำงาน observer ของ MobX

1.import model

เนื่องจากเรามีการใช้ navigation ในการจัดการหน้าจอต่างๆ เราจึงต้องทำการส่งค่าของ model ผ่านไปทาง Router ด้วยครับ

เริ่มต้นด้วยการเปิดไฟล์ MainApp.js มาแก้ไข

ทำการ import model ตั้งชื่อว่า pstore

import pstore from ‘../model/personal’;

2.ส่งค่าของ model ผ่าน Router

personalstore={pstore}

จากด้านบนจะเห็นว่าเราส่งค่า pstore ไปในชื่อ personalstore เวลาหน้าจอต่างๆเรียกใช้งาน model นี้จะเรียกใช้ผ่าน personalstore

3.เปลี่ยน Class ให้เป็น observer

ที่จำเป็นต้องเปลี่ยน class เป็น observer เพราะเมื่อค่าของข้อมูลใน Model มีการเปบี่ยนแปลง หน้าจอนั้นจะทำการ renderใหม่อัตโนมัติ

1.เปลี่ยน PageA

ทำการ import observer จาก mobx-react/native

import { observer } from ‘mobx-react/native’;

ทำการเปิดไฟล์ PageA.js มาแก้ไข โดยทำการครอบ class ด้วย observer()

2.เปลี่ยน PageB

ทำเช่นเดียวกับ PageA

4.นำ speed_now แสดงผล

การเรียกใช้งานmodel จะเรียกใช้งานผ่าน this.props ตามด้วยชื่อที่ใช้ ตามด้วยตัวแปรที่ต้องการดึงค่า เช่น

this.props.personalstore.speed_now

จะเห็นว่ามีการเรียก this.props ตัวด้วย .personalstore.speed_now เพื่อดึงค่าขอ speed_now

ทีนี้ลองมาเพิ่มบนหน้าจอ PageA โดยจะทำการแสดงค่าแทนข้อความเดิม Welcome to PageA! จะได้ดังนี้

<Text style={styles.welcome}>

PageA! {this.props.personalstore.speed_now}

</Text>

จากด้านบนจะสังเกตุเห็นว่า การนำค่าต่างๆไปแสดงใน Text จะต้องครอบด้วยปีกกา { } เสมอนะครับ

ทดสอบrun ดูจะได้ผลดังนี้

5.ทำ button และ function เพื่อจัดการค่า speed_now

มาถึงตรงนี้เราจะจำลองกระบวนการใช้งาน model ว่า modelนี้เวลามีค่าเปลี่ยนแปลงจะส่งผลถึงหน้าจอต่างๆอย่าไร

เพิ่ม function AddSpeed

AddSpeed(s) {

let nows = this.props.personalstore.speed_now + s

this.props.personalstore.setSpeed(nows)

}

จากกระบวนการด้านบน จะเห็นว่า function นี้มีการรับตัวแปร 1 ตัวคือ s

หลังจากนั้นจะทำการสร้าง ตัวแปร nows ที่มีค่าเท่ากับ ค่า speed_now ณ ปัจจุบัน บวกกับค่า s

จากนั้นทำการเรียกใช้งาน functon setSpeed ของmodel personal แล้วโดยทำการส่งค่า nows ไป

ต่อมาทำการเพิ่ม button เพื่อเรียกใช้ function AddSpeed

<Button

title={`Speed +1`}

onPress={()=>{this.AddSpeed(1)}}

/>

จะเห็นว่าเมื่อกดที่ปุ่มจะมีการเรียกใช้ function AddSpeedและส่งค่า 1 ให้function

บันทึกและทดสอบRun ดู จะพบว่าทุกครั้งที่กดปุ่มค่าจะแสดงเพิ่มขึ้นทีละ 1 เสมอ

6.ทดลองนำค่า speed_now ไปแสดงที่ PageB

ตรงนี้เราจะลองนำค่า speed_now ไปแสดงที่ PageB เพื่อให้เห็นว่าค่าของ Model จะเหมือนกันในทุกๆหน้าที่เรียกใช้

เปิดไฟล์ PageB.js แก้ไขส่วนข้อความจากเดิมเป็น Welcome to PageB! แก้เป็น

PageB! {this.props.personalstore.speed_now}

บันทึกและทดสอบRunดูผล จะเห็นว่าค่าที่แสดงทั้งสองหน้าคือค่าเดียวกัน

7.ทดลองนำobserver ที่ PageA ออก

ส่วนนี้จะช่วยให้เข้าใจกระบวนการ observer มากยิ่งขึ้นโดยให้ลองนำ observer ที่ครอบ class PageA ออกแล้วทดสอบ Run ดู

ผลที่ได้จะเห็นว่าไม่ว่ากดปุ่ม speed+1 กี่ครั้งค่า speed_now ก็ไม่แสดง กลับกันถ้าลองเปลี่ยนไปที่หน้า PageB ค่ากลับแสดงออกมา