บทนำสู่บริบทของ React API
เผยแพร่แล้ว: 2022-03-10สำหรับบทช่วยสอนนี้ คุณควรมีความเข้าใจเกี่ยวกับ hooks อย่างยุติธรรม ก่อนที่เราจะเริ่มต้น ฉันจะพูดถึงสั้น ๆ ว่ามันคืออะไรและตะขอที่เราจะใช้ในบทความนี้
ตามเอกสาร React:
“ Hooks เป็นส่วนเสริมใหม่ใน React 16.8 พวกเขาให้คุณใช้สถานะและคุณสมบัติ React อื่น ๆ โดยไม่ต้องเขียนคลาส”
นั่นคือสิ่งที่ React hook เป็น ช่วยให้เราใช้ state, refs และคุณสมบัติ React อื่นๆ ในองค์ประกอบการทำงานของเรา
ให้เราพูดถึงสอง hooks ที่เราจะเจอในบทความนี้
การใช้งาน useState
Hook
ตะขอ useState ช่วยให้เราใช้สถานะ ในส่วนประกอบการทำงานของเรา ตะขอ useState
ใช้ค่าเริ่มต้นของสถานะของเราเป็นอาร์กิวเมนต์เดียว และจะส่งกลับอาร์เรย์ของสององค์ประกอบ องค์ประกอบแรกคือตัวแปรสถานะของเรา และองค์ประกอบที่สองคือฟังก์ชันที่เราสามารถใช้อัปเดตค่าของตัวแปรสถานะได้
ลองมาดูตัวอย่างต่อไปนี้:
import React, {useState} from "react"; function SampleComponent(){ const [count, setCount] = useState(0); }
ที่นี่การ count
คือตัวแปรสถานะของเราและค่าเริ่มต้นของมันคือ 0
ในขณะที่ setCount
เป็นฟังก์ชันที่เราสามารถใช้เพื่ออัปเดตค่าของการนับ
useContext
Hook
ฉันจะพูดถึงเรื่องนี้ในบทความต่อไป แต่โดยทั่วไปแล้วเบ็ดนี้ ช่วยให้เราใช้ คุณค่าของบริบทได้ สิ่งนี้หมายความว่าจริง ๆ จะชัดเจนขึ้นในบทความต่อไป
พื้นที่ทำงานเส้นด้าย
พื้นที่ทำงานของ Yarn ช่วยให้คุณจัดระเบียบ Codebase ของโปรเจ็กต์โดยใช้ที่เก็บแบบเสาหิน (monorepo) React เป็นตัวอย่างที่ดีของโครงการโอเพ่นซอร์สที่เป็น monorepo และใช้พื้นที่ทำงานของ Yarn เพื่อให้บรรลุเป้าหมายนั้น อ่านบทความที่เกี่ยวข้อง →
เหตุใดเราจึงต้องการ Context API
เราต้องการสร้างองค์ประกอบ "ตัวสลับธีม" ซึ่งสลับระหว่างโหมดสว่างและโหมดมืดสำหรับแอป React ของเรา ทุกองค์ประกอบต้องมีสิทธิ์เข้าถึงโหมดธีมปัจจุบันเพื่อให้สามารถจัดรูปแบบได้ตามนั้น
โดยปกติ เราจะจัดเตรียมโหมดธีมปัจจุบันให้กับส่วนประกอบทั้งหมดผ่านอุปกรณ์ประกอบฉาก และอัปเดตธีมปัจจุบันโดยใช้ state
:
import React from "react"; import ReactDOM from "react-dom"; function App() { return ( <div> <Text theme= "blue" /> <h1>{theme}</h1> </div> ); } function Text({theme}) { return( <h1 style = {{ color: `${theme}` }}>{theme}</h1> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
ในตัวอย่างโค้ดด้านบน เราได้สร้างส่วนประกอบข้อความซึ่งแสดงองค์ประกอบ h1
สีขององค์ประกอบ h1
ขึ้นอยู่กับโหมดธีมปัจจุบัน ปัจจุบันธีมเป็นสีน้ำเงิน เราสามารถสลับระหว่างธีม blue
และ red
โดยใช้ state
เราจะสร้างสถานะที่เรียกว่า "ธีม" โดยใช้ useState
hook useState
hook จะคืนค่าปัจจุบันของธีมและฟังก์ชันที่เราสามารถใช้อัปเดตธีมได้
ให้เราสร้างสถานะธีมของเรา:
const [theme, setTheme] = React.useState("blue");
เราจะเพิ่มองค์ประกอบปุ่มลงในองค์ประกอบ App
ของเราด้วย ปุ่มนี้จะใช้เพื่อสลับธีมและต้องใช้ตัวจัดการเหตุการณ์การคลิก ให้เราเขียนตัวจัดการเหตุการณ์การคลิกดังนี้:
const onClickHandler = () => { setTheme(); }
ตอนนี้ เราต้องการตั้งค่าธีมใหม่เป็น Red
หากธีมปัจจุบันเป็น Blue
และในทางกลับกัน แทนที่จะใช้คำสั่ง if
วิธีที่สะดวกกว่าคือใช้ตัวดำเนินการ ternary ใน JavaScript
setTheme( theme === "red"? "blue": "red");
ตอนนี้เราได้เขียนตัวจัดการ onClick
ของเราแล้ว มาเพิ่มองค์ประกอบปุ่มนี้ให้กับองค์ประกอบ App
:
<button onClick = {onClickHandler}>Change theme</button>
ให้เราเปลี่ยนค่าของชุดรูปแบบขององค์ประกอบข้อความเป็นสถานะของชุดรูปแบบ
<Text theme={theme}/>
ตอนนี้เราควรจะมีสิ่งนี้:
import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { const[theme, setTheme] = React.useState("red"); const onClickHandler = () => { setTheme( theme === "red"? "blue": "red"); } return ( <div> <Text theme={theme}/> <button onClick = {onClickHandler}>Change theme</button> </div> ); } function Text({theme}) { return( <h1 style = {{ color: `${theme}` }}>{theme}</h1> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
ตอนนี้เราสามารถสลับไปมาระหว่างสองธีมของเราได้แล้ว อย่างไรก็ตาม หากแอปพลิเคชันนี้มีขนาดใหญ่กว่ามาก ก็คงเป็นเรื่องยากที่จะใช้ธีมนี้ในส่วนประกอบที่ซ้อนกันอย่างลึกล้ำ และโค้ดจะกลายเป็นเทอะทะ
ขอแนะนำ Context API
ให้ฉันแนะนำบริบท API ตามเอกสาร React:
“บริบทเป็นช่องทางในการส่งข้อมูลผ่านโครงสร้างส่วนประกอบโดยไม่ต้องผ่านอุปกรณ์ประกอบฉากด้วยตนเองในทุกระดับ”
สำหรับคำจำกัดความในเชิงลึกยิ่งขึ้น ข้อมูลดังกล่าวจะมอบวิธีให้คุณทำให้ข้อมูลเฉพาะพร้อมใช้งานกับส่วนประกอบทั้งหมดทั่วทั้งโครงสร้างองค์ประกอบ ไม่ว่าองค์ประกอบนั้นจะซ้อนกันอยู่ลึกเพียงใด
ให้เราดูตัวอย่างนี้:
const App = () => { return( <ParentComponent theme = "light"/> ); } const ParentComponent = (props) => ( <Child theme = {props.theme} /> ) const Child = (props) => ( <Grandchild theme = {props.theme} /> ) const Grandchild = (props) => ( <p>Theme: {props.theme}</p> )
ในตัวอย่างข้างต้น เราระบุธีมของแอปพลิเคชันโดยใช้อุปกรณ์ประกอบฉากใน ParentComponent
ที่เรียกว่า theme
เราต้องส่งต่ออุปกรณ์ประกอบฉากนั้นไปยังส่วนประกอบทั้งหมดบนแผนผังส่วนประกอบเพื่อให้ได้มาซึ่งที่จำเป็นซึ่งเป็นส่วนประกอบ GrandChild
ChildComponent
ไม่มีส่วนเกี่ยวข้องกับอุปกรณ์ประกอบฉากของธีม แต่ถูกใช้เป็นตัวกลางเท่านั้น
ตอนนี้ ลองนึกภาพองค์ประกอบ GrandChild
ซ้อนกันอย่างลึกซึ้งกว่าในตัวอย่างด้านบน เราจะต้องส่งอุปกรณ์ประกอบฉากธีมแบบเดียวกับที่เราทำที่นี่ซึ่งจะยุ่งยาก นี่คือปัญหาที่ Context
แก้ไขได้ ด้วย Context
ทุกองค์ประกอบในแผนผังองค์ประกอบจะสามารถเข้าถึงข้อมูลใดก็ตามที่เราตัดสินใจใส่ในบริบทของเรา
มาเริ่มกันเลยกับ Context
ได้เวลาจำลองปุ่มสลับธีมที่เราสร้างไว้ตอนต้นของบทความด้วย Context API คราวนี้ ตัวสลับธีมของเราจะเป็นองค์ประกอบที่แยกจากกัน เราจะสร้างองค์ประกอบ ThemeToggler
ซึ่งเปลี่ยนธีมของแอป React ของเราโดยใช้ Context
ขั้นแรก ให้เราเริ่มต้นแอป React ของเรา (ฉันชอบใช้ create-react-app
แต่คุณสามารถใช้วิธีใดก็ได้ที่คุณต้องการ)
เมื่อคุณเริ่มต้นโปรเจ็กต์ React แล้ว ให้สร้างไฟล์ชื่อ ThemeContext.js ในโฟลเดอร์ /src
ของคุณ คุณยังสามารถสร้างโฟลเดอร์ชื่อ /context
และวางไฟล์ ThemeContext ไว้ที่นั่นได้หากต้องการ
เอาล่ะ ไปกันต่อเลย
การสร้าง Context API ของคุณ
เราจะสร้างบริบทของธีมในไฟล์ ThemeContext.js
ในการสร้างบริบท เราใช้ React.createContext
ซึ่งสร้างวัตถุบริบท คุณสามารถส่งผ่านอะไรก็ได้ที่เป็นอาร์กิวเมนต์ไปยัง React.createContext
ในกรณีนี้ เราจะส่งผ่านสตริงซึ่งเป็นโหมดธีมปัจจุบัน ดังนั้นตอนนี้โหมดธีมปัจจุบันของเราคือโหมดธีม "สว่าง"
import React from "react"; const ThemeContext = React.createContext("light"); export default ThemeContext;
เพื่อให้บริบทนี้ใช้ได้กับส่วนประกอบ React ทั้งหมดของเรา เราต้องใช้ผู้ให้บริการ ผู้ให้บริการคืออะไร? ตามเอกสารของ React ทุกอ็อบเจ็กต์บริบทมาพร้อมกับส่วนประกอบ Provider React ที่อนุญาตให้ส่วนประกอบที่ใช้งานสมัครรับการเปลี่ยนแปลงบริบท เป็นผู้ให้บริการที่ช่วยให้บริบทถูกใช้โดยส่วนประกอบอื่นๆ ที่กล่าวว่าให้เราสร้างผู้ให้บริการของเรา
ไปที่ไฟล์ App.js ของคุณ ในการสร้างผู้ให้บริการ เราต้องนำเข้า ThemeContext
ของเรา
เมื่อนำเข้า ThemeContext
แล้ว เราต้องใส่เนื้อหาขององค์ประกอบ App
ของเราในแท็ก ThemeContext.Provider
และให้ส่วนประกอบ ThemeContext.Provider
เรียกว่า value
ซึ่งจะประกอบด้วยข้อมูลที่เราต้องการให้พร้อมใช้งานในแผนผังองค์ประกอบของเรา
function App() { const theme = "light"; return ( <ThemeContext.Provider value = {theme}> <div> </div> </ThemeContext.Provider> ); }
ดังนั้นตอนนี้ ค่าของ "แสง" ก็มีให้สำหรับส่วนประกอบทั้งหมดของเราแล้ว (ซึ่งเราจะเขียนเร็วๆ นี้)
การสร้างไฟล์ธีมของเรา
ตอนนี้ เราจะสร้างไฟล์ธีมของเราซึ่งมีค่าสีต่างๆ สำหรับธีมสีอ่อนและสีเข้ม สร้างไฟล์ในโฟลเดอร์ /src
ชื่อ Colors.js
ใน Colors.js เราจะสร้างวัตถุที่เรียกว่า AppTheme
วัตถุนี้จะมีสีสำหรับธีมของเรา เมื่อเสร็จแล้วให้ส่งออกวัตถุ AppTheme
ดังนี้:
const AppTheme = { light: { textColor: "#000", backgroundColor: "#fff" }, dark: { textColor: "#fff", backgroundColor: "#333" } } export default AppTheme;
ตอนนี้ได้เวลาเริ่มสร้างส่วนประกอบ React ต่างๆ ของเราแล้ว
การสร้างส่วนประกอบปฏิกิริยาของเรา
มาสร้างส่วนประกอบต่อไปนี้กัน:
-
Header
-
ThemeToggler
-
MainWithClass
Header.jsx
import React from "react"; import ThemeToggler from "./ThemeToggler"; const headerStyles = { padding: "1rem", display: "flex", justifyContent: "space-between", alignItems: "center" } const Header = () => { return( <header style = {headerStyles}> <h1>Context API</h1> <ThemeToggler /> </header> ); } export default Header;
ThemeToggler.jsx
(สำหรับตอนนี้ เราจะคืนค่า div
ที่ว่างเปล่า )
import React from "react"; import ThemeContext from "../Context/ThemeContext"; const themeTogglerStyle = { cursor: "pointer" } const ThemeToggler = () => { return( <div style = {themeTogglerStyle}> </div> ); } export default ThemeToggler;
การใช้บริบทด้วยส่วนประกอบตามคลาส
ที่นี่ เราจะใช้ค่าของ ThemeContext
ของเรา อย่างที่คุณรู้อยู่แล้ว เรามี สองวิธีในการเขียนส่วนประกอบใน React : ผ่านฟังก์ชันหรือคลาส ขั้นตอนการใช้บริบทในทั้งสองวิธีต่างกัน ดังนั้นเราจะสร้างสององค์ประกอบเพื่อใช้เป็นส่วนหลักของแอปพลิเคชันของเรา: MainWithClass
และ MainWithFunction
ให้เราเริ่มต้นด้วย MainWithClass
MainWithClass.jsx
เราจะต้องนำเข้า ThemeContext
และ AppTheme
ของเรา เมื่อเสร็จแล้ว เราจะเขียนคลาสที่ส่งคืน JSX ของเราจากวิธีการเรนเดอร์ ตอนนี้เราต้องบริโภคบริบทของเรา มีสองวิธีในการทำเช่นนี้กับคอมโพเนนต์ตามคลาส:
- วิธีแรกคือผ่าน
Class.contextType
ในการใช้วิธีนี้ เรากำหนดคอนเท็กซ์วัตถุจากThemeContext
ของเราเป็นคุณสมบัติcontextType
ของคลาสของเรา หลังจากนั้น เราจะสามารถเข้าถึงค่าบริบทได้โดยใช้this.context
คุณยังสามารถอ้างอิงสิ่งนี้ได้ในวิธีวงจรชีวิตใดๆ และแม้แต่วิธีการแสดงimport React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component{ constructor(){ super(); } static contextType = ThemeContext; render(){ const currentTheme = AppTheme[this.context]; return( <main></main> ); } }
หลังจากกำหนดThemeContext
ให้กับคุณสมบัติcontextType
ของคลาสของเราแล้ว ฉันบันทึกอ็อบเจ็กต์ธีมปัจจุบันในตัวแปรcurrentTheme
ตอนนี้ เราจะคว้าสีจากตัวแปรcurrentTheme
และใช้เพื่อกำหนดรูปแบบมาร์กอัปrender() { const currentTheme = AppTheme[this.context]; return ( <main style={{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main>
แค่นั้นแหละ! อย่างไรก็ตาม วิธีนี้จำกัดให้คุณใช้บริบทเดียวเท่านั้น - วิธีที่สองคือ
ThemeContext.Consumer
ที่เกี่ยวข้องกับการใช้ Consumer แต่ละอ็อบเจ็กต์บริบทยังมาพร้อมกับคอมโพเนนต์ Consumer React ซึ่งสามารถใช้ในคอมโพเนนต์ตามคลาส องค์ประกอบผู้บริโภครับลูกเป็นฟังก์ชันและฟังก์ชันนั้นส่งคืนโหนด React ค่าบริบทปัจจุบันถูกส่งไปยังฟังก์ชันนั้นเป็นอาร์กิวเมนต์
ตอนนี้ ให้เราแทนที่โค้ดในองค์ประกอบMainWithClass
ด้วยสิ่งนี้:class Main extends Component { constructor() { super(); this.state = { } } render(){ return( <ThemeContext.Consumer> { (theme) => { const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ) } } </ThemeContext.Consumer> ); } }
อย่างที่คุณเห็น เราใช้ค่าปัจจุบันของThemeContext
ซึ่งเราเรียกว่า "theme" และเราคว้าค่าสีสำหรับโหมดชุดรูปแบบนั้นและกำหนดให้กับตัวแปรcurrentTheme
ด้วยวิธีนี้ คุณจะใช้ Consumer ได้หลายคน
นี่เป็นสองวิธีในการบริโภคบริบทด้วยส่วนประกอบตามคลาส
การใช้บริบทด้วยส่วนประกอบที่ใช้งานได้
การใช้บริบทด้วยองค์ประกอบการทำงานนั้นง่ายกว่าและน่าเบื่อน้อยกว่าการใช้ส่วนประกอบแบบคลาส ในการใช้บริบทในองค์ประกอบการทำงาน เราจะใช้ hook ที่เรียกว่า useContext
นี่คือสิ่งที่การใช้ ThemeContext
ของเราด้วยองค์ประกอบการทำงานจะมีลักษณะดังนี้:
const Main = () => { const theme = useContext(ThemeContext); const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } export default Main;
อย่างที่คุณเห็น สิ่งที่เราต้องทำคือใช้ useContext
hook กับ ThemeContext
ส่งผ่านเข้ามาเป็นอาร์กิวเมนต์
หมายเหตุ : คุณต้องใช้ส่วนประกอบต่าง ๆ เหล่านี้ในไฟล์ App.js เพื่อดูผลลัพธ์
กำลังอัปเดตธีมของเราด้วย ThemeToggler
Component
ตอนนี้เรากำลังดำเนินการกับองค์ประกอบ ThemeToggler
ของเรา เราต้องสามารถสลับไปมาระหว่างธีมสว่างและธีมมืดได้ ในการดำเนินการนี้ เราจะต้องแก้ไข ThemeContext.js ของเรา React.createContext
ของเราจะรับวัตถุที่คล้ายกับผลลัพธ์ของ useState
hook เป็นอาร์กิวเมนต์
const ThemeContext = React.createContext(["light", () => {}]);
เราส่งอาร์เรย์ไปยังฟังก์ชัน React.createContext
องค์ประกอบแรกในอาร์เรย์คือโหมดธีมปัจจุบัน และองค์ประกอบที่สองคือฟังก์ชันที่จะใช้ในการอัปเดตธีม ดังที่ฉันกล่าวไว้ สิ่งนี้คล้ายกับผลลัพธ์ของ useState
hook แต่ไม่ใช่ผลลัพธ์ของ useState
hook อย่างแน่นอน
ตอนนี้เราจะแก้ไขไฟล์ App.js ของเรา เราจำเป็นต้องเปลี่ยนค่าที่ส่งผ่านไปยังผู้ให้บริการไปยัง useState
hook ตอนนี้ค่าของบริบทธีมของเราคือ useState
hook ซึ่งค่าเริ่มต้นคือ "light"
function App() { const themeHook = useState("light"); return ( <ThemeContext.Provider value = {themeHook}> <div> <Header /> <Main /> </div> </ThemeContext.Provider> ); }
การเขียน ThemeToggler
Component
ให้เราเขียนองค์ประกอบ ThemeToggler
ของเราจริง ๆ :
import React,{useContext} from "react"; import ThemeContext from "../Context/ThemeContext"; const themeTogglerStyle = { cursor: "pointer" } const ThemeToggler = () => { const[themeMode, setThemeMode] = useContext(ThemeContext); return( <div style = {themeTogglerStyle} onClick = {() => {setThemeMode(themeMode === "light"? "dark": "light")}}> <span title = "switch theme"> {themeMode === "light" ? "" : "️"} </span> </div> ); } export default ThemeToggler;
เนื่องจากค่าของบริบทธีมของเราตอนนี้กลายเป็นตะขอทุกครั้งที่เราเรียกใช้ useContext
มันจะส่งคืนอาร์เรย์ การใช้ destructuring ทำให้เราสามารถดึงองค์ประกอบจากอาร์เรย์ได้ จากนั้นเราได้เขียนตัวจัดการเหตุการณ์ onClick
สำหรับ ThemeToggler
ของเรา ด้วยรหัสนั้น เมื่อใดก็ตามที่มีการคลิกตัวสลับธีม มันจะเปลี่ยนธีมของแอปพลิเคชันของเรา
ตอนนี้เราจะแก้ไขส่วนประกอบ Main
ของเราในเวอร์ชันต่างๆ
การแก้ไขส่วนประกอบ MainWithClass
ของเรา
- รุ่นของส่วนประกอบ
MainWithClass
ที่ใช้วิธีClass.contextType
:import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component{ constructor(){ super(); } static contextType = ThemeContext; render(){ const currentTheme = AppTheme[this.context[0]]; return( <main style={{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } }
- เวอร์ชันของคอมโพเนนต์
MainWithClass
ที่ใช้เมธอดThemeContext.Consumer
:import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component { constructor() { super(); this.state = {} } render() { return ( <ThemeContext.Consumer> { ([theme]) => { const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ) } } </ThemeContext.Consumer> ); } } export default Main;
การแก้ไขส่วนประกอบ MainWithFunction
ของเรา
คอมโพเนนต์ MainWithFunction
ควรได้รับการแก้ไขดังนี้:
import React, { useContext } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; const Main = () => { const theme = useContext(ThemeContext)[0]; const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } export default Main;
บทสรุป
แค่นั้นแหละ! เราประสบความสำเร็จในการใช้โหมดธีมสองโหมดสำหรับแอป React โดยใช้ Context API
ในกระบวนการนี้ เราได้เรียนรู้:
- Context API คืออะไรและแก้ปัญหาได้
- เมื่อใดควรใช้ Context API
- การสร้าง
Context
และการใช้งานทั้งในส่วนประกอบที่ทำงานและตามคลาส
อ่านเพิ่มเติม เกี่ยวกับ SmashingMag:
- จัดแต่งทรงผมในเว็บแอปสมัยใหม่
- สร้างแอพมือถือด้วย Ionic และ React
- สร้าง PWA ด้วย Webpack และ Workbox
- ทำความรู้จักกับ MutationObserver API