ใน React ความแตกต่างระหว่าง onChange และ onInput คืออะไร?


89

ฉันได้ลองค้นหาคำตอบสำหรับสิ่งนี้แล้ว แต่ส่วนใหญ่อยู่นอกบริบทของการตอบสนองซึ่งonChangeทำให้เกิดความเบลอ

ในการทดสอบหลาย ๆ ครั้งฉันไม่สามารถบอกได้ว่าเหตุการณ์ทั้งสองนี้แตกต่างกันอย่างไร (เมื่อนำไปใช้กับพื้นที่ข้อความ) ใครช่วยให้ความกระจ่างเกี่ยวกับเรื่องนี้บ้าง?


1
ฉันแก้ไขโพสต์ของฉันให้ชัดเจนขึ้น ฉันพูดเฉพาะเกี่ยวกับ textarea ไม่ใช่ปุ่มตัวเลือกหรือช่องทำเครื่องหมาย
ffxsam

นั่นค่อนข้างผิด React ไม่ใช่แค่ JS และเหตุการณ์ต่างๆจะทำงานแตกต่างกันเล็กน้อยในบางกรณี (เช่นonChange) และไม่การวางข้อความใน textarea (ในการตอบสนอง) จะทริกเกอร์ทั้งonChangeและonInput. อย่าลังเลที่จะทดสอบในซอและคุณจะเห็น
ffxsam

สำหรับ <input> และ <textarea> onChange แทนที่ - และโดยทั่วไปควรใช้แทน - ตัวจัดการเหตุการณ์ oninput ในตัวของ DOM
Roko C.Buljan

ฉันคิดว่า onChange เป็นการเดิมพันที่ปลอดภัยกว่า ฉันพยายามที่จะเปลี่ยนค่าของ textarea โดยทางโปรแกรมโดยคิดว่าอาจonChangeจะทริกเกอร์และonInputจะไม่ทำ แต่ทั้งสองทริกเกอร์
ffxsam

ใช่ตอนนี้หลังจากตรวจสอบ React Docs แล้วฉันเห็นว่า React ทำบางสิ่งได้ค่อนข้างดีเช่น jQuery มันทำให้เหตุการณ์เป็นปกติและใช่ onChange ดูเหมือนจะเป็นวิธีที่ต้องการ และครับ React ไม่มีอะไรมากไปกว่า JS ไม่มีเวทมนตร์พิเศษ เพียงแค่กฎ
Roko C.Buljan

คำตอบ:


81

ดูเหมือนไม่มีความแตกต่างอย่างแท้จริง

ด้วยเหตุผลบางประการจะแนบผู้ฟังComponent.onChangeเข้ากับelement.oninputเหตุการณ์DOM ดูหมายเหตุในเอกสารในแบบฟอร์ม:

ตอบสนองเอกสาร - แบบฟอร์ม

มีคนจำนวนมากขึ้นที่แปลกใจกับพฤติกรรมนี้ สำหรับรายละเอียดเพิ่มเติมโปรดดูปัญหานี้ในตัวติดตามปัญหาการตอบสนอง:

บันทึกว่า onChange ของ React เกี่ยวข้องกับ onInput # 3964 อย่างไร

อ้างจากความคิดเห็นเกี่ยวกับปัญหานั้น:

ฉันไม่เข้าใจว่าทำไม React ถึงเลือกที่จะทำให้ onChange ทำงานเหมือน onInput อย่างที่ฉันบอกเราไม่มีทางที่จะเอาพฤติกรรม onChange แบบเก่ากลับมาได้ เอกสารอ้างว่าเป็น "การเรียกชื่อผิด" แต่ไม่ใช่จริงๆโดยจะเริ่มทำงานเมื่อมีการเปลี่ยนแปลงไม่จนกว่าอินพุตจะสูญเสียโฟกัส

