React Hook

Sittipong Saychum
NECTEC
Published in
4 min readAug 29, 2021

Part นี้เราจะมีคุยกันเรื่อง React hook กัน ใครที่ยังไม่เคยเขียน React มาก่อนเข้าไปอ่าน บทแรกๆ เริ่มต้นก่อนได้เลยจาก link ด้านล่างนี้

เร่ิมต้นกันที่เปิด vscode มาแล้วเปิด terminal

เพื่อให้การทำงานง่ายและรวดเร็ว ผมจะสร้าง config save และจัด format อัตโนมัติ สร้าง folder .vscode และสร้างไฟล์ settings.json ในไฟล์ให้เขียนแบบนี้

{
"files.autoSave": "onFocusChange",
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}

อย่าลืมลง Extensions ตัวนี้ด้วยนะ

หลังจากนั้น creat project ใช้คำสั่ง

npm create next-app clientnext

เคลียโปรแจคอะไรที่ไม่ใช้ก็ลบออก

ในไฟล์ index.js ลบทุกอย่างแล้วพิมพ์ rfc จะได้ component เริ่มต้นมา

บทนี้เราจะมาเรียนรู้ การใช้งาน useState, useRef, useEffect, useCallback และ useMemo

import { useState, useRef, useEffect, useCallback, useMemo } from "react";

เริ่มกันที่ useState จะแบ่งเป็น 3 รูปแบบการใช้งาน คือ

  1. การใช้งานกับค่าตัวแปรปกติ ไม่ว่าจะเป็น string หรือตัวเลข หรือค่า boolean ลักษณะการ set ค่าหรือเรียกใช้งานจะเหมือนๆกัน
  2. การใช้งานกับค่าตัวแปร array
  3. การใช้งานกับค่าตัวแปร Object

เรามาดูกันที่ตัว useState กันก่อนผมจะเขียน UI ง่ายด้วยการมี TextBoxใส่ input เมื่อผมพิมพ์อะไรก็ตามลงในช่องนี้ จะทำการแสดงผลออกมาด้วย ซึ่ง ค่าจากการ set useState จะแสดงผลก็ต่อเมื่อมีการเปลี่ยนแปลงค่า ซึ่งถ้าค่าที่ set useState เป็นค่าเดิมก่อนหน้า หน้าเวปของเราก็จะไม่มีการ update

const [myStr, setStr] = useState("");
function changeHendle(event) {
setStr(event.target.value);
}
return (
<div>
<input onChange={changeHendle}/>
<p>useState : str: {myStr}</p>
</div>
);

ต่อไปเรามาดู การใช้งานกับค่าตัวแปร array ผมจะเพิ่ม function submitHendle และ button เข้าไป จากโคดด้านล่างจะเห็นว่า เมื่อผมกดปุ่ม submit จะทำการ set ค่า array จากค่า myStr และนำมาแสดงผลด้วยการ map ในส่วนนี้จะแสดงผลก็ต่อเมื่อ ผมกดปุ่ม submit เมื่อผมกดปุ่ม submit ไปเรื่อยๆ ค่าก็จะ update เพิ่มจำนวนข้อมูลขึ้น

const [myArr, setArr] = useState([]);
function submitHendle(event) {
event.preventDefault();
setArr([...myArr, myStr]);
}
return (
<div>
<input onChange={changeHendle}/>
<button onClick={submitHendle}>Submit</button>
<p>useState : str: {myStr}</p>
<p>useState : arr:{" "}{myArr.map((item, i) => (<li>{item}</li>))}</p>
</div>
);

อีกตัวคือ การใช้งานกับค่าตัวแปร Object เริ่มต้นเราต้อง set ค่า default ให้เป็น object เสียก่อน หลังจากนั้นผมจะสร้าง textbox input ขึ้นมา 2 ช่อง โดยให้ 2 ช่องนี้ onChange ไปยัง function objHendle เมื่อผมพิมพ์อะไรลงไปใน 2 ช่องนี้ก็ตาม หน้า page ผมจะอัพเดทให้เห็นแบบ realtime

const [myObj, setObj] = useState({ name: "", age: 0 });
function objHendle(event) {
setObj({...myObj,[event.target.name]: event.target.value,});
}
return (
<div>
<p>Name:<input type="text" name="name" onChange={objHendle} /></p>
<p>Age:<input type="number" name="age" onChange={objHendle} /></p>
<p>useState : obj.name: {myObj.name}</p>
<p>useState : obj.age: {myObj.age}</p>
</div>
);

useRef เป็นที่นิยมในการนำมาใช้กัน textBox input ข้อดีคือ เวลาที่เราพิมพ์อะไรลงไปในช่อง มันจะไม่ update ทุกๆตัวอักษรที่เราพิมพ์ลงไป คุณลองนึกถึงเวลาคุณพิมพ์ password ลงไปสิ ดังนั้น useRef จะช่วยให้ระบบมีควาามปลอดภัยและที่สำคัญ หน้าเวปไม่ต้องทำการ render ทุกครั้งที่เราพิมพ์ลงไปด้วย

const myRef = useRef(null);
const [strRef, setStrRef] = useState("");
function submitHendle(event) {
event.preventDefault();
setStrRef(myRef.current.value);
}
return (
<div>
<input ref={myRef} placeholder="Please input text" onChange={changeHendle}/>
<button onClick={submitHendle}>Submit</button>
<p>useRef : strRef: {strRef}</p>
</div>
);

