De beste manier om event-handlers te binden in React

Afbeelding: Flickr van liz west

Bindende event-handlers in React kunnen lastig zijn (je moet JavaScript daarvoor bedanken). Voor degenen die de geschiedenis van Perl en Python kennen, zouden TMTOWTDI (er is meer dan één manier om het te doen) en TOOWTDI (er is slechts één manier om het te doen) bekende woorden moeten zijn. Helaas is JavaScript, althans voor gebeurtenisbinding, een TMTOWTDI-taal, waardoor ontwikkelaars altijd in de war raken.

In dit bericht zullen we de gebruikelijke manieren verkennen om eventbindingen te maken in React, en ik zal je hun voor- en nadelen laten zien. En het allerbelangrijkste, ik zal je helpen de "Only One Way" te vinden - of tenminste mijn favoriet.

In dit bericht wordt ervan uitgegaan dat u de noodzaak van binden begrijpt, zoals waarom we this.handler.bind (this) moeten doen, of het verschil tussen function () {console.log (this); } en () => {console.log (this); }. Als je in de war raakt over deze vragen, had Saurabh Misra een geweldige post waarin ze werden uitgelegd.

Dynamische binding in render ()

Het eerste veelgebruikte geval is het aanroepen van .bind (this) in de functie render (). Bijvoorbeeld:

klasse HelloWorld breidt component {
  handleClick (event) {}
  render () {
    terug (
      

Hallo, {this.state.name}!

           );   } }

Natuurlijk zal dit werken. Maar denk aan één ding: wat gebeurt er als this.state.name verandert?

Je zou kunnen zeggen dat het veranderen van this.state.name ervoor zorgt dat de component opnieuw wordt weergegeven (). Is goed. De component wordt weergegeven om het naamgedeelte bij te werken. Maar wordt de knop weergegeven?

Overweeg het feit dat React de virtuele DOM gebruikt. Wanneer renderen plaatsvindt, wordt de bijgewerkte virtuele DOM vergeleken met de vorige virtuele DOM en worden de gewijzigde elementen alleen bijgewerkt naar de werkelijke DOM-structuur.

In ons geval, wanneer render () wordt aangeroepen, wordt this.handleClick.bind (this) ook aangeroepen om de handler te binden. Deze aanroep genereert een geheel nieuwe handler, die compleet anders is dan de handler die werd gebruikt toen render () de eerste keer werd aangeroepen!

Virtuele DOM voor dynamische binding. Elementen in Blauw worden opnieuw gerenderd.

Zoals in het bovenstaande diagram, toen render () eerder werd aangeroepen, retourneerde this.handleClick.bind (this) funcA zodat React wist dat onChange funcA was.

Later, wanneer render () opnieuw wordt aangeroepen, retourneerde this.handleClick.bind (this) funcB (merk op dat er telkens een nieuwe functie wordt geretourneerd). Op deze manier weet React dat onChange niet langer funcA is, wat betekent dat de knop opnieuw moet worden weergegeven.

Eén knop is mogelijk geen probleem. Maar wat als u 100 knoppen in een lijst hebt weergegeven?

