Reager bindende mønstre: 5 fremgangsmåder til håndtering af `dette`

JavaScript er dette søgeordsadfærd har forvirret udviklere i årevis.

Der er mindst fem måder at håndtere denne sammenhæng i React. Lad os overveje fordelene ved hver tilgang.

1. Brug React.createClass

Hvis du bruger React.createClass, reagerer Autobind alle funktioner til dette . Så dette nøgleord er automatisk bundet til din komponents forekomst:

// This magically works with React.createClass// because `this` is bound for you.onChange={this.handleChange}

Men med fremkomsten af ​​ES6-klasser er denne ikke-standardiserede tilgang til at skabe klasser ikke fremtiden for React. Faktisk vil createClass sandsynligvis blive ekstraheret fra React-kernen i en fremtidig udgivelse.

2. Bind i gengivelse

Resten af ​​disse tilgange antager, at du erklærer React-komponenter via ES6-klasser. Hvis du bruger en ES6-klasse, reagerer ikke længere automatisk. En måde at løse dette på er at kalde bind i gengivelse:

onChange={this.handleChange.bind(this)}

Denne tilgang er kort og tydelig, men der er ydeevneimplikationer, da funktionen omfordeles på hver gengivelse. Dette lyder som en big deal, men ydeevneimplikationerne ved denne fremgangsmåde er usandsynligt, at de kan mærkes i de fleste apps. Så at udelukke dette i starten af ​​præstationsårsager er en for tidlig optimering. Når det er sagt, her er et eksempel, hvor præstationseffekten af ​​denne tilgang betyder noget.

Bundlinjen, hvis du oplever ydeevneproblemer, skal du undgå at bruge bind- eller pilfunktioner i gengivelse.

3. Brug pilefunktionen i gengivelse

Denne tilgang svarer til nr. 2. Du kan undgå at ændre denne kontekst ved at bruge en pilefunktion i gengivelse:

onChange={e => this.handleChange(e)}

Denne tilgang har den samme potentielle ydelsespåvirkning som nr. 2.

De alternative tilgange nedenfor er værd at overveje, fordi de tilbyder overlegen ydelse til lidt ekstra omkostninger.

4. Bind i konstruktør

En måde at undgå binding i gengivelse er at binde konstruktøren (den anden tilgang diskuteres i nr. 5 nedenfor).

constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); }

Dette er den fremgangsmåde, der aktuelt anbefales i React-dokumenterne for "bedre ydeevne i din applikation". Dette er også den tilgang, jeg bruger i "Building Applications with React and Redux in ES6" på Pluralsight.

Imidlertid vil implikationerne for fremgangsmåde nr. 2 og # 3 på de fleste apps ikke være mærkbare, så læsbarheds- og vedligeholdelsesfordelene ved tilgang nr. 2 og # 3 kan opveje ydeevneproblemer i mange apps.

Men hvis du er villig til at bruge fase 2-funktioner, er den sidste mulighed nedenfor sandsynligvis din bedste chance.

5. Brug pilefunktionen i klasseegenskab

Denne teknik er afhængig af den foreslåede klasseegenskabsfunktion. For at bruge denne tilgang skal du aktivere transform-class-egenskaber eller aktivere trin-2 i Babel.

handleChange = () => { // call this function from render // and this.whatever in here works fine. };

Denne tilgang har flere fordele:

  1. Pilefunktioner vedtager denne binding af det omsluttende omfang (med andre ord ændrer de ikke betydningen af dette), så tingene fungerer bare automatisk.
  2. Det undgår præstationsproblemer i tilgang nr. 2 og # 3.
  3. Det undgår gentagelse i tilgang # 4.
  4. Det er ligetil at refaktorere fra ES5 createClass-stilen til denne stil ved at konvertere relevante funktioner til pilfunktioner. Faktisk er der en helt automatiseret måde at håndtere dette på ved hjælp af en codemod.

Resumé

Dette rutediagram, der opsummerer beslutningen.

Her er fulde arbejdseksempler på alle 5 fremgangsmåder:

// Approach 1: Use React.createClass var HelloWorld = React.createClass({ getInitialState() { return { message: 'Hi' }; }, logMessage() { // this magically works because React.createClass autobinds. console.log(this.state.message); }, render() { return (  ); } }); // Approach 2: Bind in Render class HelloWorld extends React.Component { constructor(props) { super(props); this.state = { message: 'Hi' }; } logMessage() { // This works because of the bind in render below. console.log(this.state.message); } render() { return (  ); } } // Approach 3: Use Arrow Function in Render class HelloWorld extends React.Component { constructor(props) { super(props); this.state = { message: 'Hi' }; } logMessage() { // This works because of the arrow function in render below. console.log(this.state.message); } render() { return (  this.logMessage()} /> ); } } // Approach 4: Bind in Constructor class HelloWorld extends React.Component { constructor(props) { super(props); this.state = { message: 'Hi' }; this.logMessage = this.logMessage.bind(this); } logMessage() { // This works because of the bind in the constructor above. console.log(this.state.message); } render() { return (  ); } } // Approach 5: Arrow Function in Class Property class HelloWorld extends React.Component { // Note that state is a property, // so no constructor is needed in this case. state = { message: 'Hi' }; logMessage = () => { // This works because arrow funcs adopt the this binding of the enclosing scope. console.log(this.state.message); }; render() { return (  ); } }

Så hvad foretrækker folk? Her er afstemningen:

Hvordan håndterer du binding i #reactjs i dag?

Eksempler: //t.co/z7OKxe39VA

- Cory House (@housecor) 18. august 2016

Har du andre måder, du håndterer dette på? Venligst ring ind via kommentarerne.

Stor tak til @dan_abramov, @kentcdodds og @dmosher for deres værdifulde input og anmeldelse!

Cory House er forfatter til "Building Applications with React and Redux in ES6", "Building Applications with React and Flux", "Clean Code: Writing Code for Humans" og flere andre kurser om pluralsight. Han er softwarearkitekt hos VinSolutions, Microsoft MVP, og uddanner softwareudviklere internationalt i softwarepraksis som front-end-udvikling og ren kodning.