จากโคดด้านบน ปัญหาของ useRef คือ ต้อง set default ให้เป็น null ซึ่งจะทำให้การ render หน้าเวปขึ้นมาครั้งแรก จะไม่สามารถแสดงผลของค่า useRef ได้เนื่องจากค่ามันเป็น null ดังนั้นผมจะใช้ useState มารับค่าจาก useRef เมื่อผมกดปุ่ม submit อีกที

จาก useState เราจะเห็นว่าเป็นการ set ค่าและนำมาใช้งาน แต่ถ้าเราต้องการจะเขียน function เพื่อให้ทำงานอะไรสักอย่าง ตัวอย่างเช่น เรียก axios เพื่อเรียกใช้งาน api หรือ back-end ซึ่งตรงนี้ useEffect, useCallback, useMemo จะเข้ามาช่วยเรา

useEffect จะรับ parameter 2 ตัว ตัวแรกเป็น function เป็นส่วนให้เราเขียนโคดว่าจะให้ทำอะไร โดยในส่วนนี้จะทำงานตั้งแต่มีการ render ครั้งแรกเลย จากโคดด้านล่างเราจะเห็นว่า log ขึ้นมาก่อนเลยว่า ‘useEffect’ เมื่อเราใส่ข้อความลงใน textBox ค่า myStr จะเปลี่ยนไป ทำให้ในส่วนของ return ของ useEffect ทำงาน log จะขึ้นว่า ‘useEffect end.’ ก่อน แล้วค่อยมาเข้า useEffect อีกครั้งทำให้ log ขึ้นว่า ‘useEffect’ อีกที แต่ถ้าเราต้องการให้ useEffect ทำงานแค่ครั้งเดียวเมื่อ render เท่านั้น ให้เราเปลี่ยน parameter ตัวที่สอง จาก [myStr] เป็น [] และ return จะทำงานเมื่อออกจากหน้า เวปนี้ไปเท่านั้น

const [myStr, setStr] = useState("");
useEffect(() => {
console.log('useEffect');
return () => {
console.log('useEffect end.');
};
}, [myStr]);

function changeHendle(event) {
setStr(event.target.value);
}
return (
<div>
<input onChange={changeHendle}/>
<p>useState : str: {myStr}</p>
</div>
);

จาก useEffect จะเห็นปัญหา ว่าถ้าเราต้องการใช้ค่าจาก function ใน useEffect นั้น จะต้อง set useState เท่านั้น ทำให้ต้อง render ใหม่อีกครั้ง เพื่อแสดงผลที่ได้จาก function ใน useEffect ดังนั้น useMemo จะเข้ามาช่วยในการแก้ปัญหานี้

useMemo ทำงานตั้งแต่ render ครั้งแรกเหมือน useEffect แต่สามารถเก็บค่าที่ได้จากการทำงานใน function ได้ จากโคดด้านล่าง myMemo จะเก็บค่า จาก function ที่ทำการหาความยาวตัวอักษรของ myStr และทำการหาค่าใหม่ทุกครั้งที่ค่า myStr เปลี่ยนแปลง เช่นเดียวกับ useEffect ถ้าเราต้องการให้ทำงานแค่ครั้งเดียวเมื่อ render เท่านั้น ให้เราเปลี่ยนจาก [myStr] เป็น []

ใน function useMemo นี้เราสามารถ set useState ได้ด้วย แต่ก็อย่างที่บอก set useState เมื่อใด ก็ rerender อีกครั้ง แต่ถ้าไม่ set useState ก็จะไม่ rerender ทำให้จำนวนครั้งในการ render น้อยกว่า การใช้ useEffect

const [myStr, setStr] = useState("");
const myMemo = useMemo(() => {
console.log('useMemo');
return myStr.length;
}, [myStr]);
function changeHendle(event) {
setStr(event.target.value);
}
return (
<div>
<input onChange={changeHendle}/>
<p>useMemo:{myMemo}</p>
</div>
);

ตัวสุดท้าย useCallback จะทำงานคล้าย useMemo แต่ useMemo เก็บค่าที่ได้จาก function ใน useMemo แต่ useCallback เก็บทั้ง function เลย ทำให้มีความสามารถ รับ parameter เข้าไปคำนวนใน function ได้ด้วย

const [myStr, setStr] = useState("");
const myCB = useCallback(
(strInput) => {
return strInput + ":" + myStr;
},[myStr]);
function changeHendle(event) {
setStr(event.target.value);
}
return (
<div>
<input onChange={changeHendle}/>
<p>useMemo:{myCB('Hello')}</p>
</div>
);

จากโคดด้านบน ผมส่ง string ผ่าน myCB เพื่อให้ไป concat กับ myStr ที่รับค่ามาจาก textBox useCallback จะทำงานทุกครั้งที่ค่า myStr เปลี่ยนไป เช่นเดียวกับ useEffect และ useMemo ถ้าเราต้องการให้ทำงานแค่ครั้งเดียวเมื่อ render ให้เราเปลี่ยนจาก [myStr] เป็น [] ข้อระวังข้อหนึ่งของ useCallback คือ จะไม่สามารถ set useState ได้ใน function

เรียบร้อยแล้วนะครับ สำหรับการใช้งาน useState, useRef, useEffect, useCallback และ useMemo นอกจากที่ ผมกล่าวมาแล้วก็ยังมี useContext ซึ่งปกติแล้วผมไม่ค่อยได้ใช้ ผมจะใช้ recoil แทนเสียมากกว่า แต่ถ้ามีเวลาผมจะมีเขียน อธิบายให้นะครับ การใช้งานและเรียกใช้คล้ายๆกัน redux เลย ใครยังใช้งาน redux ไม่เป็นตาม link ด้านล่างเลยครับ

--

--