render () {
  terug (
    {this.state.buttons.map (btn => (
      

In het bovenstaande voorbeeld worden bij elke wijziging van een knoplabel alle knoppen opnieuw weergegeven, omdat alle knoppen een nieuwe onChange-handler genereren.

Binden in constructor ()

Een ouderwetse manier is om de binding in de constructor te doen. Niets bijzonders:

klasse HelloWorld breidt component {
  constructor () {
    this.handleClick = this.handleClickFunc.bind (this);
  }
  render () {
    return (

Op deze manier is veel beter dan de vorige. Het aanroepen van render () genereert geen nieuwe handler voor onClick, dus de wordt niet opnieuw weergegeven zolang de knop niet verandert.

Virtuele DOM voor binding in constructor. Elementen in Blauw worden opnieuw gerenderd.

Binden met de pijlfunctie

Met eigenschappen van de ES7-klasse (momenteel ondersteund door Babel) kunnen we bindingen uitvoeren met de methode-definitie:

klasse HelloWorld breidt component {
  handleClick = (event) => {
    console.log (this.state.name);
  }
  render () {
    terug (

In de bovenstaande code is handleClick een toewijzing die gelijk is aan:

constructor () {
  this.handleClick = (event) => {...} ;
}

Dus zodra het onderdeel is geïnitialiseerd, zal this.handleClick nooit meer veranderen. Op deze manier zorgt het ervoor dat niet opnieuw wordt weergegeven. Deze aanpak is waarschijnlijk de beste manier om bindingen te doen. Het is eenvoudig, gemakkelijk te lezen en vooral, het werkt.

Dynamische binding met de pijlfunctie voor meerdere elementen

Met dezelfde truc met pijlfuncties kunnen we dezelfde handler gebruiken voor meerdere ingangen:

klasse HelloWorld breidt component {
  handleChange = name => event => {
    this.setState ({[name]: event.target.value});
  }
  render () {
    terug (
      
      
    )
  }
}

Op het eerste gezicht ziet dit er dankzij de eenvoud vrij verbluffend uit. Als u echter goed nadenkt, zult u merken dat het hetzelfde probleem heeft als de eerste benadering: elke keer dat render () wordt genoemd, worden beide opnieuw weergegeven.

Ik denk inderdaad dat deze aanpak slim is en ik wil ook niet voor elk veld meerdere handleXXXChange schrijven. Gelukkig komt dit type 'multi-use handler' minder vaak in een lijst voor. Dit betekent dat er slechts een paar componenten worden die opnieuw worden weergegeven, en er zal waarschijnlijk geen prestatieprobleem zijn.

Hoe dan ook, de voordelen die het ons oplevert, zijn veel groter dan het prestatieverlies. Daarom stel ik voor dat u deze aanpak rechtstreeks gebruikt.

In het geval dat deze prestatieproblemen aanzienlijk worden, zou ik willen voorstellen om de handlers in het cachegeheugen te plaatsen bij het uitvoeren van de bindingen (maar dit maakt de code minder leesbaar):

klasse HelloWorld breidt component {
  handleChange = name => {
    if (! this.handlers [name]) {
      this.handlers [name] = event => {
        this.setState ({[name]: event.target.value});
      };
    }
    retourneer this.handlers [naam];
  }
  render () {
    terug (
      
      
    )
  }
}

Gevolgtrekking

Wanneer we eventbindingen doen in React, moeten we heel zorgvuldig controleren of de handlers dynamisch worden gegenereerd. Meestal is dit geen probleem wanneer de getroffen componenten slechts een of twee keer verschijnen. Maar wanneer event-handlers in een lijst verschijnen, kan dit leiden tot ernstige prestatieproblemen.

Oplossingen

  • Gebruik waar mogelijk pijlbinding
  • Als u dynamisch bindingen moet genereren, kunt u overwegen de handlers in het cachegeheugen te plaatsen als de bindingen een prestatieprobleem worden

Bedankt voor het lezen! Ik hoop dat dit bericht nuttig was. Als je dit bericht nuttig vindt, deel het dan met meer mensen door het aan te bevelen.

Bijwerken:

Omri Luzon en Shesh vermeldden lodash-decorators en react-autobind-pakketten voor handiger bindingen. Persoonlijk ben ik geen groot fan van automatisch iets doen (ik probeer altijd dingen zoals bindingen minimaal te houden), maar automatisch binden is absoluut een geweldige manier om schone code te schrijven en meer inspanningen te besparen. De code ziet er als volgt uit:

import autoBind van 'react-autobind';
klasse HelloWorld () {
  constructor () {
    autoBind (deze);
  }
  handleClick () {
    ...
  }
  render () {
    return (

Aangezien autoBind de bindingen automatisch afhandelt, is het niet nodig om de pijlfunctie trick (handleClick = () => {}) te gebruiken om de binding te doen, en in de functie render () kan this.handleClick direct worden gebruikt.