Sådan skriver du din første React.js-komponent

Reager funktion og klassekomponenter, rekvisitter, tilstand og begivenhedshåndterere

Opdatering: Denne artikel er nu en del af min bog "React.js Beyond The Basics". Læs den opdaterede version af dette indhold og mere om React på jscomplete.com/react-beyond-basics .

Det vigtigste koncept at forstå i React.js er komponenten. En React-komponent kan være en af ​​to typer. Det kan enten være en funktionskomponent eller en klassekomponent . Nogle gange vil du høre forskellige udtryk for at beskrive disse to typer, som statsløs og stateful . Funktionskomponenter er også ofte forbundet med præsentationskonceptet . Jeg vil henvise til dem i denne artikel som funktionskomponenter og klassekomponenter.

En funktionskomponent er den enkleste form for en React-komponent. Det er en simpel funktion med en simpel kontrakt:

Funktionskomponenten modtager et objekt med egenskaber, der normalt kaldes rekvisitter. Det returnerer det, der ligner HTML, men er virkelig en særlig JavaScript-syntaks kaldet JSX.

En klassekomponent er en mere fremhævet måde at definere en React-komponent på. Det fungerer også som en funktion, der modtager rekvisitter, men den funktion betragter også en privat intern tilstand som yderligere input, der styrer den returnerede JSX.

Denne private interne tilstand er det, der giver React sin reaktive natur. Når tilstanden for en klassekomponent ændres, gengiver React komponenten igen i browseren.

State og Props objekter har en vigtig forskel. Inde i en klassekomponent kan tilstandsobjektet ændres, mens Props-objektet repræsenterer faste værdier. Klassekomponenter kan kun ændre deres interne tilstand, ikke deres egenskaber. Dette er en kerneide at forstå i React, og denne artikel vil have et eksempel på det.

Lad os se på et faktisk eksempel på en komponent. En meget enkel, uden input og med en simpel h1i divoutput.

På venstre side er komponenten skrevet i den specielle JSX-syntaks.

