Javascript a this
Spoléhání na this v javascriptu, je stejné, jako hrát ruskou ruletu. Do funkce lze totiž ono this podstrčit a tím změnit kontext. Do toho se ještě zamíchá function.bind, use strict a arrow functions a už z toho máme slušný guláš.Dnešní článek se bude snažit vám odhalit, jak to s tím samotným this vlastně je a proč je v JSX bind(this) tou nejhorší možnou volbou.
Funkce a this
Lepší, než tisíc slov, je vždy malá ukázka. Pokud máme následující kód, zkuste uhádnout, co bude jeho výsledkem:function test() {
return this;
}
console.log(test());
Pokud tento příklad spustíte ve webu, bude dané this obsahovat globální objekt window. Pokud daný kód spustíte v node.js, bude obsahovat globální objekt global. O tom, co je globální objekt, se můžete dočíst zde.
Nyní pojďme náš kód trochu upravit:
function test() {
'use strict';
return this;
}
console.log(test());
V současné chvíli bude this undefined. Proč tomu tak je? Důvodem je přidání strict módu. Zjednodušeně se dá říci, že strict mód nám modifikuje chování javascriptu. Tento strict mod vznikl v ES5 a pokud vás zajímá, co všechno modifikuje, můžete si danou věc pročíst zde.
Pojďme si ukázat další variantu:
function test() {
return this;
}
console.log(test.bind({invasion:'Hello'})());
Výsledkem bude, že this bude obsahovat podstrčený objekt {invasion: 'Hello'}. Toto je přesně ta ukázka, kterou ve svém projektu nechceme používat, ač je to častý jev. Důvod proč, rozeberu v následující ukázce:
import * as React from 'react';
interface Props {
onClick: (origin: string) => void;
}
export class Button extends React.Component<Props, void> {
handleOnClick() {
this.props.onClick('From button component');
}
render() {
return (
<button onClick={this.handleOnClick.bind(this)}>Click me!</button>
)
}
}
Co zde vidíme? Máme komponentu s názvem Button, která vrací callback s názvem onClick. Pokud bychom v JSX nepoužili this.handleOnClick.bind(this), tak by naše funkce nemohla použít this.props.onClick(origin). Jak jsme si ukázali výše, tak this je ve funkci buď globální objekt, nebo undefined. Podle toho, zda jsme v režimu strict či nikoli.
Ono by na tomto příkladě nic tak špatného nebylo, až na to, že pokud si uvědomíme, jak funguje React a metoda render, tak zjistíme, že jsme napsali dost neefektivní kód. Metoda render se totiž bude vykonávat velice často a to vždy, když je komponenta poprvé renderována a také ve chvíli, kdy jí předek změní props či se modifikuje state. To od Reactu přeci očekáváme, že takto bude fungovat. Nicméně v případě metody bind se dostáváme k tomu, že vlastně znovu a znovu a znovu vytváříme další funkci. Finále je, že jen dost zatěžujeme Garbage Collector, který musí znovu a znovu zahazovat něco, co jsme vytvořili, aniž bychom o tom věděli.
Jak z toho ven?
První variantou je to, že bind aplikujeme v konstruktoru. Je to efektivní způsob, jak se vyhnout tomu, aby nám nevznikalo příliš funkcí, ale také to je způsob, který bych označil spíše za nedoporučovaný. Důvod proč, je zřejmý. V případě nové handler funkce musíte provést její registraci v konstruktoru.Pojďme se podívat na ukázku:
import * as React from 'react';
interface Props {
onClick: (origin: string) => void;
}
export class Button extends React.Component<Props, void> {
constructor(props: Props, context: any) {
super(props, context);
this.handleOnClick = this.handleOnClick.bind(this)
}
handleOnClick() {
this.props.onClick('From button component');
}
render() {
return (
<button onClick={this.handleOnClick}>Click me!</button>
)
}
}
Paráda. Sice jsme se zbavili bind v JSX, ale že by to byla taková výhra se říct nedá.
Druhou a lepší variantou jsou arrow functions. Než se dostaneme k výslednému řešení, pojďme se podívat jak to je s this v arrow functions.
Arrow functions a this
Arrow functions byly přidány v ES6 a dá se říci, že díky nim se nám více přiblížilo funkcionální programování. Arrow functions zjednodušují zápis a také mají jednu důležitou vlastnost a tou je právě zmíněné this.Pojďme se podívat na ukázku:
const test = () => this;
console.log(test());
Skvěle. Máme stejnou funkci test, akorát přepsanou pomocí arrow function. Co bude jejím výsledkem? Bude to prázdný objekt {}. Skvělé na zamotání hlavy :)
Arrow functions mají v this současný kontext. Abychom to lépe pochopili, pojďme se podívat ještě na jednu ukázku:
this.user = {
id: 1,
firstName: 'Ales',
lastName: 'Dostal',
};
const test = () => this;
console.log(test());
Výsledkem bude, že this obsahuje objekt s atributem user.
Nyní si pojďme tuto znalost aplikovat na React komponentu user.
Výsledkem bude následující kód:
import * as React from 'react';
interface Props {
onClick: (origin: string) => void;
}
export class Button extends React.Component<Props, void> {
handleOnClick = () => {
this.props.onClick('From button component');
};
render() {
return (
<button onClick={this.handleOnClick}>Click me!</button>
);
}
}
Díky arrow functions již nepotřebujeme ani konstruktor, ani funkci bind.
Žádné komentáře:
Okomentovat