Πώς να γράψετε το πρώτο σας στοιχείο React.js

Λειτουργία και στοιχεία κλάσης του React, στηρίγματα, κατάσταση και χειριστές συμβάντων

Ενημέρωση: Αυτό το άρθρο είναι πλέον μέρος του βιβλίου μου "React.js Beyond The Basics". Διαβάστε την ενημερωμένη έκδοση αυτού του περιεχομένου και περισσότερα σχετικά με το React στη διεύθυνση jscomplete.com/react-beyond-basics .

Η πιο σημαντική ιδέα που πρέπει να κατανοήσετε στο React.js είναι το στοιχείο. Ένα συστατικό React μπορεί να είναι ένας από τους δύο τύπους. Μπορεί να είναι είτε ένα στοιχείο συνάρτησης είτε ένα στοιχείο κλάσης . Μερικές φορές θα ακούσετε διαφορετικούς όρους για να περιγράψετε αυτούς τους δύο τύπους, όπως οι απάτριδες και οι πολιτείες . Τα στοιχεία της λειτουργίας συνδέονται επίσης συχνά με την έννοια της παρουσίασης . Θα τα αναφέρω σε αυτό το άρθρο ως στοιχεία συνάρτησης και στοιχεία κλάσης.

Ένα στοιχείο συνάρτησης είναι η απλούστερη μορφή ενός στοιχείου React. Είναι μια απλή λειτουργία με ένα απλό συμβόλαιο:

Το στοιχείο συνάρτησης λαμβάνει ένα αντικείμενο ιδιοτήτων που συνήθως ονομάζεται στηρίγματα. Επιστρέφει ό, τι μοιάζει με HTML, αλλά είναι πραγματικά μια ειδική σύνταξη JavaScript που ονομάζεται JSX.

Ένα στοιχείο κλάσης είναι ένας πιο χαρακτηριστικός τρόπος για να ορίσετε ένα στοιχείο React. Λειτουργεί επίσης σαν μια λειτουργία που λαμβάνει στηρίγματα, αλλά αυτή η συνάρτηση θεωρεί επίσης μια ιδιωτική εσωτερική κατάσταση ως πρόσθετη είσοδο που ελέγχει το JSX που επέστρεψε.

Αυτή η ιδιωτική εσωτερική κατάσταση είναι αυτή που δίνει στο React την αντιδραστική του φύση. Όταν αλλάξει η κατάσταση ενός στοιχείου κλάσης, το React θα αποδώσει ξανά αυτό το στοιχείο στο πρόγραμμα περιήγησης.

Τα αντικείμενα State and Props έχουν μια σημαντική διαφορά. Μέσα σε ένα στοιχείο κλάσης, το αντικείμενο State μπορεί να αλλάξει ενώ το αντικείμενο Props αντιπροσωπεύει σταθερές τιμές. Τα στοιχεία της τάξης μπορούν να αλλάξουν μόνο την εσωτερική τους κατάσταση και όχι τις ιδιότητές τους. Αυτή είναι μια βασική ιδέα που πρέπει να κατανοήσετε στο React και αυτό το άρθρο θα έχει ένα παράδειγμα αυτού.

Ας δούμε ένα πραγματικό παράδειγμα ενός στοιχείου. Πολύ απλό, χωρίς είσοδο και με απλό h1σε divέξοδο.

Στην αριστερή πλευρά, το στοιχείο είναι γραμμένο στην ειδική σύνταξη JSX.

Το JSX μας επιτρέπει να περιγράψουμε τις διεπαφές χρήστη (UI) σε μια σύνταξη πολύ κοντά στο HTML που έχουμε συνηθίσει. Ωστόσο, είναι προαιρετικό. Το React μπορεί να χρησιμοποιηθεί χωρίς JSX, όπως μπορείτε να δείτε στη δεξιά πλευρά. Στην πραγματικότητα, το React καταρτίζει μόνο το JSX που βλέπετε στα αριστερά προς το καθαρό JavaScript που βλέπετε στα δεξιά. Στη συνέχεια, λειτουργεί με μεταγλωττισμένη JavaScript στο πρόγραμμα περιήγησης.