JSX giver os mulighed for at beskrive vores brugergrænseflader (UI'er) i en syntaks meget tæt på den HTML, som vi er vant til. Det er dog valgfrit. React kan bruges uden JSX, som du kan se på højre side. Faktisk kompilerer React bare den JSX, du ser til venstre, til den rene JavaScript, du ser til højre. Derefter fungerer det med kompileret JavaScript i browseren.

Den React.createElementopfordring på højre side er en JavaScript repræsentation af Document Object Model (DOM). Reager effektivt oversætter det til DOM-operationer, som det udfører i browseren.

Lad os skrive en React-komponent.

Jeg bruger jsCompletets React Playground til eksemplerne i denne artikel. Det er et værktøj, hvor du kan teste din JavaScript og React-kode lige i browseren. Der er ikke behov for at installere eller konfigurere noget.

Værktøjet har en simpel grænseflade med to paneler. Det venstre panel er editoren, hvor du skriver din JavaScript og React-kode. Den seneste version af både React og ReactDOM er allerede forudindlæst der. Editoren forstår også JSX-udvidelsen og alle de moderne funktioner i JavaScript. Dette giver os mulighed for at fokusere på selve React API snarere end at konfigurere og kompilere en React-applikation.

Det højre panel er preview-panelet. Du har et foruddefineret mountNodeelement i editoren. Når du udfører din JavaScript-kode, vises alt, hvad du lægger i mountNodeelementet, i eksempelpanelet. Eksempelpanelet viser også eventuelle fejl, du støder på, når du udfører din kode. Legepladsen er også en simpel JavaScript REPL (Run, Eval, Print, Loop), hvor du kan teste hurtige JavaScript-funktioner og -udtryk. For at udføre koden når som helst skal du trykke på CTRL+Enter.

Prøv for eksempel følgende i REPL:

mountNode.innerHTML = 'Hello!!';

Eller den enkle REPL-tilstand

3 == '3'

For at oprette en React-komponent skal du definere en ny funktion. Lad os få denne funktion til at returnere et HTML-knapelement:

function Button() { return ( Go );}

Hvad vi returnerede her ligner HTML, men husk at det ikke er det. Det bliver samlet i JavaScript. Det faktiske JavaScript, som browseren ser, når vi bruger dette knapelement i JSX, er et kald til React.createElement funktionen:

function Button() { return ( React.createElement("button", null, "Go") );}

Mens du kan bruge React på denne måde uden JSX, ville det være meget sværere at kode og vedligeholde. Så lad os holde os til JSX.

Ovenstående funktion er en komplet og meget enkel React-komponent. Lad os bruge det!

Vi bruger en komponent ved at montere den i browseren. Funktionen designet til at gøre det er ReactDOM.render, som tager i to argumenter:

  • Den første er den komponent, der skal gengives, i vores tilfælde er den Button.
  • Det andet argument er det element, hvor denne komponent skal gengives. I REPLs miljø kan vi bruge den specielle mountNodevariabel.
ReactDOM.render(, mountNode);

Alle kodeeksempler i denne artikel har et link i billedteksten på skærmbilledet, hvor du kan redigere eksemplet på jsComplete REPL.

En React-funktionskomponent modtager propsobjektet som sit første argument . Dette argument giver os mulighed for at gøre komponenten genbrugelig. For eksempel kan vi i stedet for at hårdkode "Go" -etiketten på knappen ovenfor sende Buttonkomponenten en labelattribut, som vi gør med almindelige HTML-elementer:

ReactDOM.render(, mountNode);

Så kan vi få adgang til denne attribut inde i komponenten med et krøllet beslag til props.label.

function Button(props) { return ( {props.label} );}

Den propsargument er et objekt, der rummer alle de værdier der blev videregivet til komponenten, når det blev gjort.

Gør komponenten interaktiv

Vi har et knapelement, og det gengives gennem en React-komponent.

Lad os nu tilføje noget interaktivitet til dette hidtil kedelige eksempel. Lad os få dette knapelement til at øge en tællerværdi for hvert klik og vise den værdi som selve knapmærket. Så etiketten på denne knap begynder med tallet 1, og når brugeren klikker på knappen, ændres etiketten til 2, 3, 4 og så videre.

Da dette er noget, der skal reflekteres i den komponent, der gengives, hører det til komponentens tilstand. Vi har brug for komponenten til at gengive sig selv hver gang tælleren ændres. Vi kan ikke bruge en egenskab her, fordi en komponentrekvisition ikke kan ændres. Ved at bruge det specielle React-tilstandsobjekt bruger vi React's reaktive natur, og vi behøver ikke bekymre os om, hvordan vi ændrer browseren. React vil gøre det for os.

Men vores Button-komponent er i øjeblikket en funktionskomponent. Funktionskomponenter kan ikke have tilstand, så vi skal først opgradere denne komponent til en klassekomponent.

Dette er meget simpelt. Vi definerer først en klasse, der strækker sigReact.Component

class Button extends React.Component { }

I denne klasse definerer vi en renderfunktion, der returnerer komponentens JSX; HTML-knappen i vores tilfælde.

render() { return ( 1 );}

Dette er lidt mere kode, men vi kan nu bruge en privat tilstand på Button-komponenten!

For at bruge et tilstandsobjekt skal vi først initialisere det. Tilstandsobjektet er en simpel instansegenskab, så vi kan initialisere det inde i Buttonklassens konstruktørfunktion . Vi definerer bare den normale konstruktorfunktion (som modtager et propsobjekt i React) og kalder supermetoden for at respektere komponentens arv.

constructor(props) { super(props); this.state = { counter: 1 };}

After that, we initialize this.state to whatever we want. The keys of this state object are the various elements of the state. For our case, we need a counter state, which starts from 1.

Inside the render function, since we can write any JavaScript expression within curly brackets, we can read the value of the new counter state element that we initialized on the state using this.state.counter.

render() { return ( {this.state.counter} );}

The “this” keyword refers to the component instance we are handing off to ReactDOM.

You can try and change that counter state to see how the button will render the values you put on the state.

There is another shorter syntax to define the initial state, which is to simply use a class property without a constructor call:

class Button extends React.Component { state = { counter: 1 }; render() { return ( {this.state.counter} ); }}

This is not yet part of the official JavaScript language but it will be soon. The syntax works at the jsComplele REPL playground because that tool is using Babel to transpile it to the supported JavaScript that the browser will understand.

When you configure your own React application you’ll have to use something like Babel anyway to compile JSX into JavaScript. It is an easy win to also include and use the JavaScript features that are well on their way to becoming an official part of the language.

In the Button example so far, we have a state object and an HTML button element that displays a counter value that we initialized on the state. Now we need to change that value when we click the button. We need to define a click handler on that button.

React comes with normalized events that are easy to use. For this case, we need the onClick event, which we define on the HTML button element itself:

function F() {}

Unlike DOM event handlers, which use a string, React event handlers use an actual JavaScript function. This function can be a global one (like F above), or an inline function:

 {}} />

