สรุปเนื้อหาสำหรับ React ของปี 2022
Table of contents
Intro
บทความสรุป React cheatsheet นี้จะเป็นการอธิบายเกี่ยวกับ React พื้นฐานเนื้อหาอย่างย่อเพื่อทำความเข้าใจ React อย่างรวดเร็ว และให้คุณเห็นภาพรวมของแนวคิด React ทั้งหมดที่คุณต้องรู้ในปี 2022 มาเริ่มกันเลย!
แปลและเรียบเรียงใหม่จาก https://www.freecodecamp.org/news/the-react-cheatsheet/
React Elements
React elements นั้นเขียนคล้ายกับการเขียน HTML elements โดยคุณสามารถเขียน HTML elements ต่าง ๆ ใน React ได้ดังนี้
() => { return ( <> <h1>My Header</h1> <p>My paragraph></p> <button>My button</button> </> ) }
สามารถเขียน React elements โดยคุณสมบัติที่เรียกว่า JSX
แต่เพราะว่า JSX
คือ JavaScript ฟังก์ชัน (ไม่ใช่ HTML) ดังนั้น Syntax จะมีความแตกต่างกันเ ล็กน้อย
ดูดี ๆ จะไม่เหมือน HTML ทั้งหมด โดย JSX
สามารถเขียน Single-tag elements ที่เป็น Self-closing ที่ปิดด้วย /
(forward slash) ดังตัวอย่าง:
() => { return ( <> <img src="https://loremflickr.com/640/360" /> <br /> <hr /> </> ) }
React Element Attributes
นอกจากนี้ JSX ยังใช้ Syntax ที่แตกต่างกันสำหรับ Attributes
เนื่องจาก JSX
คือ JavaScript และ JavaScript ใช้ Camelcase naming convention ในการตั้งชื่อ Attributes (ตย. "camelCase") ซึ่งจะเขียนต่างจาก HTML
ตัวอย่างที่พบบ่อยสุดคือ Class attribute ที่เขียนเป็น className
ดังตัวอย่าง:
<div className="container"></div>
React Element Styles
สามารถใช้ Inline styles แทนการใช้ Double quotes ("") ด้วยการใช้ Curly braces ()
โดยจะเป็นการเขียนแบบ Objects by properties ที่เป็น Inline styles ซึ่งจะไม่สามารถเขียน CSS Styles แบบ Plain string ได้
() => { return ( <h1 style={ { fontSize: 24, margin: '0 auto', textAlign: 'center' } }>My header</h1> ) }
React Fragments React
React นั้นจำเป็นต้อง Returned ทุก Elements ภายในหนึ่ง "Parent" ของ Component
สำหรับตัวอย่างนี้จะไม่สามารถ Return 2 elements ได้ (h1 และ p จาก MyComponent)
() => { // นี่คือ Syntax ที่ผิดและมี Errors return ( <h1>My header</h1> <p>My paragraph </p> ) }
ปกติจะต้องครอบด้วย <div>
ถ้าไม่ต้องการ Wrap elements เป็น Container ด้วย div
สามารถใช้ Fragment (<>
และ </>
) แทนได้ดังนี้
() => { // Syntax ที่ถูกต้อง (วิธีที่ 1) function MyComponent() { return ( <> <h2>My header 1</h2> <p>My paragraph 1</p> </> ) } // Syntax ที่ถูกต้อง (วิธีที่ 2 แต่ไม่เป็นที่นิยม) function MyComponent2() { return ( [ <h2>My header 2</h2>, <p>My paragraph 2</p> ] ) } return ( <> <MyComponent /> <MyComponent2 /> </> ) }
สามารถเขียน Fragments ในรูปแบบปกติและแบบสั้นด้วย Syntax:
<React.Fragment></React.Fragment>
และ<></>
ตามลำดับ
React Components
React Components ช่วยให้สามารถจัดโครงสร้างแบบกลุ่มของ Elements ใน React ได้
โดยก็คือการเขียนแบบปกติในรูปแบบธรรมดาของ JavaScript function โดยจะมีความแตกต่างกันอยู่บ้างนิดนึงดังนี้
- ชื่อ Component ตัวอักษรแรกควรเริ่มด้วยตัวพิมพ์ใหญ่ ตย. MyComponent
- Components จะไม่เหมือนกับ JavaScript functions ดังนั้นควร Return เป็น JSX
ตัวอย่าง Basic syntax ของ React function component:
() => { function App() { return ( <> <div>Hello world!</div> <User /> </> ); } function User() { return <p>Hello, Kongvut!</p>; // Hello, Kongvut! } return <App /> }
React Props
React components สามารถรับข้อมูล Property ที่เรียกว่า Props
และบางที Props คือการส่งค่าจาก Parent component ไปที่ Child component
ตัวอย่างการ Passing ของ Prop name จาก App ไปที่ User component
() => { function App() { return <User name="Kongvut"/> } function User(props) { return <h1>Hello, {props.name}</h1>; // Hello, Kongvut! } return <App /> }
โดย Props คือ Object ดังนั้นสามารถใช้ชื่อของ Prop ในการเข้าถึงค่าของ Value ได้
ในการเก็บค่า Value แบบ Dynamic (Variable หรือ Expression) ภายใน JSX นั้นควรจะ Wrap ด้วยวงเล็บปีกา
{}
เนื่องจากใช้เพียงชื่อ Property เท่านั้นใน Props object ดังนั้นสามารถเขียน Code อย่างง่ายเพื่อทำ object destructuring ได้ดังตัวอย่าง:
function App() {
return <User name="Kongvut"/>
}
function User({name}) {
return <h1>Hello, {name}!</h1>; // Hello, Kongvut!
}
ทุก JavaScript value สามารถส่งผ่านไปที่ Prop และรวมไปถึงพวก Elements และ Components
React Children Props
Props สามารถส่งผ่านตำ แหน่งระหว่าง Tags เปิด-ปิด
ของ Components
Props จะถูกส่งผ่านในส่วนของ Property children
function App() {
return (
<User>
<h1>Hello, Kongvut!</h1>
</User>
);
}
function User({children}) {
return children; // Hello, Kongvut!
}
React Conditionals
React components และ elements สามารถทำเป็นเงื่อนไขในการแสดงผลได้
ตัวอย่างธรรมดาทั่วไปคือการสร้างส่วน Return แยกส่วนกันร่วมกับการใช้ if-statement
function App() {
const isAuthUser = useAuth();
if (isAuthUser) {
// if our user is authenticated, let them use the app
return <AuthApp/>;
}
// if user is not authenticated, show a different screen
return <UnAuthApp/>;
}
ถ้าคุณต้องการเขียนเงื่อนไขภายใต้ Return statement สามารถใช้ Ternary operator
ที่เป็นการเขียนเงื่อนไขแบบสั้นและ Wrap เงื่อนไขด้วย Curly braces
function App() {
const isAuthUser = useAuth();
return (
<>
<h1>My App</h1>
{isAuthUser ? <AuthApp/> : <UnAuthApp/>}
</>
)
}
React Lists
อ่านเนื้อหาเพิ่มเติมเกี่ยวกับการใช้งาน .map() อย่างละเอียดได้ที่นี่ "ความแตกต่างระหว่าง Map Filter Find Reduce Foreach Every Some ของ JavaScript"
Lists ของ React components สามารถสร้าง Output ด้วย .map() function
.map() สามารถ Loop ข้อมูลใน Array และ Output ออกเป็น JSX
ในตัวอย่างจะแสดง List output ของ Soccer players โดยใช้ Component SoccerPlayer
() => { function SoccerPlayer({key, name}) { return <li key={key}>{name}</li> } function SoccerPlayers() { const players = ["Messi", "Ronaldo", "Laspada"]; return ( <> <ul> {players.map((playerName) => ( <SoccerPlayer key={playerName} name={playerName}/> ))} </ul> </> ); } return ( <SoccerPlayers /> ) }
เมื่อไหร่ก็ตามที่คุณ Loop ข้อมูลของ Array คุณควรให้มี Key prop บน Element หรือ Component
เพิ่มเติม Key prop นี้ควรให้เป็น Unique value และไม่ได้หมายถึง Element index
ในตัวอย่างด้านบนจะใช้ค่าข้อมูลเป็น Unique นั่นคือ playerName
React Context
React context อนุญาตให้เราส่งผ่านข้อมูลไปที่โครงสร้าง Component ไม่ต้องใช้ Props
ปัญหากับ Props คือบางครั้งเราส่งข้อมูลผ่าน Component หลายชั้น ๆ แบบไม่ต้องการซึ่งปัญหานี้เรียกว่า Props drilling
นี่คือตัวอย่างที่ง่าย ๆ ของการส่ง Props ผ่าน Body component ที่ไม่ต้องการ
function App() {
return (
<Body name="Kongvut"/>
);
}
function Body({name}) {
return (
<Greeting name={name}/>
);
}
function Greeting({name}) {
return <h1>Welcome, {name}</h1>;
}
ก่อนใช้ Context ให้พิจารณาการจัดโครงสร้างการส่ง Props ผ่าน Components ที่ไม่จำเป็น
สำหรับการใช้ Context เราใช้ createContext function จาก React
เราสามารถ Call function กับการ Initial value ในตอนสร้าง Context
เมื่อสร้าง Context ใน Provider และ Consumer property ระหว่างแต่ละ Components แล้ว
สามารถ Wrap Provider ทั้งโครงสร้าง Component ที่เราต้องการให้ส่งค่า Value จากนั้นสามารถใช้ Consumer ใน Component เมื่อต้องการใช้งานค่า Value
import {createContext} from 'react';
const NameContext = createContext('');
function App() {
return (
<NameContext.Provider value="Kongvut">
<Body/>
</NameContext.Provider>
);
}
function Body() {
return <Greeting/>;
}
function Greeting() {
return (
<NameContext.Consumer>
{name => <h1>Welcome, {name}</h1>}
</NameContext.Consumer>
);
}
React Hooks
React hooks เริ่มใช้งานตั้งแต่ React version 16.8 ที่ทำให้ง่ายต่อการใช้งานและ Reusable สำหรับการใช้งานกับ React function components
โดย Hooks สามารถใช้คุณสมบัติทั้งหมดก่อนหน้าที่มีใน Class components
และเพิ่มเติมคือ เราสามารถสร้าง Custom hooks ของตัวเองสำหรับใช้กับฟังก์ชัน Custom app
หลาย React hooks ที่ถูกเพิ่มเข้ามาใน React library หลักและจะพาไปดู 6 Hooks พื้นฐานที่คุณจำเป็นต้องรู้
- useState
- useEffect
- useRef
- useContext
- useCallback
- useMemo
React useState Hook
useState สำหรับใช้จัดการกับ Stateful values ใน Function components
useState เป็นการใช้แทนค่าตัวแปรใน State อย่างง่ายโดยที่เมื ่อค่าใน State ถูก Updated แล้ว Component จะถูก Re-created และถูก Re-renders จากนั้น Display จะถูกอัปเดตค่า
คล้ายกับ Hooks ทั้งหมด โดยจะ Call useState ในส่วนบนของ Component และสามารถใส่ค่า Initial value สำหรับค่าเริ่มต้นกับ State variable
สามารถใช้ Array destructuring กับค่า Value จาก useState เพื่อใช้กับการ Access value และ Stored state สำหรับอัปเดตกับ State
import {useState} from 'react';
function MyComponent() {
const [stateValue, setStateValue] = useState(initialValue);
}
ตัวอย่างพื้นฐานของการใช้ useState คือการเพิ่มค่าสำหรับ Counter
เราสามารถเห็นค่าปัจจุบันของการ Count จากตัวแปร Count และสามารถเพิ่มค่า State ด้วยการใส่ค่า count + 1 ที่ setCount function
() => { function Counter() { const [count, setCount] = useState(0); function updateCount() { setCount(count + 1); } return <button onClick={updateCount}>Count is: {count}</button>; } return ( <Counter /> ) }
React useEffect Hook
สมมุติถ้าเราต้องการเขียนติดต่อกับ API เราสามารถใช้ useEffect Hook
useEffect คือ Hook ที่คำนึงถึงการดำเนินการที่มี Effect
โดยพื้นฐาน Syntax ของ useEffect จำเป็นต้องระบุ 2 Arguments คือ Function และ Array dependencies
import {useEffect} from 'react';
function MyComponent() {
useEffect(() => {
// perform side effect here
}, []);
}
ถ้าเราต้องการทำการ Fetch data สามารถใช้ useEffect อย่างเช่นในตัวอย่างด้านล่างในการ Fetching เพื่อการแสดงผล List ของ Posts
() => { function Post({key, post}) { return <div key={key}> <h3>{post.title}</h3> <p>{post.body}</p> </div> } function PostList() { const [posts, setPosts] = useState([]); useEffect(() => { fetch('https://jsonplaceholder.typicode.com/posts?_limit=5') .then(response => response.json()) .then(posts => setPosts(posts)); }, []); return posts.map(post => <Post key={post.id} post={post}/>); } return ( <PostList/> ) }
ถ้าเราจำเป็นต้องให้ Function useEffect มีการเปลี่ยนแปลง (มีการทำงานอีกครั้ง) สามารถระบุค่าใน Dependency array
ถ้าเมื่อ Value เปลี่ยนแปลง Effect function ก็จะทำงานซ้ำอีกครั้ง (re-executed)
ตัวอย่าง สำหรับโค้ดสั้น ๆ ที่เป็นการเพิ่ม และการลบ Class "overflow-hidden" กับ Body element เมื่อเมนูของ Mobile เปิด หรือ ปิด
function Mobile({open}) {
useEffect(() => {
const body = document.querySelector("#__next");
if (open) {
body.classList.add("overflow-hidden");
} else {
body.classList.remove("overflow-hidden");
}
}, [open]);
// ...
}
เมื่อค่า open
มีการเปลี่ยนแปลง Effect function ก็จะทำงานซ้ำอีกครั้ง
React useRef
อ่านเนื้อหาเพิ่มเติมเกี่ยวกับการใช้งาน useRef อย่างละเอียดได้ที่นี่ "บันทึกการใช้งาน useRef createRef และ forwardRef ของ React"
useRef ใช้สำหรับการเข้าถึง JSX element โดยตรง
โดยใช้ useRef เพื่อดึง Value และวางมันบน Ref prop ใน React element
Refs ไม่ได้ built-in prop บน components มีแค่สำหรับ React elements เท่านั้น
นี่คือ Syntax พื้นฐานสำหรับ useRef
import {useRef} from 'react';
function MyComponent() {
const ref = useRef();
return <div ref={ref}/>
}
Ref คือการแนบเพื่อให้ Element เราสามารถใช้ค่า Value ที่เก็บบน ref.current เพื่อ Access element
สำหรับตัวอย่าง ถ้าเราต้องการเขียนบาง Code โดยโพกัส Search Input เมื่อผู้ใช้กด Control + K
import {useWindowEvent} from "@mantine/hooks";
import {useRef} from "react";
function Header() {
const inputRef = useRef();
useWindowEvent("keydown", (event) => {
if (event.code === "KeyK" && event.ctrlKey) {
event.preventDefault();
inputRef.current.focus();
}
});
return <input ref={inputRef}/>
}
React useContext
useContext เป็นวิธีที่ง่ายในการใช้งาน Context แล้วใช้ Component มาตรฐานคือ Context.Consumer
Syntax เกี่ยวกับการเรียกใช้ค่าจาก Context โดยใช้ useContext ซึ่งค่า Return value คือค่าที่ใส่ลงใน Context
import {useContext} from 'react';
function MyComponent() {
const value = useContext(Context);
// ...
}
ลอง Rewrite ใหม่ของตัวอย่างก่อนหน้าโดยการใช้ useContext hook:
import {createContext, useContext} from 'react';
const NameContext = createContext('');
function App() {
return (
<NameContext.Provider value="Kongvut">
<Body/>
</NameContext.Provider>
);
}
function Body() {
return <Greeting/>;
}
function Greeting() {
const name = useContext(NameContext);
return (
<h1>Welcome, {name}</h1>
);
}
React useCallback
อ่านเนื้อหาเพิ่มเติมเกี่ยวกับการใช้งาน useCallback อย่างละเอียดได้ที่นี่ "การใช้งานและความแตกต่างระหว่าง useMemo และ useCallback ของ React Hooks"
useCallback คือ Hook ที่ช่วยเพิ่มประสิทธิภาพให้กับ App
เป็นเรื่องที่สำคัญเพราะเป็นฟังก์ชันป้องกันการ Re-renders ที่ไม่จำเป็นซึ่งสามารถส่งผลเสี ยต่อประสิทธิภาพของ App ของเราได้
ถ้าเรากลับไปดูตัวอย่าง PlayerList จากก่อนหน้า จากนั้นเพิ่มความสามารถที่ Players ไปที่ Array เมื่อเรามีฟังก์ชันสำหรับลบคือ handleRemovePlayer ผ่าน Props ทำให้ Function จะ Re-created ทุกครั้ง
แนวทางในการแก้ไขเรื่องนี้จะใช้ Callback function ใน useCallback และอีกหนึ่ง Argument player ใน Dependency array
() => { function PlayerList({players, handleRemovePlayer}) { return ( <ul> {players.map((player, key) => ( <li key={key}> {player} <button onClick={() => handleRemovePlayer(key)}>x</button> </li> ))} </ul> ); } function App() { const inputRef = useRef(""); const [players, setPlayers] = useState(["Messi", "Ronaldo", "Laspada"]); const handleAddPlayer = useCallback(() => { const input = inputRef.current.value; setPlayers([...players, input]); }, [players]) const handleRemovePlayer = useCallback((index) => { players.splice(index, 1); setPlayers([...players]); }, [players]); return ( <> <input ref={inputRef} type="text" placeholder="Enter Player name ..."/> <button onClick={handleAddPlayer}>Add Player</button> <PlayerList players={players} handleRemovePlayer={handleRemovePlayer}/> </> ); } return ( <App/> ) }
React useMemo
อ่านเนื้อหาเพิ่มเติมเกี่ยวกับการใช้งาน useMemo อย่างละเอียดได้ที่นี่ "การใช้งานและความแตกต่างระหว่าง useMemo และ useCallback ของ React Hooks"
useMemo คือ Performance hook อีกตัวที่มีความสามารถสำหรับตัวดำเนินการทำ "memoize"
Memoization คือการทำการจดจำ หรือการ Caching ค่า Result ต่าง ๆ ที่เสียเวลาคำนวณหรือค่าที่ไม่จำเป็นต้องเปลี่ยนแปลงบ่อย ๆ และสามารถเรียกใช้งานได้ทันทีโดยไม่ต้องคำนว ณใหม่ทุกครั้ง
คล้ายกับ useEffect และ useCallback โดยที่ useMemo สามารถใช้ Callback function และกำหนด Dependency array
แต่ก็ไม่คล้ายกันหมดไปซะทีเดียว โดยที่ useMemo นั้นวัตถุประสงค์คือใช้ Return value
คุณควรที่จะส่งออกค่า Value โดยการใช้คำสั่ง Return หรือจะใช้แบบ Arrow function แบบสั้น (เหมือนตัวอย่างด้านล่าง)
มาดูตัวอย่างจริงที่ใช้ useMemo กับ mdx-bundler โดยที่ mdx-bundler คือ Library สำหรับการ Convert .mdx ไฟล์เป็น React Component
คือการใช้ useMemo สำหรับ Convert โค้ดที่เป็น Raw string เป็น React component
import * as React from 'react'
import {getMDXComponent} from 'mdx-bundler/client'
function Post({code, frontmatter}) {
const Component = React.useMemo(() => getMDXComponent(code), [code]);
return (
<>
<header>
<h1>{frontmatter.title}</h1>
<p>{frontmatter.description}</p>
</header>
<main>
<Component/>
</main>
</>
)
}
เหตุผลคือการป้องกัน Component value ไม่ให้ Re-created โดยไม่จำเป็นเมื่อ Component มีการ Re-renders
โดย useMemo จะสั่งให้ Callback function ทำงานก็ต่อเมื่อค่าใน Dependency มีการเปลี่ยนแปลงเท่านั้น