Η React.createElementκλήση στη δεξιά πλευρά είναι μια αναπαράσταση JavaScript του μοντέλου αντικειμένου εγγράφου (DOM). Το React μεταφράζει αποτελεσματικά σε λειτουργίες DOM που εκτελεί στο πρόγραμμα περιήγησης.

Ας γράψουμε ένα στοιχείο React.

Θα χρησιμοποιήσω το React Playground του jsComplete για τα παραδείγματα σε αυτό το άρθρο. Είναι ένα εργαλείο όπου μπορείτε να δοκιμάσετε τον κώδικα JavaScript και να αντιδράσετε απευθείας στο πρόγραμμα περιήγησης. Δεν χρειάζεται να εγκαταστήσετε ή να διαμορφώσετε τίποτα.

Το εργαλείο έχει μια απλή διεπαφή δύο πάνελ. Το αριστερό πλαίσιο είναι το πρόγραμμα επεξεργασίας όπου γράφετε τον κώδικα JavaScript και το React. Η τελευταία έκδοση τόσο του React όσο και του ReactDOM είναι ήδη προεγκατεστημένο εκεί. Ο συντάκτης κατανοεί επίσης την επέκταση JSX και όλες τις σύγχρονες δυνατότητες στο JavaScript. Αυτό θα μας επιτρέψει να επικεντρωθούμε στο ίδιο το React API αντί να ρυθμίσουμε και να συντάξουμε μια εφαρμογή React.

Ο σωστός πίνακας είναι ο πίνακας προεπισκόπησης. Έχετε ένα προκαθορισμένο mountNodeστοιχείο στο πρόγραμμα επεξεργασίας. Όταν εκτελείτε τον κώδικα JavaScript, οτιδήποτε τοποθετείτε στο mountNodeστοιχείο εμφανίζεται στον πίνακα προεπισκόπησης. Ο πίνακας προεπισκόπησης θα εμφανίσει επίσης τυχόν σφάλματα που αντιμετωπίζετε κατά την εκτέλεση του κωδικού σας. Η παιδική χαρά είναι επίσης ένα απλό REPL JavaScript (Run, Eval, Print, Loop) όπου μπορείτε να δοκιμάσετε γρήγορες λειτουργίες και εκφράσεις JavaScript. Για να εκτελέσετε τον κωδικό οποιαδήποτε στιγμή πατήστε CTRL+Enter.

Δοκιμάστε τα ακόλουθα στο REPL, για παράδειγμα:

mountNode.innerHTML = 'Hello!!';

Ή η απλή λειτουργία REPL

3 == '3'

Για να δημιουργήσετε ένα στοιχείο React, ορίστε μια νέα συνάρτηση. Ας κάνουμε αυτήν τη λειτουργία να επιστρέψει ένα στοιχείο κουμπιού HTML:

function Button() { return ( Go );}

Αυτό που επιστρέψαμε εδώ μοιάζει με HTML, αλλά να θυμάστε ότι δεν είναι. Θα μεταγλωττιστεί σε JavaScript. Η πραγματική JavaScript που βλέπει το πρόγραμμα περιήγησης όταν χρησιμοποιούμε αυτό το στοιχείο κουμπιού στο JSX είναι μια κλήση στη React.createElement συνάρτηση:

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

Ενώ μπορείτε να χρησιμοποιήσετε το React με αυτόν τον τρόπο χωρίς JSX, θα ήταν πολύ πιο δύσκολο να κωδικοποιήσετε και να διατηρήσετε. Ας μείνουμε λοιπόν στο JSX.

Η παραπάνω λειτουργία είναι ένα πλήρες και πολύ απλό στοιχείο React. Ας το χρησιμοποιήσουμε!

Χρησιμοποιούμε ένα στοιχείο τοποθετώντας το στο πρόγραμμα περιήγησης. Η συνάρτηση που έχει σχεδιαστεί για να γίνει αυτό είναι ReactDOM.render, η οποία περιλαμβάνει δύο επιχειρήματα:

  • Το πρώτο είναι το συστατικό που αποδίδουμε, στην περίπτωσή μας είναι Button.
  • Το δεύτερο επιχείρημα είναι το στοιχείο στο οποίο πρέπει να αποδοθεί αυτό το στοιχείο. Στο περιβάλλον του REPL μπορούμε να χρησιμοποιήσουμε την ειδική mountNodeμεταβλητή.
