การจัดการเหตุการณ์
การจัดการเหตุการณ์ที่เกิดบน React elements นั้นมีความคล้ายคลึงกับการจัดการเหตุการณ์บน DOM elements มาก มีเพียงไวยากรณ์ที่ต่างกันเล็กน้อย
- เหตุการณ์ที่เกิดบน React ใช้วิธีเขียนชื่อแบบ camelCase แทนที่จะเป็นภาษาอังกฤษตัวพิมพ์เล็ก
- เราส่งฟังก์ชั่นเข้ามาเป็นตัวจัดการเหตุการณ์ใน JSX เลย แทนที่จะเป็น string
ยกตัวอย่างเช่น ในกรณีที่เป็น HTML:
<button onclick="activateLasers()">
Activate Lasers
</button>
จะเห็นว่าแตกต่างจาก React เพียงเล็กน้อย
<button onClick={activateLasers}> Activate Lasers
</button>
สิ่งที่แตกต่างอีกอย่างใน React คือเราไม่สามารถใช้การคืนค่า false
ในการป้องกันสิ่งที่จะเกิดขึ้นหลังจากเหตุการณ์นั้น เราต้องเรียก preventDefault
เอง ยกตัวอย่างเช่นถ้าต้องการจะป้องกันการเปิดหน้าใหม่หลังจากที่กดลิงค์ ในกรณีที่เป็น HTML นั้นสามารถเขียนได้ในลักษณะนี้
<a href="#" onclick="console.log('The link was clicked.'); return false">
Click me
</a>
ส่วนใน React นั้นจะเขียนแบบนี้
function ActionLink() {
function handleClick(e) { e.preventDefault(); console.log('The link was clicked.'); }
return (
<a href="#" onClick={handleClick}> Click me
</a>
);
}
โดย e
ในที่นี้เป็นข้อมูลที่สังเคราะห์ขึ้นจากเหตุการณ์นั้น ซึ่ง React สร้างขึ้นมาตามข้อกำหนดของ W3C ดังนั้นจึงไม่ต้องกังวลกับการรองรับการใช้งานในหลายบราว์เซอร์ ดูรายละเอียดเพิ่มเ
ในการเขียน React นั้น มักจะไม่จำเป็นที่ต้องเรียก addEventListener
เพื่อเพิ่มข้อมูลการรับฟัง (listener) บน DOM element หลังจากที่มันถูกสร้าง เราเพียงแค่ให้ข้อมูลการรับฟัง(listener) เมื่อ element นั้นถูกนำไปแสดงผลในครั้งแรก
เมื่อเขียนคอมโพเนนท์ขึ้นมาด้วย คลาส ES6 ตัวจัดการเหตุการณ์โดยทั่วไปนั้นคือ method ของคลาส ยกตัวอย่างเช่นคอมโพเนนท์ Toggle
ที่มีการแสดงสถานะ “เปิด” (ON) และ “ปิด” (OFF) โดยผู้ใช้สามารถสลับค่าไปมาได้
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// การ bind นี้เป็นสิ่งที่จำเป็นและทำให้ `this` ใช้งานได้ใน callback this.handleClick = this.handleClick.bind(this); }
handleClick() { this.setState(state => ({ isToggleOn: !state.isToggleOn })); }
render() {
return (
<button onClick={this.handleClick}> {this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
เราควรจะต้องระมัดระวังในค่า this
ที่มากับ callback ของ JSX โดยในคลาส JavaScript นั้น method จะยังไม่ได้ถูก bind มาให้ ดังนั้นถ้าเราลืม bind this.handleClick
แล้วส่งมันเข้าไปใน onClick
เมื่อฟังก์ชันนี้ถูกเรียก ค่าของ this
ในที่นี้จะกลายเป็น undefined
ทันที
นี้ไม่ใช่สิ่งที่พบเจอได้แค่ใน React แต่มันเป็นวิธีการทำงานของฟังก์ชั่น JavaScritp ทั่วไป นั้นคือโดยทั่วไปแล้วถ้าเราอ้างถึงฟังก์ชั่นโดยที่ไม่มี ()
ต่อท้ายเช่น onClick={this.handleClick}
เราควร bind method นั้น
ถ้าการเรียก bind
นั้นดูไม่น่าใช้ ยังมีอีกสองวิธีที่สามารถทำได้ โดยใช้ ไวยากรณ์ class fields ที่ยังอยู่ในช่วงทดลองนั้นจัดการ bind ให้กับ callback
class LoggingButton extends React.Component {
// ไวยากรณ์แบบนี้จะให้ทำให้ `this` นั้นถูก bind ภายใน handleClick // ระวัง: ไวยากรณ์แบบนี้ยังอยู่ในช่วง *ทดลอง* handleClick = () => { console.log('this is:', this); }
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
ไวยากรณ์ในรูปแบบนี้เปิดให้ใช้งานเป็นค่าเริ่มต้นแล้วใน Create React App
แต่ถ้าไม่ได้ใช้ไวยากรณ์ class fields เราก็สามารถใช้ฟังก์ชั่นแบบลูกศร ใน callback ได้
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// ไวยากรณ์แบบนี้จะให้ทำให้ `this` นั้นถูก bind ภายใน handleClick return ( <button onClick={() => this.handleClick()}> Click me
</button>
);
}
}
ปัญหาของการเขียนแบบนี้คือจะมีการสร้าง callback ใหม่ทุกครั้งที่มีการแสดงผล LogginButton
ซึ่งโดยทั่วไปแล้วสามารถทำได้ แต่ถ้า callback นี้ถูกส่งเข้าเป็น prop ของคอมโพเนนท์ที่อยู่ภายในอีก อาจจะส่งผลให้คอมโพเนนท์เหล่านั้นมีการสร้างการแสดงผลใหม่อย่างไม่จำเป็น เราจึงแนะนำให้ทำการ bind ที่ส่วน constructor หรือใช้ไวยากรณ์ class fields เพื่อหลีกเลี่ยงปัญหาด้านประสิทธิภาพ
การส่งค่าเข้าไปยังการจัดการเหตุการณ์
ภายใน Loop เรามักจะต้องการพารามิเตอร์ที่เพิ่มขึ้นในการจัดการเหตุการณ์ ยกตัวอย่างเช่นถ้า id
เป็นค่าเฉพาะของแต่ละแถว(ID) หรือไม่ตัวอย่างข้างล่างนี้ควรจะทำงานได้
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
การเขียนทั้งสองบรรทัดบนนั้นเหมือนกัน โดยใช้ฟังก์ชั่นแบบลูกศร และ Function.prototype.bind
ตามลำดับ
โดยทั้งสองแบบนั้นจะได้รับ e
ซึ่งเป็นข้อมูลเหตุการณ์ของ React โดยมันจะถูกส่งเข้าไปลำดับที่สองต่อจาก ID ถ้าเขียนแบบฟังก์ชั่นลูกศร เราต้องเขียน e
ลงไปเองให้ชัดเจนเลย แต่ในแบบ bind
ค่าของ e
นั้นจะถูกส่งต่อเข้าไปเองโดยอัตโนมัติ