javascript - useState() ใน React คืออะไร?
ฉันกำลังเรียนรู้แนวคิดของ hooks ใน React และพยายามทำความเข้าใจตัวอย่างด้านล่าง
import { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
ตัวอย่างข้างต้นเพิ่มค่าตัวนับในตัวพารามิเตอร์ของฟังก์ชันตัวจัดการเอง ถ้าฉันต้องการแก้ไขค่าตัวนับภายในฟังก์ชันตัวจัดการเหตุการณ์ล่ะ
พิจารณาตัวอย่างด้านล่าง:
setCount = () => {
//how can I modify count value here. Not sure if I can use setState to modify its value
//also I want to modify other state values as well here. How can I do that
}
<button onClick={() => setCount()}>
Click me
</button>
คำตอบ #1
React hooks เป็นวิธีใหม่ (ยังอยู่ระหว่างการพัฒนา) ในการเข้าถึงคุณสมบัติหลักของ React เช่น state โดยไม่ต้องใช้คลาส ในตัวอย่างของคุณ หากคุณต้องการเพิ่มตัวนับโดยตรงในฟังก์ชันตัวจัดการโดยไม่ระบุโดยตรงในพร็อพ onClick คุณสามารถทำบางอย่างเช่นนี้ได้:
...
const [count, setCounter] = useState(0);
const [moreStuff, setMoreStuff] = useState(...);
...
const setCount = () => {
setCounter(count + 1);
setMoreStuff(...);
...
};
และเมื่อคลิก:
<button onClick={setCount}>
Click me
</button>
const [count, setCounter] = useState(0);
useState(0) จะส่งกลับเป็นทูเพิล โดยที่พารามิเตอร์ตัวแรก count คือสถานะปัจจุบันของเคาน์เตอร์ และ setCounter คือเมธอดที่จะช่วยให้เราสามารถอัปเดตสถานะของเคาน์เตอร์ได้ เราสามารถใช้เมธอด setCounter เพื่ออัปเดตสถานะของ count ได้ทุกที่ - ในกรณีนี้เรากำลังใช้งานมันภายในฟังก์ชัน setCount ซึ่งเราสามารถทำสิ่งต่าง ๆ ได้มากขึ้น; แนวคิดของ hooks คือเราสามารถทำให้โค้ดของเรามีความเป็นฟังก์ชันมากขึ้นและหลีกเลี่ยง คอมโพเนนต์แบบคลาส หากไม่ต้องการหรือไม่จำเป็น
ฉันเขียนบทความครบถ้วนเกี่ยวกับ hooks พร้อมตัวอย่างหลายตัวอย่าง (รวมถึง counters) เช่น codepen นี้ ฉันใช้ useState, useEffect, useContext, และ custom hooks ฉันสามารถอธิบายรายละเอียดเพิ่มเติมเกี่ยวกับการทำงานของ hooks ในคำตอบนี้ได้ แต่เอกสารนั้นอธิบายเกี่ยวกับ state hook และ hooks อื่น ๆ ได้อย่างละเอียดอยู่แล้ว
อัปเดต: Hooks ไม่ใช่ข้อเสนออีกต่อไป ตั้งแต่เวอร์ชัน 16.8 พวกมันสามารถใช้งานได้แล้ว มีส่วนหนึ่งในเว็บไซต์ของ React ที่ตอบคำถามที่พบบ่อยบางข้อ
คำตอบ #2
useState เป็นหนึ่งใน hook ของ react ที่มีอยู่ในเวอร์ชัน 0.16.7
useState ควรใช้เฉพาะภายในคอมโพเนนต์แบบฟังก์ชันเท่านั้น useState เป็นวิธีที่เหมาะสมหากเราต้องการสถานะภายในและไม่จำเป็นต้องใช้ตรรกะที่ซับซ้อนมากขึ้น เช่น เมธอดวงจรชีวิต
const [state, setState] = useState(initialState);
ส่งกลับค่าที่มีสถานะ และฟังก์ชันเพื่อปรับปรุงมัน
ในระหว่างการเรนเดอร์เริ่มต้น สถานะที่ส่งกลับ (state) จะเหมือนกับค่าที่ส่งเป็นอาร์กิวเมนต์ตัวแรก (initialState)
ฟังก์ชัน setState ถูกใช้เพื่ออัปเดตสถานะ มันรับค่าสถานะใหม่และเข้าคิวการเรนเดอร์ซ้ำของคอมโพเนนต์
โปรดทราบ ว่า useState hook callback สำหรับการอัปเดตสถานะ มีพฤติกรรมแตกต่าง จาก this.setState ของคอมโพเนนต์ เพื่อแสดงให้คุณเห็นความแตกต่าง ผมได้เตรียมตัวอย่างสองตัวอย่างไว้
class UserInfoClass extends React.Component {
state = { firstName: 'John', lastName: 'Doe' };
render() {
return <div>
<p>userInfo: {JSON.stringify(this.state)}</p>
<button onClick={() => this.setState({
firstName: 'Jason'
})}>Update name to Jason</button>
</div>;
}
}
// Please note that new object is created when setUserInfo callback is used
function UserInfoFunction() {
const [userInfo, setUserInfo] = React.useState({
firstName: 'John', lastName: 'Doe',
});
return (
<div>
<p>userInfo: {JSON.stringify(userInfo)}</p>
<button onClick={() => setUserInfo({ firstName: 'Jason' })}>Update name to Jason</button>
</div>
);
}
ReactDOM.render(
<div>
<UserInfoClass />
<UserInfoFunction />
</div>
, document.querySelector('#app'));<script src="https://unpkg.com/[email /umd/react.development.js"></script>
<script src="https://unpkg.com/[email /umd/react-dom.development.js"></script>
<div id="app"></div>วัตถุใหม่จะถูกสร้างขึ้นเมื่อใช้ callback setUserInfo สังเกตว่าเราสูญเสียค่าของคีย์ lastName เพื่อแก้ไขปัญหานั้น เราสามารถส่งฟังก์ชันภายใน useState
setUserInfo(prevState => ({ ...prevState, firstName: 'Jason' })
ดูตัวอย่าง:
// Please note that new object is created when setUserInfo callback is used
function UserInfoFunction() {
const [userInfo, setUserInfo] = React.useState({
firstName: 'John', lastName: 'Doe',
});
return (
<div>
<p>userInfo: {JSON.stringify(userInfo)}</p>
<button onClick={() => setUserInfo(prevState => ({
...prevState, firstName: 'Jason' }))}>
Update name to Jason
</button>
</div>
);
}
ReactDOM.render(
<UserInfoFunction />
, document.querySelector('#app'));<script src="https://unpkg.com/[email /umd/react.development.js"></script>
<script src="https://unpkg.com/[email /umd/react-dom.development.js"></script>
<div id="app"></div>แตกต่างจากเมธอด setState ที่พบในคอมโพเนนต์แบบคลาส useState จะไม่ผสานวัตถุอัปเดตโดยอัตโนมัติ คุณสามารถจำลองพฤติกรรมนี้ได้โดยการรวมรูปแบบฟังก์ชันอัปเดตกับไวยากรณ์การกระจายวัตถุ:
setState(prevState => { // Object.assign would also work return {...prevState, ...updatedValues}; });
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ useState ให้ดูเอกสารอย่างเป็นทางการ
คำตอบ #3
ไวยากรณ์ของฮุ๊ก useState นั้นตรงไปตรงมา
const [value, setValue] = useState(defaultValue)ถ้าคุณไม่คุ้นเคยกับไวยากรณ์นี้ ให้ไปที่นี่
ผมขอแนะนำให้คุณอ่านเอกสาร มีคำอธิบายที่ดีเยี่ยมพร้อมตัวอย่างจำนวนพอสมควร
import { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
// its up to you how you do it
const buttonClickHandler = e => {
// increment
// setCount(count + 1)
// decrement
// setCount(count -1)
// anything
// setCount(0)
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={buttonClickHandler}>
Click me
</button>
</div>
);
}คำตอบ #4
useState() เป็น React hook Hooks ทำให้สามารถใช้ state และความสามารถในการเปลี่ยนแปลงภายในฟังก์ชันคอมโพเนนต์ได้
แม้ว่าคุณจะไม่สามารถใช้ hooks ภายในคลาสได้ แต่คุณสามารถห่อหุ้มคอมโพเนนต์คลาสของคุณด้วยคอมโพเนนต์ฟังก์ชันหนึ่งและใช้ hooks จากมันได้ นี่เป็นเครื่องมือที่ยอดเยี่ยมสำหรับการย้ายคอมโพเนนต์จากรูปแบบคลาสไปเป็นฟังก์ชัน นี่คือตัวอย่างที่สมบูรณ์:
สำหรับตัวอย่างนี้ฉันจะใช้คอมโพเนนต์ตัวนับ นี่คือตัวมัน:
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = { count: props.count };
}
inc() {
this.setState(prev => ({count: prev.count+1}));
}
render() {
return <button onClick={() => this.inc()}>{this.state.count}</button>
}
}
ReactDOM.render(<Hello count={0}/>, document.getElementById('root'))<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'></div>นี่คือคอมโพเนนต์แบบคลาสง่าย ๆ ที่มี state นับจำนวน และการอัปเดต state ทำโดยเมธอด นี่เป็นรูปแบบที่พบได้บ่อยในคอมโพเนนต์แบบคลาส สิ่งแรกคือการห่อหุ้มมันด้วยคอมโพเนนต์แบบฟังก์ชันที่มีชื่อเดียวกัน ซึ่งส่งตัวแปรทั้งหมดไปยังคอมโพเนนต์ที่ถูกห่อ นอกจากนี้คุณยังจำเป็นต้องเรนเดอร์คอมโพเนนต์ที่ถูกห่อในฟังก์ชัน return ดังนี้:
function Hello(props) {
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = { count: props.count };
}
inc() {
this.setState(prev => ({count: prev.count+1}));
}
render() {
return <button onClick={() => this.inc()}>{this.state.count}</button>
}
}
return <Hello {...props}/>
}
ReactDOM.render(<Hello count={0}/>, document.getElementById('root'))<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'></div>นี่คือคอมโพเนนต์เดียวกันเป๊ะ ๆ มีพฤติกรรมเดียวกัน ชื่อเดียวกัน และคุณสมบัติเดียวกัน ตอนนี้เรามายกสถานะการนับไปยังคอมโพเนนต์ฟังก์ชัน นี่คือลำดับขั้นตอน:
function Hello(props) {
const [count, setCount] = React.useState(0);
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = { count: props.count };
}
inc() {
this.setState(prev => ({count: prev.count+1}));
}
render() {
return <button onClick={() => setCount(count+1)}>{count}</button>
}
}
return <Hello {...props}/>
}
ReactDOM.render(<Hello count={0}/>, document.getElementById('root'))<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js" integrity="sha256-3vo65ZXn5pfsCfGM5H55X+SmwJHBlyNHPwRmWAPgJnM=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js" integrity="sha256-qVsF1ftL3vUq8RFOLwPnKimXOLo72xguDliIxeffHRc=" crossorigin="anonymous"></script>
<div id='root'></div>โปรดสังเกตว่าฟังก์ชัน inc ยังคงอยู่ มันจะไม่ทำร้ายใคร จริงๆ แล้วนี่คือโค้ดที่ตายแล้ว นี่คือแนวคิด แค่คงการยกสถานะขึ้นไป เมื่อคุณเสร็จแล้วคุณสามารถลบคอมโพเนนต์คลาสได้:
function Hello(props) {
const [count, setCount] = React.useState(0);
return <button onClick={() => setCount(count+1)}>{count}</button>;
}
ReactDOM.render(<Hello count={0}/>, document.getElementById('root'))<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js" integrity="sha256-3vo65ZXn5pfsCfGM5H55X+SmwJHBlyNHPwRmWAPgJnM=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js" integrity="sha256-qVsF1ftL3vUq8RFOLwPnKimXOLo72xguDliIxeffHRc=" crossorigin="anonymous"></script>
<div id='root'></div>แม้ว่าสิ่งนี้จะทำให้สามารถใช้งานฮู๊กส์ภายในคอมโพเนนต์แบบคลาสได้ แต่ผมจะไม่แนะนำให้คุณทำเช่นนั้น เว้นแต่ว่าคุณกำลังย้ายเหมือนที่ผมทำในตัวอย่างนี้ การผสมระหว่างคอมโพเนนต์แบบฟังก์ชันและแบบคลาสจะทำให้การจัดการสถานะวุ่นวาย ผมหวังว่าสิ่งนี้จะช่วยได้
ด้วยความนับถือ
