Skip to main content

สรุปเนื้อหาสำหรับ React ของปี 2022

Kongvut Sangkla
Tags:

Intro

บทความสรุป React cheatsheet นี้จะเป็นการอธิบายเกี่ยวกับ React พื้นฐานเนื้อหาอย่างย่อเพื่อทำความเข้าใจ React อย่างรวดเร็ว และให้คุณเห็นภาพรวมของแนวคิด React ทั้งหมดที่คุณต้องรู้ในปี 2022 มาเริ่มกันเลย!

แปลและเรียบเรียงใหม่จาก https://www.freecodecamp.org/news/the-react-cheatsheet/

React Elements

React elements นั้นเขียนคล้ายกับการเขียน HTML elements โดยคุณสามารถเขียน HTML elements ต่าง ๆ ใน React ได้ดังนี้

Live Editor
() => {
  return (
    <>
      <h1>My Header</h1>
      <p>My paragraph></p>
      <button>My button</button>
    </>
  )
}
Result
Loading...

สามารถเขียน React elements โดยคุณสมบัติที่เรียกว่า JSX

แต่เพราะว่า JSX คือ JavaScript ฟังก์ชัน (ไม่ใช่ HTML) ดังนั้น Syntax จะมีความแตกต่างกันเล็กน้อย

ดูดี ๆ จะไม่เหมือน HTML ทั้งหมด โดย JSX สามารถเขียน Single-tag elements ที่เป็น Self-closing ที่ปิดด้วย / (forward slash) ดังตัวอย่าง:

Live Editor
() => {
  return (
    <>
      <img src="https://loremflickr.com/640/360" />
      <br />
      <hr />
    </>
  )
}
Result
Loading...

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 ได้

Live Editor
() => {
  return (
    <h1 style={
      {
        fontSize: 24,
        margin: '0 auto',
        textAlign: 'center'
      }
    }>My header</h1>
  )
}
Result
Loading...

React Fragments React

React นั้นจำเป็นต้อง Returned ทุก Elements ภายในหนึ่ง "Parent" ของ Component

สำหรับตัวอย่างนี้จะไม่สามารถ Return 2 elements ได้ (h1 และ p จาก MyComponent)

Live Editor
() => {
  // นี่คือ Syntax ที่ผิดและมี Errors
  return (
    <h1>My header</h1>
    <p>My paragraph </p>
  )
}
Result
Loading...

ปกติจะต้องครอบด้วย <div> ถ้าไม่ต้องการ Wrap elements เป็น Container ด้วย div สามารถใช้ Fragment (<> และ </>) แทนได้ดังนี้

Live Editor
() => {
  
  // 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 />
    </>
  )
}
Result
Loading...

สามารถเขียน Fragments ในรูปแบบปกติและแบบสั้นด้วย Syntax: <React.Fragment></React.Fragment> และ <></> ตามลำดับ

React Components

React Components ช่วยให้สามารถจัดโครงสร้างแบบกลุ่มของ Elements ใน React ได้

โดยก็คือการเขียนแบบปกติในรูปแบบธรรมดาของ JavaScript function โดยจะมีความแตกต่างกันอยู่บ้างนิดนึงดังนี้

  1. ชื่อ Component ตัวอักษรแรกควรเริ่มด้วยตัวพิมพ์ใหญ่ ตย. MyComponent
  2. Components จะไม่เหมือนกับ JavaScript functions ดังนั้นควร Return เป็น JSX

ตัวอย่าง Basic syntax ของ React function component:

Live Editor
() => {
  function App() {
    return (
      <>
        <div>Hello world!</div>
        <User />
      </>
    );
  }

  function User() {
    return <p>Hello, Kongvut!</p>; // Hello, Kongvut!
  }
  
  return <App />
}
Result
Loading...

React Props

React components สามารถรับข้อมูล Property ที่เรียกว่า Props

และบางที Props คือการส่งค่าจาก Parent component ไปที่ Child component

ตัวอย่างการ Passing ของ Prop name จาก App ไปที่ User component

Live Editor
() => {
  function App() {
    return <User name="Kongvut"/>
  }

  function User(props) {
    return <h1>Hello, {props.name}</h1>; // Hello, Kongvut!
  }
  
  return <App />
}
Result
Loading...

โดย 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

Live Editor
() => {
  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 />
  )
}
Result
Loading...

เมื่อไหร่ก็ตามที่คุณ 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

Live Editor
() => {
  function Counter() {
    const [count, setCount] = useState(0);

    function updateCount() {
      setCount(count + 1);
    }

    return <button onClick={updateCount}>Count is: {count}</button>;
  }
  
  return (
    <Counter />
  )
}
Result
Loading...

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

Live Editor
() => {
  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/>
  )
}
Result
Loading...

ถ้าเราจำเป็นต้องให้ 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

Live Editor
() => {
  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/>
  )
}
Result
Loading...

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 มีการเปลี่ยนแปลงเท่านั้น

References

Loading...