สำหรับการตรวจสอบความถูกต้องบางครั้งเราไม่ต้องการแสดงข้อผิดพลาดในการตรวจสอบความถูกต้องจนกว่าจะพิมพ์เสร็จ หรือบางทีเราไม่ต้องการให้ re-render ทุกครั้งที่กดแป้นพิมพ์ ตอนนี้วิธีเดียวที่จะทำได้คือ onBlur แต่ตอนนี้เราต้องตรวจสอบด้วยว่าค่านั้นเปลี่ยนแปลงด้วยตนเองหรือไม่

ไม่ใช่เรื่องใหญ่ แต่สำหรับฉันแล้วดูเหมือนว่า React โยนเหตุการณ์ที่เป็นประโยชน์ออกไปและเบี่ยงเบนไปจากพฤติกรรมมาตรฐานเมื่อมีเหตุการณ์ที่ทำเช่นนี้อยู่แล้ว

ฉันเห็นด้วย 100% กับความคิดเห็น ... แต่ฉันเดาว่าการเปลี่ยนตอนนี้จะทำให้เกิดปัญหามากกว่าที่จะแก้ได้เนื่องจากมีการเขียนโค้ดจำนวนมากที่อาศัยพฤติกรรมนี้แล้ว

React ไม่ได้เป็นส่วนหนึ่งของคอลเล็กชัน Web API อย่างเป็นทางการ

แม้ว่า React จะสร้างขึ้นบน JS และได้เห็นอัตราการนำไปใช้อย่างมากเนื่องจาก React เทคโนโลยีมีอยู่เพื่อซ่อนฟังก์ชันการทำงานจำนวนมากภายใต้ API ของตัวเอง (ค่อนข้างเล็ก) เมื่อพื้นที่ที่เห็นได้ชัดคือในระบบเหตุการณ์ซึ่งมีหลายสิ่งเกิดขึ้นใต้พื้นผิวซึ่งแตกต่างจากระบบเหตุการณ์ DOM มาตรฐานอย่างสิ้นเชิง ไม่เพียงแค่ในแง่ของเหตุการณ์ที่ทำสิ่งใด แต่ยังรวมถึงเมื่อข้อมูลได้รับอนุญาตให้คงอยู่ในขั้นตอนของการจัดการเหตุการณ์ คุณสามารถอ่านเพิ่มเติมได้ที่นี่:

ตอบสนองระบบเหตุการณ์


1
ย่อหน้าสุดท้ายทำให้เข้าใจผิด / ไม่เป็นความจริง
rounce

1
React ไม่ใช่เฟรมเวิร์ก (ที่ให้การกำหนดเส้นทางการคงอยู่การจัดการเครือข่าย I / O ฯลฯ ) เป็นไลบรารีที่ให้นามธรรมหลักสำหรับการจัดการโครงสร้างการแสดงผลผ่านแบ็กเอนด์เฉพาะของแพลตฟอร์ม (react-dom, react-native, ฯลฯ ).
rounce

7
ว้าวโอเค ฉัน sorta ยอมรับว่ามันเป็นห้องสมุดมากกว่าเฟรมเวิร์ก แต่จะเรียกย่อหน้าที่ทำให้เข้าใจผิด / ไม่จริงเพราะเหตุนี้ ?? ฉันไม่เห็นด้วยกับความคิดที่ว่าเฟรมเวิร์กควรให้ทุกด้านของเว็บแอปพลิเคชัน ... มีกรอบการกำหนดเส้นทางเช่นหรือสิ่งต่างๆเช่น redux ซึ่งเป็นกรอบการจัดการของรัฐ ในความคิดของฉันเฟรมเวิร์กช่วยให้คุณเติมชิ้นส่วนของเครื่องจักรที่ใช้งานได้ ในกรณีของ Redux คุณต้องกรอกตัวลด ในกรณีของ Express คุณต้องกรอกตัวจัดการคำขอและมิดเดิลแวร์ เป็นต้น แต่ความแตกต่างคือพื้นที่สีเทาที่จะพูดน้อยที่สุด
Stijn de Witt

