useState hook - คู่มือที่ครอบคลุม PlatoBlockchain Data Intelligence ค้นหาแนวตั้ง AI.

ตะขอ useState – คู่มือที่ครอบคลุม

รัฐคืออะไร?

ก่อนที่เราจะลงลึกใน useState hook เรามาทำความเข้าใจกับคำศัพท์กันก่อน รัฐ

รัฐแสดงข้อมูลเกี่ยวกับบางสิ่งบางอย่าง ณ เวลาใดเวลาหนึ่ง

ตัวอย่างเช่น ลองพิจารณากล่องข้อความ

ในขั้นต้น ไม่มีอะไรในกล่องข้อความนี้ ดังนั้นสถานะของมันจึงเป็น ไม่มีข้อมูล. สมมติว่าคุณเริ่มพิมพ์คำว่า สวัสดี ในแต่ละการกดแป้น สถานะของกล่องข้อความจะเปลี่ยนไป ตอนแรกจะเป็น "H" ตามด้วย "He" จากนั้น "Hel" ไปเรื่อยๆ จนกลายเป็น "Hello"

นอกจากนี้ โปรดสังเกตด้วยว่าขณะที่คุณกำลังพิมพ์ คุณจะไม่ได้สูญเสียค่าก่อนหน้า หากคุณกด “H” ตามด้วย “e” คุณจะได้ “He” ไม่ใช่แค่ “e” กล่าวอีกนัยหนึ่ง คุณสามารถคิดว่ารัฐเป็น หน่วยความจำ ของกล่องข้อความ

ความต้องการสถานะในส่วนประกอบ React

มาทำความเข้าใจกับตัวอย่างนี้กัน

Codesanbox ที่ไม่มีสถานะ

ที่นี่เรามี คลิกเคาน์เตอร์ คอมโพเนนต์ที่แสดงจำนวนครั้งที่คลิกปุ่มเพิ่ม

เรากำลังใช้ ตัวแปรท้องถิ่น "เคาน์เตอร์" เพื่อเก็บจำนวนคลิก

แต่ละครั้งที่เราคลิกปุ่มเพิ่ม จัดการคลิก จะมีการเรียกใช้ฟังก์ชัน ฟังก์ชันนี้จะเพิ่มค่าตัวนับทีละ 1 และบันทึกค่าในคอนโซลด้วย

ไปข้างหน้า คลิกปุ่มเพิ่มในการแสดงตัวอย่าง CodeSandbox

ไม่มีอะไรเกิดขึ้น?

ตรรกะของเราดูเหมือนจะถูกต้อง ค่าที่บันทึกไว้ในคอนโซล (CodeSandbox) จะอัปเดตอย่างถูกต้องทุกครั้งที่เราคลิก แต่เหตุใดการอัปเดตนี้จึงไม่แสดงใน UI

เป็นเพราะวิธีการทำงานของ React

  • การเปลี่ยนแปลงตัวแปรในเครื่องจะไม่ทำให้เกิดการเรนเดอร์ซ้ำ
  • ในระหว่างการเรนเดอร์ใหม่ คอมโพเนนต์จะถูกสร้างขึ้นตั้งแต่เริ่มต้น กล่าวคือ ฟังก์ชันของคอมโพเนนต์ (ในตัวอย่างนี้คือฟังก์ชัน ClickCounter) จะถูกดำเนินการอีกครั้ง เนื่องจากตัวแปร (เช่น ตัวนับ) เป็นแบบโลคัลของฟังก์ชัน ค่าก่อนหน้าจึงสูญหายไป

แล้วเราจะทำให้ส่วนประกอบจำค่าระหว่างการเรนเดอร์ได้อย่างไร

ใช่ คุณเข้าใจถูกแล้ว! เราทำเช่นนี้ด้วยความช่วยเหลือของ ใช้สถานะ ตะขอ.

ตะขอ useState

เบ็ด useState มีกลไกเพื่อรักษาสถานะและทริกเกอร์การแสดงผลใหม่

มาดูการใช้งานกัน

import React, { useState } from "react";
const state = useState(initialValue);

// OR

const state = React.useState(initialValue);