ReactDOM.render(, mountNode);

Όλα τα παραδείγματα κώδικα σε αυτό το άρθρο έχουν έναν σύνδεσμο στη λεζάντα του στιγμιότυπου οθόνης όπου μπορείτε να επεξεργαστείτε το παράδειγμα στο jsComplete REPL.

Ένα στοιχείο συνάρτησης React λαμβάνει ως πρώτο όρισμα το propsαντικείμενο. Αυτό το επιχείρημα μας επιτρέπει να κάνουμε το στοιχείο επαναχρησιμοποιήσιμο. Για παράδειγμα, αντί της κωδικοποίησης της ετικέτας "Go" του παραπάνω κουμπιού, μπορούμε να μεταβιβάσουμε στο Buttonστοιχείο ένα labelχαρακτηριστικό, όπως κάνουμε με τα κανονικά στοιχεία HTML:

ReactDOM.render(, mountNode);

Τότε μπορούμε να έχουμε πρόσβαση σε αυτό το χαρακτηριστικό μέσα στο στοιχείο με ένα σγουρό βραχίονα για props.label.

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

Το propsόρισμα είναι ένα αντικείμενο που κρατά όλες τις τιμές που μεταβιβάστηκαν στο στοιχείο όταν αποδόθηκε.

Καθιστώντας το στοιχείο διαδραστικό

Έχουμε ένα στοιχείο κουμπιού και αποδίδεται μέσω ενός στοιχείου React.

Let’s now add some interactivity to this so-far boring example. Let’s make that button element increment a counter value on every click and display that value as the button label itself. So the label of this button is going to start with the number 1 and when the user clicks the button its label will change to 2, 3, 4 and so on.

Since this is something that needs to be reflected in the component rendered output, it belongs to the state of the component. We need the component to re-render itself every time the counter changes. We cannot use a property here because a component props cannot be changed. By using the special React state object, we will be utilizing React’s reactive nature and we will not need to worry about how to take the changes to the browser. React will do that for us.

But, our Button component is currently a function component. Function components cannot have state, so we need to upgrade this component to a class component first.

This is very simple. We first define a class that extends React.Component

class Button extends React.Component { }

In that class we define a render function, which returns the component’s JSX; the HTML button in our case.

render() { return ( 1 );}

This is a little bit more code, but we can now use a private state on the Button component!

To use a state object we first need to initialize it. The state object is a simple instance property, so we can initialize it inside the constructor function of the Button class. We just define the normal constructor function (which receives a props object in React) and call the super method to honor the inheritance of the component.

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) => {});

Αυτή η λειτουργία λαμβάνει ένα prevStateαντικείμενο που μπορούμε να χρησιμοποιήσουμε με αυτοπεποίθηση χωρίς να ανησυχούμε για τις συνθήκες του αγώνα. Η συνάρτηση επιστρέφει το αντικείμενο που θέλουμε να χρησιμοποιήσει το React για να ορίσει την κατάσταση. Το counterπαραπάνω παράδειγμα αξίας γίνεται:

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

Πρέπει να χρησιμοποιήσετε αυτήν τη δεύτερη σύνταξη μόνο setStateεάν η ενημέρωσή σας εξαρτάται από την τρέχουσα κατάσταση. Ωστόσο, μπορεί να είναι καλή ιδέα να συνηθίζετε να χρησιμοποιείτε πάντα τη δεύτερη σύνταξη function-argument.

Εδώ είναι ο τελικός κωδικός:

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

Δοκιμάστε το και αν έχετε απορίες, ενημερώστε με στα παρακάτω σχόλια.

Αυτό το άρθρο είναι μια σύνταξη μέρους του προγράμματος Pluralsight μου - React.js: Ξεκινώντας. Καλύπτω παρόμοιο περιεχόμενο σε μορφή βίντεο εκεί.

Εκμάθηση αντίδρασης ή κόμβος; Δείτε τα βιβλία μου:

  • Μάθετε το React.js δημιουργώντας παιχνίδια
  • Node.js Πέρα από τα βασικά