However, the standard practice is to define a function on the class component itself. Let’s call it handleClick and we can define it on the component as an instance property:

class Button extends React.Component { state = { counter: 1 }; handleClick = () => { console.log('Button is clicked!!'); }; render() { return (  {this.state.counter}  ); }}

We are using the modern class field syntax, which allows us to use arrow functions that are bound to the component instance. handleClick will now act as a prototype function on this class. Inside handleClick the keyword “this” refers to the component instance that we are mounting in the DOM.

handleClick ’s job is easy: read the current counter value from the state object using this.state.counter. Then increment this value and update the component state with the new incremented value.

We can use React’s built-in setState method, which is available on every class component instance, to update a component state.

The button will now increment its label on every click.

This was simple and powerful! We defined an event handler for the onClick method. Every time the user clicks the button the handleClick function will be executed. The function reads the current state of the counter value, increments it, and then sets the state to the new incremented value. React takes care of all the rendering needed after these changes so you do not have to worry about that.

Note that we did not update the state object directly. We have to use React’s setState method when we want to update any element on the state. You can’t for example do this:

// WRONG:this.state.counter = this.state.counter + 1;

React’s setState method is an asynchronous one which schedules an update. Multiple setState calls might potentially be batched for performance. Since we are both reading and writing to the state object inside the handleClick function, we could hit a race condition. The general rule of thumb is whenever you need to update the state using a value from the current state, use the other contract of the setState method. This receives a function reference instead of an object as its first argument:

this.setState((prevState) => {});

Denne funktion modtager et prevStateobjekt, som vi trygt kan bruge uden at bekymre os om race-forhold. Funktionen returnerer det objekt, som vi ønsker, at React skal bruge til at indstille tilstanden. Vores counterværdieksempel ovenfor bliver:

this.setState((prevState) => ({ counter: prevState.counter + 1 }));

Du skal kun bruge denne anden syntaks af, setStatehvis din opdatering afhænger af den aktuelle tilstand. Det kan dog være en god idé at gøre det til en vane at altid bruge den anden funktionsargument-syntaks.

Her er den endelige kode:

class Button extends React.Component { state = { counter: 1 }; handleClick = () => { this.setState((prevState) => ({ counter: prevState.counter + 1 })); }; render() { return (  {this.state.counter}  ); }}
ReactDOM.render(, mountNode);

Test det, og hvis du har spørgsmål, så lad mig det vide i kommentarerne nedenfor.

Denne artikel er en opskrivning af en del af mit Pluralsight-kursus - React.js: Kom godt i gang. Jeg dækker lignende indhold i videoformat der.

Learning React eller Node? Tjek mine bøger:

  • Lær React.js ved at bygge spil
  • Node.js ud over det grundlæggende