เบ็ด useState ส่งคืนอาร์เรย์ที่มีสองรายการ:

  • A ตัวแปรสถานะ ที่คงค่าไว้ระหว่างการแสดงผลซ้ำ ค่าเริ่มต้นที่ส่งผ่านไปยัง useState ถูกกำหนดให้กับตัวแปรสถานะระหว่างการเรนเดอร์ครั้งแรก
  • A ฟังก์ชันเซ็ตเตอร์ ที่อัปเดตตัวแปรสถานะและทริกเกอร์การแสดงผลซ้ำ
const state = useState(0);
const data = state[0];
const setData = state[1];

การใช้ การปรับโครงสร้างอาร์เรย์ เราสามารถ refactor ข้อความข้างต้นให้เป็นข้อความเดียวได้ ดังที่แสดงด้านล่าง:

const [data, setData] = useState(0);

ค่าเริ่มต้นที่ส่งผ่านไปยัง useState จะใช้ระหว่างการเรนเดอร์ครั้งแรกเท่านั้น สำหรับการแสดงผลซ้ำ ระบบจะละเว้น

เคาน์เตอร์กับ useState

ตอนนี้มาอัปเดตตัวอย่างตัวนับก่อนหน้านี้เพื่อรวม useState hook

  • เนื่องจากเราต้องการค่าตัวนับระหว่างการแสดงผลซ้ำ ลองแปลงเป็นสถานะ
const [counter, setCounter] = useState(0);
  • เรียกใช้ setCounter ภายในฟังก์ชัน handleClick
const handleClick = () => {
  setCounter(counter + 1);
  console.log(`%c Counter:${counter}`, "color:green");
};

ฟังก์ชัน setCounter จะอัปเดตค่าตัวนับทีละ 1 และทริกเกอร์การแสดงผลใหม่ เมื่อฟังก์ชันของคอมโพเนนต์ถูกเรียกใช้ในการแสดงผลซ้ำ ตัวแปรสถานะที่ส่งคืนโดย useState จะมีค่าที่อัปเดต

ลองใช้ CodeSandbox ด้วยรหัสที่อัปเดต คลิกปุ่มเพิ่มและดูความมหัศจรรย์ของ useState ในการดำเนินการ

Codesanbox พร้อม useState

คุณสามารถตรวจสอบได้ว่าในการแสดงผลซ้ำ คอมโพเนนต์การทำงาน คลิกเคาน์เตอร์ ถูกเรียกอีกครั้งโดยดูบันทึกของคอนโซล บันทึก “ClickCounter start” ซึ่งเพิ่มในส่วนเริ่มต้นของส่วนประกอบจะถูกบันทึกในการเรนเดอร์แต่ละครั้ง

ก่อนแสดงผล

แสดงผลอีกครั้ง

ฟังก์ชั่นอัพเดท

สมมติว่าเราต้องการเพิ่มมูลค่าของตัวนับ 4 ในแต่ละคลิก

const handleClick = () => {
  setCounter(counter + 1);
    setCounter(counter + 1);
    setCounter(counter + 1);
    setCounter(counter + 1);
    console.log(`%c Counter:${counter}`, "color:green");
    };

สมมติว่าค่าเริ่มต้นของตัวนับคือ 0 คุณคาดว่าจะเห็นอะไรเมื่อคลิกปุ่ม

ไม่มีฟังก์ชั่นอัพเดท

คุณคาดว่าการนับเป็น 4 ใช่ไหม แต่ทำไมเห็นเป็น 1 แทนล่ะ?

ก) การเรนเดอร์แต่ละครั้งเชื่อมโยงกับสถานะ ค่าของสถานะนั้นยังคงถูกล็อคไว้ตลอดอายุของการแสดงผลนั้น

ขอให้สังเกตว่าบันทึกภายในฟังก์ชัน handleClick พิมพ์ค่าตัวนับเป็น 0

ไม่ว่าคุณจะเรียกใช้เมธอด setCounter กี่ครั้ง ค่าของตัวนับจะยังคงเท่าเดิม

