javascript - Skjult input utløser ikke Reacts onChange-hendelse


Jeg står overfor et merkelig problem med inndatafeltet i React. Jeg er klar over at hidden-input ikke utløser input eller change-hendelser. Men selv om jeg utløser dem manuelt, blir ikke Reacts onChange-hendelse fortsatt kalt.

Jeg utløser begge, change og input hendelser her fordi Reacts onChange faktisk er input-hendelsen. Når jeg setter opp en hendelseslytter på inputRef (addEventListener("change",() => { ... })) for testformål, blir den kalt uten problemer. Imidlertid viser det seg at React har noen problemer med å fange den.

Her er min nåværende kode:

const [fieldValue, setFieldValue] = useState(0);
const inputRef = useRef<HTMLInputElement>(null);

const handleClick = useCallback((): void => {
    if (inputRef.current) {
        inputRef.current.dispatchEvent(new Event("change", { bubbles: true }));
        inputRef.current.dispatchEvent(new Event("input", { bubbles: true }));
    }

    setFieldValue(prev => prev + 1);
}, []);

JSX :

<input type="hidden" ref={inputRef} value={fieldValue} onChange={(e) => { console.log("React:onChange"); }} />
<button type="button" onClick={handleClick}>Hit it</button>

Gjør jeg noe galt her? Hva mer må jeg gjøre for å utløse Reacts onChange-hendelse på riktig måte?

Svar #1

Hendelser i React håndteres av react-dom, gjennom forskjellige plugins. Inndatameldinger håndteres med SimpleEventPlugin (se https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SimpleEventPlugin.js)

Denne pluginen mottar en hendelse og videresender den uten å forstyrre for mye. Så du kan sende den som en innebygd hendelse, og den vil bli utløst som en SyntheticEvent uten store endringer.

Endringshendelsen håndteres av ChangeEventPlugin (se https://github.com/facebook/react/blob/master/packages/react-dom/src/events/ChangeEventPlugin.js). Dette pluginet har følgende formål:

Denne pluginen oppretter en onChange-hendelse som normaliserer endringshendelser på tvers av formelementer. Denne hendelsen utløses på et tidspunkt når det er mulig å endre elementets verdi uten å se en flimmer.

Pluginet utløser endringshendelsen, men ikke nøyaktig som den innebygde endringshendelsen. Den innebygde endringshendelsen på et tekstinndataelement, for eksempel, utløses bare ved blur. Se https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event

For noen elementer, inkludert , utløses ikke endringshendelsen før kontrollen mister fokus.

Men med React utløses det på en annen måte, ved hver endring av verdien. For å gjøre dette håndteres det av plugin-en som har disse begrensningene: Hendelsen vil kun bli utløst på disse inputtypene:

  color: true,
  date: true,
  datetime: true,
  'datetime-local': true,
  email: true,
  month: true,
  number: true,
  password: true,
  range: true,
  search: true,
  tel: true,
  text: true,
  time: true,
  url: true,
  week: true

Så på skjulte inputfelt stopper React sendingen.

En annen begrensning er at hendelsen kun vil bli sendt hvis verdien av inndataen faktisk endres. Se

 if (updateValueIfChanged(targetNode)) {
    return targetInst;
  }

Så selv på en støttet input, vil ikke din dispatch gå gjennom plugin-en. Du kan se det i dette utdraget, ved å manipulere en metode som brukes for å hente verdien av inputen kan du klare å sende hendelsen.

class Hello extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();

  }

  componentDidMount() {
  //_valueTracker.getValue is used for comparing the values of the input. If you write fake value in the input it'll dispatch, anything else won't
    document.getElementsByTagName('INPUT')[0]._valueTracker.getValue = () => {
      return 'fake value'
    }
  }

  render() {

    const handleClick = () => {
      if (this.myRef) {

        this.myRef.current.dispatchEvent(new Event("change", {
          bubbles: true
        }));


      }
    };

    return <div > < input type = "text"
    ref = {
      this.myRef
    }
    onChange = {
      (e) => {
        console.log("React:onChange");
      }
    }
    /><button type="button"  onClick={handleClick}>Hit it</button > < /div>

  }
}

ReactDOM.render( <
  Hello name = "World" / > ,
  document.getElementById('container')
);
<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="container">

</div>

Så for et skjult input finnes det ingen måte å få det til å fungere på (merk at dette er i samsvar med standarden). For et tekstfelt (eller andre støttede input) kan du det, men du må mer eller mindre manipulere metoden ved å sammenligne verdien før hendelsen med verdien etter hendelsen.

Svar #2

onChange-hendelsen utløses når brukeren endrer elementverdien - i ditt tilfelle endrer du programmessig inndataverdien, og dermed utløses ikke onChange

onInput-hendelser utløses når verdien endres - uansett hvordan. Derfor må du legge til onInput i stedet for onChange.

<input type="hidden" ref={inputRef} value={fieldValue} onInput={(e) => { console.log("React:onChange"); }} />

Her er et fungerende eksempel: Sandbox

Relaterte artikler