4
เห็นด้วยอย่างสิ้นเชิง. การโต้เถียงเกี่ยวกับความแตกต่างระหว่างไลบรารีและเฟรมเวิร์กเป็นเรื่องที่เสียเวลามาก ขึ้นอยู่กับระดับของนามธรรมที่คุณกำลังทำงานอยู่
swyx

1
@chadsteele ขอบคุณเพื่อนดีมากที่ได้ยินมันช่วยคุณ
Stijn de Witt

23

ไม่มีความแตกต่างกัน

React ไม่มีลักษณะการทำงานของเหตุการณ์ 'onChange' เริ่มต้น 'onChange' ที่เราเห็นในการตอบสนองมีลักษณะการทำงานของเหตุการณ์ 'onInput' เริ่มต้น ดังนั้นเพื่อตอบคำถามของคุณไม่มีความแตกต่างในการตอบสนองทั้งคู่ ฉันได้ยกปัญหาเกี่ยวกับ GitHub เกี่ยวกับสิ่งเดียวกันและนี่คือสิ่งที่พวกเขาพูดเกี่ยวกับเรื่องนี้:

ฉันคิดว่าในเวลาที่ตัดสินใจครั้งนี้ (~ 4 ปีที่แล้ว?) onInput ไม่ทำงานอย่างสม่ำเสมอระหว่างเบราว์เซอร์และสร้างความสับสนให้กับผู้คนที่มาที่เว็บจากแพลตฟอร์มอื่น ๆ เนื่องจากพวกเขาคาดว่าจะเกิดเหตุการณ์ "การเปลี่ยนแปลง" ไฟในทุกการเปลี่ยนแปลง ในกรณีของการตอบสนองเป็นปัญหาที่ใหญ่กว่าเพราะหากคุณไม่สามารถจัดการกับการเปลี่ยนแปลงได้เร็วพออินพุตที่ควบคุมจะไม่อัปเดตทำให้ผู้คนคิดว่า React เสีย ดังนั้นทีมจึงเรียกมันว่า onChange

หากมองย้อนกลับไปอาจเป็นความคิดที่ดีกว่าที่จะใส่ polyfill onInput และเก็บชื่อไว้แทนที่จะเปลี่ยนพฤติกรรมของเหตุการณ์อื่น แต่เรือลำนั้นแล่นไปนานแล้ว เราอาจทบทวนการตัดสินใจนี้อีกครั้งในอนาคต แต่ฉันขอแนะนำให้คุณถือว่าการตอบสนองของ DOM เป็นนิสัยแปลก ๆ (ซึ่งคุณจะคุ้นเคยอย่างรวดเร็ว)

https://github.com/facebook/react/issues/9567

นอกจากนี้บทความนี้จะให้ข้อมูลเชิงลึกเพิ่มเติม เนื่องจากเป็นวิธีแก้ปัญหาสำหรับค่าเริ่มต้น "onChange" ที่หายไปบทความนี้จึงแนะนำให้ฟังเหตุการณ์ "onBlur"

https://www.peterbe.com/plog/onchange-in-reactjs


ดูเหมือนฉันจะสะดุดกับความแตกต่าง onChange ของ React ไม่ทริกเกอร์เมื่อเลือกและแทนที่อักขระด้วยอักขระเดียวกัน onInput จะทริกเกอร์
ต่อEnström

3

เมื่อเร็ว ๆ นี้ฉันได้รับข้อบกพร่องที่onChangeไม่อนุญาตให้คัดลอกและวางในช่องป้อนข้อมูลบน IE11 ในขณะที่onInputเหตุการณ์จะเอื้อให้เกิดพฤติกรรมนั้น ฉันไม่พบเอกสารใด ๆ ที่จะอธิบายสิ่งนี้ในเอกสาร แต่แสดงว่ามีความแตกต่างระหว่างทั้งสอง (คาดว่าหรือไม่)