const handleClick = () => {
  setCounter(counter + 1);
    setCounter(counter + 1);
    setCounter(counter + 1);
    setCounter(counter + 1);
    console.log(`%c Counter:${counter}`, "color:green");
    };
b) จนกว่าโค้ดทั้งหมดในตัวจัดการเหตุการณ์จะถูกดำเนินการ React จะไม่ทริกเกอร์การเรนเดอร์ซ้ำ

ด้วยเหตุนี้การเรียก setCounter แต่ละครั้งจึงไม่ทริกเกอร์การเรนเดอร์แต่ละครั้ง React จะเพิ่มฟังก์ชัน setter เหล่านี้ในคิวแทน ดำเนินการตามลำดับที่พวกเขาอยู่ในคิว การอัปเดตที่ทำกับสถานะหลังจากดำเนินการคำสั่งทั้งหมดจะสะท้อนให้เห็นในการเรนเดอร์ถัดไป การเข้าคิวของการอัปเดตหลายสถานะนี้เรียกว่า เครื่องผสม. ช่วยให้ React มีประสิทธิภาพมากขึ้น

ดังนั้นเราจึงได้เรนเดอร์เดียวแทนที่จะเป็น 4 เรนเดอร์ที่แตกต่างกัน

ตัวอย่างนี้ง่ายและคุณสามารถแก้ไขปัญหานี้ได้โดยอัปเดตโค้ดตามที่แสดงด้านล่าง:

const handleClick = () => {
setCounter(counter + 4);
    console.log(`%c Counter:${counter}`, "color:green");
    };

แต่ถ้าคุณมีกรณีการใช้งานที่คุณต้องการอัปเดตสถานะหลายครั้งก่อนที่จะแสดงผลครั้งต่อไป

นั่นคือที่ที่ _ ตัวอัปเดต _ ฟังก์ชั่นมาสะดวก

เราสามารถ refactor ตัวอย่างก่อนหน้านี้ด้วยฟังก์ชัน updater ดังนี้:

const handleClick = () => {
  setCounter(prevCounter => prevCounter + 1);
    setCounter(prevCounter => prevCounter + 1);
    setCounter(prevCounter => prevCounter + 1);
    setCounter(prevCounter => prevCounter + 1);
    console.log(`%c Counter:${counter}`, "color:green");
    };

Here ตัวนับก่อนหน้า ⇒ ตัวนับก่อนหน้า + 1 แสดงถึงฟังก์ชันตัวอัปเดต

ตามที่อธิบายไว้ก่อนหน้านี้ คำสั่งตัวอัปเดตเหล่านี้จะถูกจัดคิว (แบตช์) ด้วย

ฟังก์ชันตัวอัปเดตได้รับสถานะที่รอดำเนินการ/ก่อนหน้าซึ่งใช้ในการคำนวณสถานะถัดไป

การแบทช์ฟังก์ชัน Updater

ด้านล่างนี้คือ CodeSandbox ที่เพิ่มฟังก์ชันตัวอัปเดต ลองคลิกที่ปุ่มเพิ่ม

แซนด์บ็อกซ์ฟังก์ชั่น Updater

ฟังก์ชันตัวเริ่มต้น

ลองดูตัวอย่างด้านล่าง ที่นี่เรากำลังเรียกใช้ฟังก์ชัน getItems เพื่อรับค่าเริ่มต้นสำหรับสถานะ

import React from "react";
import { useState } from "react";
function ListItems() { 
  const getItems = () => { 
    console.log(`%c getItems called`, "color:hotpink");
    	return Array(50).fill(0); 
    }; 
  const [items, setItems] = useState(getItems()); 
    
    return ( 
    	<div className="card">
        <ul> {items.map((item, index) => 
        	( <li key={index}>Item {index + 1}		</li>))} 
        </ul> <button onClick={() => setItems([...items, 0])}>Add Item</button> 	</div> );
} 
export default ListItems;

ฟังก์ชันนี้สร้างอาร์เรย์ที่มีขนาด 50 และเติมอาร์เรย์ด้วยศูนย์ อ้างอิงภาพด้านล่าง

อาร์เรย์เต็มไปด้วย 50 ศูนย์

รายการเหล่านี้จะแสดงบนหน้าจอ

