Hvorfor Skriver Vi super(props)?
2018 M11 30 • ☕️ 5 min read
Translated by readers into: Deutsch • Español • Français • Italiano • Magyar • Nederlands • Norsk • Português do Brasil • Slovenčina • Tiếng Việt • Türkçe • srpski • Čeština • Українська • فارسی • ไทย • မြန်မာဘာသာ • 日本語 • 简体中文 • 繁體中文
Read the original • Improve this translation • View all translated posts
Jeg hørte at Hooks er på moten. Ironisk nok vil jeg heller starte denne bloggen med å snakke om morsomme fakta om klasse komponenter. Tenke seg til!
Disse gotcha’ene er ikke viktige for å kunne bruke React produktivt, men de kan appellere til deg dersom du liker å komme til bunns i hvordan ting funker.
Her er den første.
Jeg har skrevet super(props)
flere ganger i mitt liv enn jeg vil vite:
class Checkbox extends React.Component {
constructor(props) {
super(props); this.state = { isOn: true };
}
// ...
}
Selvfølgelig, forslaget til class fields lar oss hoppe over seremonien:
class Checkbox extends React.Component {
state = { isOn: true };
// ...
}
og denne syntaksen var planlagt da React 0.13 la til støtte for vanlige klasser i 2015. Det å definere en constructor
også kalle super(props)
var alltid ment til å være en midlertidig løsning, frem til klassefeltene (class fields) kunne bidra med et ergonomisk alternativ.
Allikevel, la oss ta for oss dette eksemplet ved å bare bruke ES2015 egenskaper:
class Checkbox extends React.Component {
constructor(props) {
super(props); this.state = { isOn: true };
}
// ...
}
Hvorfor kaller vi på super
? Kan vi velge å ikke kalle på den? Hvis vi kaller på den, hva skjer hvis vi ikke sender med props
?
Finnes det andre parametre? La oss finne ut av det.
I JavaScript, refererer super
til parent-klasse konstruktøren. (I vårt eksempel peker den på React.Component
implementasjonen.)
Her er det viktig å nevne at du ikke kan bruke this
i en konstruktør før du har kalt på parent konstruktøren. JavaScript tillater det ikke:
class Checkbox extends React.Component {
constructor(props) {
// 🔴 Kan ikke bruke `this` enda
super(props);
// ✅ Herfra går det bra
this.state = { isOn: true };
}
// ...
}
Og det er med god grunn at JavaScript krever at parent konstruktøren kjører før du rører this
. Se for deg følgende klassehierarki:
class Person {
constructor(name) {
this.name = name;
}
}
class PolitePerson extends Person {
constructor(name) {
this.greetColleagues(); // 🔴 Dette er ikke lov, les hvorfor nedenfor
super(name);
}
greetColleagues() {
alert('God morgen folkens!');
}
}
Forestill deg at det var tillat å bruke this
før super
-kallet. En måned senere, kan det hende vi endrer greetColleagues
slik at den inkluderer personens navn:
greetColleagues() {
alert('God morgen folkens!');
alert('Mitt navn er' + this.name + ', hyggelig å møte dere!');
}
Men vi glemte at this.greetColleagues()
blir kalt før super()
-kallet hadde en sjanse til å sette opp this.name
. Så this.name
er ikke definert enda! Som du skjønner, kan kode som dette bli veldig vanskelig å holde styr på.
For å unngå slike fallgruver, krever JavaScript at hvis du vil bruke this
i en konstruktør, må du kalle super
først. La parenten gjøre greia si! Denne begrensningen gjelder for React komponenter som er definert som klasser også:
constructor(props) {
super(props);
// ✅ Okay å bruke `this` nå
this.state = { isOn: true };
}
Dette fører oss videre til neste spørsmål: hvorfor sende props
?
Du tenker kanskje at å sende props
til super
er nødvendig for at den underliggende React.Component
konstruktøren skal kunne initiere this.props
:
// Inne i React
class Component {
constructor(props) {
this.props = props;
// ...
}
}
Og det er ikke langt fra sannheten — det er nemlig akkurat det den gjør.
Men på en eller annen måte, selv om du kaller super()
uten props
parameteren, kan du fortsatt akksesere this.props
i render
og andre metoder. (Hvis du ikke tror på meg kan du jo prøve det selv!)
Hvordan funker det? Det viser seg at React også tildeler props
til instansen rett etter den kaller på din konstruktør:
// Inne i React
const instance = new YourComponent(props);
instance.props = props;
Så selv om du glemmer å sende props
til super()
, vil React uansett initiere dem rett etterpå, og det er en grunn til dette.
Da React la til støtte for klasser, la den ikke bare til støtte for ES6 klasser. Målet var å støtte så mange klasseabtrasksjoner som overhode mulig. Det var imidlertid uklart hvor velykket ClojureScript, CoffeeScript, ES6, Fable, Scala.js, TypeScript, eller andre løsninger ville være for å definere komponenter. Dermed hadde React helt bevisst ingen formening om det skulle være et krav å kalle super()
— selv om ES6 klasser krever det.
Så betyr dette at man bare kan skrive super()
istedenfor super(props)
?
Egentlig ikke, da dette fortsatt er forvirrende. Selv om React vil tildele this.props
verdier etter at konstruktøren din kjører, vil this.props
fortsatt være udefinert mellom super
kallet og slutten på konstuktøren din:
// Inne i React
class Component {
constructor(props) {
this.props = props;
// ...
}
}
// Inne i din kode
class Button extends React.Component {
constructor(props) {
super(); // 😬 Vi glemte å sende med props
console.log(props); // ✅ {}
console.log(this.props); // 😬 udefinert }
// ...
}
Og det kan være enda mer utfordrende å debugge dette hvis det skjer i en metode som er kalt fra konstrukøtren. Derfor anbefaler jeg på det sterkeste å alltid bruke super(props)
, selv om det strengt tatt ikke er nødvendig:
class Button extends React.Component {
constructor(props) {
super(props); // ✅ Vi sendte props
console.log(props); // ✅ {}
console.log(this.props); // ✅ {}
}
// ...
}
På denne måten er vi helt sikre på at this.props
er definert, også før man går ut av konstruktøren.
Det er en siste ting som langvarige React-brukere kanskje er nysgjerrige på.
Du har kanskje merket at når du bruker Context APIet i klasser (enten med den utdaterte contextTypes
, eller det moderne contextType
APIet som ble lagt til i React 16.6), blir også context
sendt som en parameter til konstruktøren.
Hvorfor skriver vi ikke da super(props, context)
isteden? Vi kunne ha gjort det, men context brukes mye mindre en props, og dermed oppstår altså fallgruven heller ikke så ofte.
Dessuten forsvinner denne fallgruven mer eller mindre helt med forslaget til class fields som ble nevnt tidligere. Ettersom vi ikke eksplitsitt lager en konstruktør, vil alle parameterene bli sendt automatisk. Det er også denne funksjonaliteten som tillater en kodesnutt som state = {}
å fortsatt inkludere referanser til this.props
eller this.context
om nødvendig.
Når det kommer til Hooks, har vi ikke super
eller this
engang, men det er et tema for en annen dag.