2

ดังที่คุณเห็นในความคิดเห็นต่างๆที่นี่ React ปฏิบัติต่อ onChange และ onInput เหมือนกันแทนที่จะถกเถียงถึงข้อดีของการตัดสินใจนี้ นี่คือวิธีแก้ปัญหา

ใช้ onBlur เมื่อคุณไม่ต้องการประมวลผลการแก้ไขของผู้ใช้จนกว่าจะเสร็จสิ้น :)


1

สำหรับใครก็ตามที่สะดุดกับปัญหานี้ที่กำลังมองหาวิธีการฟังchangeเหตุการณ์ที่ใช้ DOM จริงนี่คือวิธีที่ฉันทำ (เขียนใน TypeScript):

import { Component, createElement, InputHTMLAttributes } from 'react';

export interface CustomInputProps {
    onChange?: (event: Event) => void;
    onInput?: (event: Event) => void;
}

/**
 * This component restores the 'onChange' and 'onInput' behavior of JavaScript.
 *
 * See:
 * - https://reactjs.org/docs/dom-elements.html#onchange
 * - https://github.com/facebook/react/issues/3964
 * - https://github.com/facebook/react/issues/9657
 * - https://github.com/facebook/react/issues/14857
 */
export class CustomInput extends Component<Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'onInput' | 'ref'> & CustomInputProps> {
    private readonly registerCallbacks  = (element: HTMLInputElement | null) => {
        if (element) {
            element.onchange = this.props.onChange ? this.props.onChange : null;
            element.oninput = this.props.onInput ? this.props.onInput : null;
        }
    };

    public render() {
        return <input ref={this.registerCallbacks} {...this.props} onChange={undefined} onInput={undefined} />;
    }
}

โปรดแจ้งให้เราทราบหากคุณเห็นวิธีปรับปรุงแนวทางนี้หรือพบปัญหา ซึ่งแตกต่างจากblurที่changeเหตุการณ์ยังจะถูกเรียกเมื่อผู้ใช้กดเข้าและจะถูกเรียกเฉพาะในกรณีที่ค่าการเปลี่ยนแปลงจริง

ฉันยังคงได้รับประสบการณ์กับCustomInputส่วนประกอบนี้ ตัวอย่างเช่นช่องทำเครื่องหมายทำงานแปลก ๆ ฉันต้องกลับด้านevent.target.checkedในonChangeตัวจัดการในขณะที่ส่งค่าไปยังช่องทำเครื่องหมายด้วยcheckedหรือกำจัดการผกผันนี้เมื่อส่งค่าไปยังช่องทำเครื่องหมายด้วยdefaultCheckedแต่สิ่งนี้จะแบ่งช่องทำเครื่องหมายหลายช่องที่แสดงสถานะเดียวกันในที่ต่างๆบนหน้าให้ซิงค์ . (ในทั้งสองกรณีฉันไม่ได้ส่งonInputตัวจัดการไปที่CustomInputช่องทำเครื่องหมายสำหรับ)


0

ความแตกต่างอย่างหนึ่งที่ดูเหมือนว่าonChangeจะไม่เกิดขึ้นเมื่อเลือกและแทนที่อักขระด้วยอักขระเดียวกันในขณะที่onInputเป็น

ดูแซนด์บ็อกซ์นี้: https://codesandbox.io/s/react-onchange-vs-oninput-coggf?file=/src/App.js

  • พิมพ์ "A" ในช่องจากนั้นเลือกทั้งหมดแล้วพิมพ์ "B" นี้จะทำให้เหตุการณ์ 4 2 2onChangeonInput
  • ตอนนี้เลือกและพิมพ์ "B" อีกครั้งนี้จนทริกเกอร์ใหม่onInputเหตุการณ์ onChangeแต่ไม่มี
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.