ทุกอย่างดูเหมือนจะดี แต่เรามีปัญหาที่นี่

คลิกที่ เพิ่มรายการ ปุ่ม (อยู่หลังรายการ) เพื่อเพิ่มรายการใหม่ในรายการ สังเกตบันทึก

ไม่มีฟังก์ชั่น initializer

คุณเห็นปัญหาที่นี่หรือไม่?

บันทึก "getItems ที่เรียก" จะถูกเพิ่มลงในคอนโซลทุกครั้งที่คุณเพิ่มรายการ ซึ่งหมายความว่ามีการเรียกใช้ฟังก์ชันนี้ในการเรนเดอร์แต่ละครั้ง

โปรดจำไว้ว่า useState จะละเว้นค่าเริ่มต้นที่ส่งผ่านไปยังมันหลังจากการเรนเดอร์ครั้งแรก แต่ในที่นี้ ค่าเริ่มต้นยังคงถูกคำนวณใหม่ สิ่งนี้อาจมีราคาแพงหากเรากำลังสร้างอาร์เรย์ขนาดใหญ่หรือทำการคำนวณจำนวนมาก

เราสามารถแก้ปัญหานี้ได้โดยผ่าน รับรายการ เป็น _ ตัวเริ่มต้น _ การทำงาน.

ตอนนี้เรามาเปลี่ยนโค้ดกันเล็กน้อย

const [items, setItems] = useState(getItems);

ด้วยฟังก์ชั่นเริ่มต้น

ดูหน้าต่างคอนโซลใน CodeSandbox โปรดสังเกตว่าบันทึก "getItems ที่เรียก" จะพิมพ์เฉพาะในการเรนเดอร์แรกเท่านั้น เมื่อมีการเพิ่มรายการที่ตามมา จะไม่มีบันทึกนี้

แม้ว่าจะไม่มีความแตกต่างทางสายตาระหว่างสองตัวอย่าง แต่ในแง่ของประสิทธิภาพนั้นแตกต่างกัน

โปรดจำไว้ว่าเมื่อคุณต้องการฟังก์ชันสำหรับสถานะเริ่มต้น ให้ส่งฟังก์ชันหรือเรียกใช้ฟังก์ชันภายในฟังก์ชันอื่นเสมอ ห้ามเรียกใช้ฟังก์ชันโดยตรง

✅ const [items, setItems] = useState(getItems);
✅ const [items, setItems] = useState(() => getItems());
❌ const [items, setItems] = useState(getItems());

ฉันสามารถมีตะขอ useState ได้กี่อัน

คุณสามารถมี useState hooks ภายในส่วนประกอบได้มากเท่าที่ต้องการ

ดู CodeSandbox

ตะขอ useState หลายอัน

คอมโพเนนต์ด้านล่างมีสามสถานะที่แตกต่างกัน – ชื่อผู้ใช้ รหัสผ่าน KeepMeSignedIn

ลองอัปเดตค่าของชื่อผู้ใช้, keepMeSignedIn สถานะที่อัปเดตจะถูกบันทึกในคอนโซลเมื่อคลิกปุ่มเข้าสู่ระบบ

ไฮไลท์

  • useState จัดเตรียมกลไกในการทริกเกอร์การแสดงผลซ้ำและคงสถานะระหว่างการแสดงผลซ้ำ
  • ใช้ฟังก์ชันตัวอัปเดตเมื่อคุณต้องการ:
    • คำนวณสถานะถัดไปตามสถานะก่อนหน้า
    • ดำเนินการอัปเดตหลายสถานะก่อนการเรนเดอร์ครั้งต่อไป
  • หากได้รับสถานะเริ่มต้นจากฟังก์ชัน - ใช้ไวยากรณ์ของฟังก์ชัน initializer
  • สามารถมี useState hooks ได้หลายตัวภายในคอมโพเนนต์

ชอบโพสต์นี้? แบ่งปันกับผู้อื่น
Orignally เขียนสำหรับบล็อกส่วนตัวของฉัน - https://gauravsen.com/use-state-hook

ประทับเวลา:

เพิ่มเติมจาก Codementor React ข้อเท็จจริง