Tutti i concetti fondamentali di React.js, inseriti in questo singolo articolo Medium

Un'introduzione per imparare Why, What e How di React

Questo articolo è un adattamento di una guida interattiva su jsComplete.com/react-intro. La versione jsComplete ha esempi di codice e collegamenti incorporati per navigare nel contenuto.
Prima di iniziare, tieni presente che questa è una guida per principianti che tratta i concetti che classifico come fondamentali per lavorare con React. Non è una guida completa a React ma piuttosto un'introduzione completa.
Alla fine di questa guida, elencherò alcune risorse di livello successivo per te. Questa guida ti aprirà la strada per capirli.

React è definito come una libreria JavaScript per la creazione di interfacce utente. Cominciamo parlando delle due diverse parti di questa definizione.

React è una "libreria" JavaScript. Non è esattamente un "quadro". Non è una soluzione completa e spesso dovrai utilizzare più librerie con React per formare qualsiasi soluzione. React non assume nulla riguardo alle altre parti in nessuna soluzione.

I frame hanno un grande scopo, specialmente per i giovani team e le startup. Quando si lavora con un framework, molte decisioni di progettazione intelligente sono già prese per te, il che ti dà un chiaro percorso per concentrarti sulla scrittura di una buona logica a livello di applicazione. Tuttavia, i framework presentano alcuni svantaggi. Per gli sviluppatori esperti che lavorano su basi di codice di grandi dimensioni, questi svantaggi a volte compromettono l'affare.

I frame non sono flessibili, anche se alcuni sostengono di esserlo. Un framework di solito vuole che codifichi tutto in un certo modo. Se provi a deviare da quel modo, la struttura di solito finisce per combatterti. Anche i frame sono generalmente grandi e pieni di funzionalità. Se devi usarne solo una piccola parte, devi comunque includere tutto. Certo, questo punto sta cambiando oggi ma non è ancora l'ideale. Alcuni framework stanno diventando modulari, cosa che penso sia grandiosa, ma sono un grande fan della pura filosofia Unix:

Scrivi programmi che fanno una cosa e la fanno bene. Scrivi programmi per lavorare insieme.
- Doug McIlroy

React segue la filosofia Unix perché è una piccola biblioteca che si concentra su una sola cosa e sul fare quella cosa estremamente bene. Quella "cosa" è la seconda parte della definizione di React: costruire interfacce utente.

Un'interfaccia utente (UI) è tutto ciò che mettiamo di fronte agli utenti per farli interagire con una macchina. Le interfacce utente sono ovunque, dai semplici pulsanti di un forno a microonde al cruscotto di una navetta spaziale. Se il dispositivo che stiamo tentando di interfacciare è in grado di comprendere JavaScript, possiamo utilizzare React per descriverne un'interfaccia utente. Poiché i browser Web comprendono JavaScript, possiamo usare React per descrivere le UI Web.

Mi piace usare la parola descritta qui perché è quello che fondamentalmente facciamo con React. Diciamo solo quello che vogliamo! React costruirà quindi l'interfaccia utente effettiva, per nostro conto, nel browser Web. Senza React o librerie simili, avremmo bisogno di creare manualmente interfacce utente con API Web e JavaScript nativi e non è così facile.

Quando senti l'affermazione che "React è dichiarativo", questo è esattamente ciò che significa. Descriviamo le UI con React e diciamo quello che vogliamo (non come farlo). React si occuperà del "come" e tradurrà le nostre descrizioni dichiarative (che scriviamo nella lingua di React) in interfacce utente reali nel browser. React condivide questo semplice potere dichiarativo con lo stesso HTML, ma con React diventiamo dichiarativi per le UI HTML che rappresentano dati dinamici, non solo dati statici.

Quando React è stato rilasciato, ci sono stati molti ronzii nelle sue prestazioni perché ha introdotto l'idea intelligente di un DOM virtuale che può essere usato per riconciliare il DOM reale (e ne parleremo nella prossima sezione).

DOM è "Document Object Model". È l'interfaccia di programmazione dei browser per i documenti HTML (e XML) che li tratta come strutture ad albero. L'API DOM può essere utilizzata per modificare la struttura, lo stile e il contenuto del documento.

Sebbene le prestazioni di React siano ancora uno dei motivi più importanti per cui è estremamente popolare oggi, non la classifico come la cosa "migliore" al riguardo. Penso che React sia stato un punto di svolta perché ha creato un linguaggio comune tra sviluppatori e browser che consente agli sviluppatori di descrivere in modo dichiarativo le interfacce utente e gestire le azioni sul loro stato anziché le azioni sui loro elementi DOM. È semplicemente la lingua dei "risultati" dell'interfaccia utente. Invece di proporre passaggi per descrivere le azioni sulle interfacce, gli sviluppatori descrivono semplicemente le interfacce in termini di uno stato "finale" (come una funzione). Quando si verificano azioni in quello stato, React si occupa di aggiornare le UI nel DOM in base a ciò (e lo fa in modo efficiente come vedremo in seguito).

Se qualcuno ti chiedesse di dare una ragione per cui React vale la pena imparare, questo è il linguaggio dell'interfaccia utente basato sui risultati. Chiamo questa lingua "la lingua React".

Il linguaggio React

Supponiamo che abbiamo un elenco di TODO come questo:

const todos: [
  {body: "Learn React Fundamentals", done: true},
  {body: 'Build a TODOs App', done: false},
  {body: 'Build a Game', done: false},
];

Questo array todos è lo stato iniziale dell'interfaccia utente. Dovrai creare un'interfaccia utente per visualizzarli e gestirli. L'interfaccia utente avrà un modulo per aggiungere nuovi TODO, un modo per contrassegnare un TODO come completo e un modo per rimuovere tutti i TODO completati.

Ognuna di queste azioni richiederà all'app di eseguire un'operazione DOM per creare, inserire, aggiornare o eliminare i nodi DOM. Con React, non ti preoccupare di tutte queste operazioni DOM. Non ti preoccupare quando devono accadere o come eseguirle in modo efficiente. Basta posizionare l'array todos nello "stato" della tua app, quindi utilizzare il linguaggio React per "comandare" React per visualizzare tale stato in un determinato modo nell'interfaccia utente:

Elenco TODO
      {todos.map (todo =>     
  • {todo.body}   )}
// Altri elementi del modulo ...

Non preoccuparti ancora della sintassi, ma se ti stai chiedendo cosa sta succedendo, abbiamo semplicemente mappato una matrice di oggetti JavaScript in una matrice di elementi React. Ne parleremo presto.

Dopodiché puoi concentrarti sul fare semplicemente operazioni di dati su quell'array todos! Aggiungete, rimuovete e aggiornate gli elementi di quell'array e React rifletterà le modifiche apportate su questo oggetto nell'interfaccia utente renderizzata nel browser.

Questo modello mentale sulla modellazione dell'interfaccia utente basata su uno stato finale è più facile da capire e lavorare, specialmente quando le viste hanno molte transizioni di dati. Ad esempio, considera la vista che ti dice quanti dei tuoi amici sono online. Lo "stato" di quella vista sarà solo un singolo numero di quanti amici sono attualmente online. Non importa che un momento fa tre amici siano entrati online, poi uno di loro si sia disconnesso e poi altri due si siano uniti. Sa solo che in questo momento, quattro amici sono online.

Riconciliazione dell'albero di React

Prima di React, quando dovevamo lavorare con l'API di un browser, nota come API DOM, abbiamo evitato di attraversare il più possibile l'albero DOM e c'è una ragione per questo. Qualsiasi operazione sul DOM viene eseguita nello stesso thread singolo che è responsabile di tutto ciò che sta accadendo nel browser, comprese le reazioni a eventi dell'utente come la digitazione, lo scorrimento, il ridimensionamento, ecc.

Qualsiasi operazione costosa sul DOM significa un'esperienza lenta e bizzarra per l'utente. È estremamente importante che le applicazioni eseguano le operazioni minime assolute e le raggruppino ove possibile. React ha ideato un concetto unico per aiutarci a fare esattamente questo!

Quando diciamo a React di eseguire il rendering di un albero di elementi nel browser, in primo luogo genera una rappresentazione virtuale di quell'albero e lo mantiene in memoria per dopo. Quindi procederà ad eseguire le operazioni DOM che mostreranno l'albero nel browser.

Quando diciamo a React di aggiornare l'albero degli elementi che ha reso in precedenza, genera una nuova rappresentazione virtuale dell'albero aggiornato. Ora React ha 2 versioni dell'albero in memoria!

Per eseguire il rendering dell'albero aggiornato nel browser, React non elimina ciò che è già stato visualizzato. Invece, confronterà le 2 versioni virtuali dell'albero che ha in memoria, calcolerà le differenze tra loro, capirà quali sottoalberi dell'albero principale devono essere aggiornati e aggiornerà solo questi sotto-alberi nel browser.

Questo processo è noto come algoritmo di riconciliazione dell'albero ed è ciò che rende React un modo molto efficiente di lavorare con l'albero DOM di un browser. Ne vedremo un esempio a breve.

Oltre al linguaggio basato sui risultati dichiarativi e all'efficace riconciliazione dell'albero, ecco alcuni degli altri motivi per cui penso che React abbia guadagnato la sua enorme popolarità:

  • Lavorare con l'API DOM è difficile. React offre agli sviluppatori la possibilità di lavorare con un browser "virtuale" che è più amichevole del browser reale. React si comporta sostanzialmente come il tuo agente che farà la comunicazione con il DOM per tuo conto.
  • A React viene spesso assegnata l'etichetta "Just JavaScript". Ciò significa che ha un'API molto piccola da imparare e dopo che le tue abilità JavaScript sono ciò che ti rende uno sviluppatore React migliore. Questo è un vantaggio rispetto alle librerie con API più grandi. Inoltre, l'API React è principalmente funzioni (e facoltativamente classi se ne hai bisogno). Quando senti che una vista dell'interfaccia utente è una funzione dei tuoi dati, in React è letteralmente il caso.
  • Learning React ripaga alla grande anche per le applicazioni mobili iOS e Android. React Native ti consente di utilizzare le tue abilità di React per creare applicazioni mobili native. Puoi persino condividere un po 'di logica tra le tue applicazioni web, iOS e Android.
  • Il team di React su Facebook mette alla prova tutti i miglioramenti e le nuove funzionalità che vengono introdotte a React proprio su Facebook.com, il che aumenta la fiducia nella biblioteca della comunità. È raro vedere bug gravi e gravi nelle versioni di React perché vengono rilasciati solo dopo accurati test di produzione su Facebook. React alimenta anche altre applicazioni Web molto utilizzate come Netflix, Twitter, Airbnb e molte altre.

Il tuo primo esempio di React

Per vedere il vantaggio pratico del processo di riconciliazione dell'albero e la grande differenza che fa, esaminiamo un semplice esempio incentrato proprio su quel concetto. Generiamo e aggiorniamo due volte un albero di elementi HTML, una volta utilizzando l'API Web nativa e quindi utilizzando l'API React (e il suo lavoro di riconciliazione). Per semplificare questo esempio, non userò componenti o JSX (l'estensione JavaScript comunemente usata con React). Farò anche l'operazione di aggiornamento all'interno di un timer intervallo JavaScript. Non è così che scriviamo le applicazioni React, ma concentriamoci su un concetto alla volta.

Inizia con questa sessione di giochi completi js: jsdrops.com/react-dom1.

In questa sessione, un semplice elemento HTML viene visualizzato sul display usando 2 metodi:

Metodo n. 1: utilizzo diretto dell'API DOM Web

document.getElementById ('mountNode'). innerHTML = `
  
    Ciao HTML    `;

Metodo n. 2: utilizzo dell'API di React

ReactDOM.render (
  React.createElement (
    'Div',
    nullo,
    "Hello React",
  ),
  document.getElementById ( 'mountNode2'),
);

Il metodo ReactDOM.render e il metodo React.createElement sono i metodi API principali in un'applicazione React. In effetti, un'applicazione Web React non può esistere senza utilizzare entrambi questi metodi. Lasciatemi spiegare brevemente loro:

ReactDOM.render

Questo è fondamentalmente il punto di ingresso di un'applicazione React nel DOM del browser. Ha 2 argomenti:

  1. Il primo argomento è COSA rendere al browser. Questo è sempre un "elemento React".
  2. Il secondo argomento è DOVE visualizzare l'elemento React nel browser. Questo deve essere un nodo DOM valido che esiste nel codice HTML reso staticamente. L'esempio sopra utilizza uno speciale elemento mountNode2 presente nell'area di visualizzazione del parco giochi (il primo mountNode viene utilizzato per la versione nativa).

Che cos'è esattamente un elemento React? È un elemento VIRTUALE che descrive un elemento DOM. È ciò che restituisce il metodo API React.createElement.

React.createElement

Invece di lavorare con le stringhe per rappresentare elementi DOM (come nell'esempio DOM nativo sopra) in React rappresentiamo elementi DOM con oggetti usando le chiamate al metodo React.createElement. Questi oggetti sono noti come elementi React.

La funzione React.createElement ha molti argomenti:

  1. Il primo argomento è il "tag" HTML che l'elemento DOM deve rappresentare, che è div in questo esempio.
  2. Il secondo argomento è per tutti gli attributi (come id, href, title, ecc.) Che vogliamo avere l'elemento DOM. Il semplice div che stiamo usando non ha attributi, quindi abbiamo usato null.
  3. Il terzo argomento è il contenuto dell'elemento DOM. Abbiamo inserito una stringa "Hello React". Il terzo argomento facoltativo e tutti gli argomenti facoltativi dopo di esso formano l'elenco figlio per l'elemento renderizzato. Un elemento può avere 0 o più figli.
React.createElement può anche essere usato per creare elementi dai componenti di React.

Gli elementi React vengono creati in memoria. Per rendere effettivamente visibile un elemento React nel DOM, utilizziamo il metodo ReactDOM.render che farà molte cose per capire il modo più ottimale per riflettere lo stato di un elemento React nell'albero DOM reale nel browser.

Quando esegui i 2 metodi in questa sessione di codice, vedrai una casella "Hello HTML" e una casella "Hello React":

Nesting React elements

Abbiamo due nodi: uno controllato direttamente con l'API DOM e un altro controllato con l'API React (che a sua volta utilizza l'API DOM). L'unica grande differenza tra i modi in cui stiamo costruendo questi due nodi nel browser è che nella versione HTML abbiamo usato una stringa per rappresentare l'albero DOM, mentre nella versione React abbiamo usato chiamate JavaScript pure e rappresentato l'albero DOM con un oggetto invece di una stringa.

Indipendentemente dalla complessità dell'interfaccia utente HTML, quando si utilizza React ogni elemento HTML verrà rappresentato con un elemento React.

Aggiungiamo più elementi HTML a questa semplice interfaccia utente. Aggiungiamo una casella di testo per leggere l'input dell'utente. Per la versione HTML, puoi semplicemente iniettare il tag del nuovo elemento direttamente all'interno del modello:

document.getElementById ('mountNode'). innerHTML = `
  
    Ciao HTML         `;

Per fare lo stesso con React, puoi aggiungere più argomenti dopo il terzo argomento per React.createElement sopra. Per abbinare ciò che è nell'esempio DOM nativo finora, possiamo aggiungere un quarto argomento che è un'altra chiamata React.createElement che rende un inputelement:

ReactDOM.render (
  React.createElement (
    "Div",
    nullo,
    "Hello React",
    React.createElement ( "input")
  ),
  document.getElementById ( 'mountNode2'),
);

Eseguiamo anche il rendering dell'ora corrente in entrambe le versioni. Mettiamolo in un pre-elemento (solo per dargli un carattere monospaziale nel parco giochi). È possibile utilizzare il nuovo Date (). ToLocaleTimeString () per visualizzare una semplice stringa temporale. Ecco cosa devi fare per la versione DOM nativa:

document.getElementById ('mountNode1'). innerHTML = `
  
    Ciao HTML          
 $ {new Date (). toLocaleTimeString ()} 

   `;

Per fare lo stesso in React, aggiungiamo un quinto argomento all'elemento div di livello superiore. Questo nuovo quinto argomento è un'altra chiamata React.createElement, questa volta utilizzando un pre tag con la nuova stringa Date (). ToLocaleTimeString () per il contenuto:

ReactDOM.render (
  React.createElement (
    'Div',
    nullo,
    "Hello React",
    React.createElement ( 'ingresso'),
    React.createElement (
      'pre',
      nullo,
      nuova data (). toLocaleTimeString ()
    )
  ),
  document.getElementById ( 'mountNode2')
);

Entrambe le versioni visualizzano ancora lo stesso HTML esatto nel browser.

Come probabilmente stai pensando ora, usare React è molto più difficile del modo semplice e familiare nativo. Che cosa fa React così bene che vale la pena rinunciare all'HTML familiare e dover imparare una nuova API per scrivere ciò che può essere semplicemente scritto in HTML?

La risposta non riguarda il rendering della prima vista HTML. Riguarda ciò che dobbiamo fare per aggiornare qualsiasi vista esistente nel DOM.

Aggiornamento degli elementi React

Facciamo un'operazione di aggiornamento sugli alberi DOM che abbiamo finora. Facciamo semplicemente spuntare la stringa del tempo ogni secondo.

Possiamo facilmente ripetere una chiamata di funzione JavaScript in un browser utilizzando l'API timer Web setInterval. Mettiamo tutte le nostre manipolazioni DOM per entrambe le versioni in una funzione, chiamiamolo render e usiamolo in una chiamata setInterval per farlo ripetere ogni secondo.

Ecco il codice finale completo per questo esempio:

// jsdrops.com/react-dom2
const render = () => {
  document.getElementById ('mountNode'). innerHTML = `
    
      Ciao HTML              
 $ {new Date (). toLocaleTimeString ()} 
       `;
  ReactDOM.render (
    React.createElement (
      'Div',
      nullo,
      "Hello React",
      React.createElement ('input', null),
      React.createElement (
        'pre',
        nullo,
        nuova data (). toLocaleTimeString ()
      )
    ),
    document.getElementById ( 'mountNode2')
  );
};
setInterval (render, 1000);

Controlla il risultato dell'esecuzione di questo codice su jsdrops.com/react-dom2 e nota come la sequenza temporale passa ogni secondo in entrambe le versioni. Stiamo aggiornando la nostra interfaccia utente nel DOM.

Questo è il momento in cui React ti lascerà a bocca aperta. Se si tenta di digitare qualcosa nella casella di testo della versione DOM nativa, non sarà possibile. Questo è molto atteso perché fondamentalmente stiamo eliminando l'intero nodo DOM su ogni tick e lo stiamo rigenerando. Tuttavia, se provi a digitare qualcosa nella casella di testo visualizzata con React, puoi sicuramente farlo!

Sebbene l'intero codice di rendering di React sia all'interno del timer di ticchettio, React sta cambiando solo il contenuto dell'elemento pre e non l'intero albero DOM. Questo è il motivo per cui la casella di inserimento del testo non è stata rigenerata e siamo riusciti a digitarla.

Puoi vedere visivamente i diversi modi in cui stiamo aggiornando il DOM se controlli i due nodi DOM in un pannello di elementi DevTools di Chrome. Il pannello degli elementi di DevTools di Chrome evidenzia tutti gli elementi DOM che vengono aggiornati. Vedrai come la versione HTML nativa sta rigenerando il suo intero contenitore div # mountNode ad ogni tick, mentre React sta rigenerando in modo intelligente solo il pre tag nel suo contenitore div # mountNode2.

Questo è l'algoritmo di diffting intelligente di React in azione. Aggiorna solo nella struttura principale del DOM ciò che effettivamente deve essere aggiornato mentre mantiene tutto il resto uguale. Questo diverso processo è possibile grazie alla rappresentazione DOM virtuale di React che mantiene in memoria. Indipendentemente da quante volte le visualizzazioni dell'interfaccia utente devono essere rigenerate, React porterà al browser solo gli aggiornamenti "parziali" necessari.

Questo metodo non solo è molto più efficiente, ma rimuove anche un grande livello di complessità nel modo in cui pensiamo di aggiornare le interfacce utente. Avere React a fare tutti i calcoli sull'opportunità o meno di aggiornare il DOM ci consente di concentrarci sul pensiero dei nostri dati (stato) e sul modo di descriverne un'interfaccia utente. Gestiamo quindi gli aggiornamenti sullo stato dei dati secondo necessità senza preoccuparci dei passaggi necessari per riflettere questi aggiornamenti nell'interfaccia utente effettiva nel browser (perché sappiamo che React farà esattamente questo e lo farà in modo efficiente!)

React è tutto sui componenti

In React, descriviamo le UI usando componenti riutilizzabili, compostabili e con stato.

Definiamo piccoli componenti e poi li uniamo per formare quelli più grandi. Tutti i componenti piccoli o grandi sono riutilizzabili, anche attraverso progetti diversi.

Puoi pensare ai componenti come a semplici funzioni (in qualsiasi linguaggio di programmazione). Chiamiamo funzioni con qualche input e ci danno un output. Possiamo riutilizzare le funzioni secondo necessità e comporre funzioni più grandi da quelle più piccole.

I componenti di React sono esattamente gli stessi; il loro input è un insieme di "oggetti di scena" e il loro output è una descrizione di un'interfaccia utente. Possiamo riutilizzare un singolo componente in più UI e i componenti possono contenere altri componenti. La forma base di un componente React è in realtà una semplice funzione JavaScript.

Alcuni componenti di React sono puri ma puoi anche introdurre effetti collaterali in un componente. Ad esempio, un componente potrebbe cambiare il "titolo" HTML di una pagina Web quando viene montato nel browser o potrebbe scorrere la vista del browser in una determinata posizione.

Ancora più importante, un componente React può avere uno stato privato per contenere i dati che possono cambiare nel corso del ciclo di vita del componente. Questo stato privato è una parte implicita dell'input che guida l'output del componente e questo è in realtà ciò che dà a React il suo nome!

Perché React si chiama comunque "React"?
Quando lo stato di un componente React (che fa parte del suo input) cambia, anche l'interfaccia utente che rappresenta (il suo output) cambia. Questa modifica nella descrizione dell'interfaccia utente deve riflettersi nel dispositivo con cui stiamo lavorando. In un browser, è necessario aggiornare l'albero DOM. In un'applicazione React non lo facciamo manualmente. React reagirà semplicemente ai cambiamenti di stato e aggiornerà automaticamente (ed efficacemente) il DOM quando necessario.

Creazione di componenti mediante funzioni

Un componente React - nella sua forma più semplice - è una semplice funzione JavaScript:

// jsdrops.com/bx1
Tasto funzione (oggetti di scena) {
  // Restituisce qui un elemento DOM / React. Per esempio:
  return ;
}
// Per eseguire il rendering di un elemento Button nel browser
ReactDOM.render (

Nota come ho scritto quello che sembra HTML nell'output restituito della funzione Button sopra. Questo non è né HTML né JavaScript e non è nemmeno React. Questo è JSX. È un'estensione di JavaScript che ci consente di scrivere chiamate di funzione in una sintassi simile a HTML.

Vai avanti e prova a restituire qualsiasi altro elemento HTML all'interno della funzione Button e vedi come sono tutti supportati (ad esempio, restituisce un elemento di input o un elemento textarea).

JSX non è HTML

JSX non è compreso dai browser. Se si tenta di eseguire la funzione Button in una normale console del browser, si lamenterà del primo carattere nella parte JSX:

Ciò che i browser comprendono (data la libreria React inclusa) sono le chiamate React.createElementAPI. Lo stesso esempio di pulsante può essere scritto senza JSX come segue:

// jsdrops.com/bx2
Tasto funzione (oggetti di scena) {
  return React.createElement (
    "pulsante",
    {type: "submit"},
    props.label
  );
}
ReactDOM.render (
  React.createElement (Button, {label: "Save"}),
  mountNode
);

Puoi usare React in questo modo (senza JSX). Puoi eseguire la funzione Button in un browser direttamente (dopo aver caricato la libreria React) e le cose funzioneranno bene. Tuttavia, ci piace vedere e lavorare con HTML invece di occuparci delle chiamate di funzione. Quando è stata l'ultima volta che hai creato un sito Web con solo JavaScript e non utilizzato HTML? Puoi se vuoi, ma nessuno lo fa. Ecco perché esiste JSX.

JSX è fondamentalmente un compromesso. Invece di scrivere componenti React usando la sintassi React.createElement, usiamo una sintassi molto simile all'HTML e quindi usiamo un compilatore per tradurlo in chiamate React.createElement.

Un compilatore che traduce una forma di sintassi in un'altra è noto come "transpiler". Per tradurre JSX possiamo usare transpiler come Babel o TypeScript. Ad esempio, il parco giochi jsComplete utilizza TypeScript per trascrivere qualsiasi JSX inserito in esso. Quando usi l'app create-reagire, l'app generata utilizzerà internamente Babel per compilare il tuo JSX.

Puoi usare babeljs.io/repl/ per vedere in cosa viene convertita qualsiasi sintassi JSX per React ma JSX può anche essere usato da solo. Non è una cosa solo React.

Quindi un componente React è una funzione JavaScript che restituisce un elemento React (di solito con JSX). Quando si utilizza JSX, la sintassi

Il nome deve iniziare con una lettera maiuscola

Nota come ho chiamato il componente "Button". La prima lettera maiuscola è in realtà un requisito poiché avremo a che fare con un mix di elementi HTML ed elementi React. Un compilatore JSX (come Babel) considererà tutti i nomi che iniziano con una lettera minuscola come nomi di elementi HTML. Questo è importante perché gli elementi HTML vengono passati come stringhe alle chiamate React.createElement mentre gli elementi React devono essere passati come variabili:

Vai avanti e prova a nominare il "pulsante" del componente React invece di "Button" e guarda come ReactDOM ignorerà totalmente la funzione e renderà un normale elemento pulsante HTML vuoto.

// jsdrops.com/bx3
// Sbagliato:
tasto funzione () {
  return 
Il mio pulsante fantasia
; };
// Di seguito verrà visualizzato un pulsante HTML
// (e ignora la funzione pulsante fantasia)
ReactDOM.render (

Il primo argomento è un oggetto di "oggetti di scena"

Proprio come agli elementi HTML possono essere assegnati attributi come id o title, un elemento React può anche ricevere un elenco di attributi quando viene renderizzato. L'elemento Button sopra (jsdrops.com/bx2) ha ricevuto un attributo label. In React, l'elenco degli attributi ricevuti da un elemento React è noto come oggetti di scena. Un componente della funzione React riceve questo elenco come primo argomento. L'elenco viene passato come oggetto con le chiavi che rappresentano i nomi degli attributi e i valori che rappresentano i valori loro assegnati.

Quando si utilizza un componente di funzione, non è necessario nominare l'oggetto che contiene l'elenco di attributi come "oggetti di scena", ma questa è la pratica standard. Quando si usano i componenti di classe, che faremo di seguito, lo stesso elenco di attributi viene sempre presentato con una proprietà di istanza speciale chiamata props.

Nota che ricevere oggetti di scena è facoltativo. Alcuni componenti non avranno oggetti di scena. Tuttavia, il valore di ritorno di un componente non è facoltativo. Un componente React non può restituire "non definito" (né esplicitamente né implicitamente). Deve restituire un valore. Può restituire "null" per fare in modo che il renderer ignori il suo output.

Mi piace usare la distruzione degli oggetti ogni volta che uso oggetti di scena (o stato, davvero). Ad esempio, la funzione del componente Button può essere scritta in questo modo con la distruzione degli oggetti di scena:

pulsante const = ({label}) => (
  
);

Questo approccio ha molti vantaggi, ma il più importante è quello di ispezionare visivamente quali oggetti di scena vengono utilizzati in un componente e assicurarsi che un componente non riceva alcun oggetto aggiuntivo non necessario.

Nota come ho usato una funzione freccia anziché una normale. Questa è solo una preferenza di stile per me personalmente. Alcune persone preferiscono lo stile di funzione regolare e non c'è nulla di sbagliato in questo. Penso che l'importante sia essere coerenti con lo stile che scegli. Userò qui le funzioni freccia ma non la interpreterò come un requisito.

Espressioni in JSX

Puoi includere un'espressione JavaScript usando una coppia di parentesi graffe ovunque all'interno di JSX:

// jsdrops.com/bx4
const RandomValue = () => (
  
    {Math.floor (Math.random () * 100)}    );
ReactDOM.render (, mountNode);

Si noti che solo queste espressioni possono essere incluse in queste parentesi graffe. Ad esempio, non è possibile includere un'istruzione if normale ma un'espressione ternaria va bene. Tutto ciò che restituisce un valore va bene. Puoi sempre inserire qualsiasi codice in una funzione, farlo restituire qualcosa e chiamare quella funzione tra parentesi graffe. Tuttavia, mantieni al minimo la logica che hai inserito in queste parentesi graffe.

Le variabili JavaScript sono anche espressioni, quindi quando il componente riceve un elenco di oggetti di scena è possibile utilizzare questi oggetti all'interno di parentesi graffe. È così che abbiamo usato {label} nell'esempio Button.

I letterali degli oggetti JavaScript sono anche espressioni. A volte usiamo un oggetto JavaScript tra parentesi graffe, il che lo fa sembrare doppie parentesi graffe: {{a: 42}}. Questa non è una sintassi diversa; è solo un oggetto letterale definito all'interno delle normali parentesi graffe JSX.

Ad esempio, un caso d'uso per usare un oggetto letterale in queste parentesi graffe è passare un oggetto di stile CSS all'attributo di stile speciale in React:

// jsdrops.com/bx5
const ErrorDisplay = ({message}) => (
  
    {Messaggio}    );
ReactDOM.render (
  ,
  mountNode
);

L'attributo di stile sopra è speciale. Usiamo un oggetto come suo valore e quell'oggetto definisce gli stili come se li stessimo impostando tramite l'API DOM di JavaScript (nomi delle proprietà del caso di cammello, valori di stringa). React traduce questi oggetti di stile in attributi di stile CSS incorporati. Questo non è il modo migliore per modellare un componente React ma lo trovo estremamente comodo da usare quando si applicano gli stili condizionali agli elementi. Ad esempio, ecco un componente che genererà il suo testo in verde o rosso in modo casuale per circa la metà del tempo:

// jsdrops.com/bx6
class ConditionalStyle estende React.Component {
  render () {
    ritorno (
      
        Ti piace questo?            );   } }
ReactDOM.render (
  ,
  mountNode,
);

La logica di questo stile è proprio lì nel componente. Mi piace! È più facile lavorare rispetto all'uso condizionale di un nome di classe e quindi tracciare cosa sta facendo quel nome di classe nel foglio di stile CSS globale.

JSX non è un linguaggio modello

Alcune librerie che si occupano di HTML forniscono un linguaggio modello per esso. Scrivi le tue visualizzazioni dinamiche con una sintassi HTML "migliorata" con loop e condizionali. Queste librerie utilizzeranno quindi JavaScript per convertire i modelli in operazioni DOM. Le operazioni DOM possono quindi essere utilizzate nel browser per visualizzare la struttura DOM descritta dall'HTML avanzato.

React ha eliminato quel passaggio. Non inviamo affatto al browser un modello con un'applicazione React. Gli abbiamo inviato un albero di oggetti descritto con l'API React. React utilizza questi oggetti per generare le operazioni DOM necessarie per visualizzare l'albero DOM desiderato.

Quando viene utilizzato un modello HTML, la libreria analizza l'applicazione come una stringa. Un'applicazione React viene analizzata come un albero di oggetti.

Mentre JSX potrebbe apparire come un linguaggio modello, in realtà non lo è. È solo un'estensione JavaScript che ci consente di rappresentare l'albero degli oggetti di React con una sintassi che assomiglia a un modello HTML. I browser non devono assolutamente avere a che fare con JSX e React non deve nemmeno occuparsene! Lo fa solo il compilatore. Ciò che inviamo al browser è senza template e senza codice JSX.

Ad esempio, per l'array todos che abbiamo visto sopra, se vogliamo mostrare quell'array in un'interfaccia utente utilizzando un linguaggio modello, dovremo fare qualcosa del tipo:

      <% FOR ogni todo nell'elenco di todos%>     
  • <% = todo.body%>
  •   <% END FOR%>
<%%> È una sintassi per rappresentare le parti dinamiche migliorate. Potresti anche vedere la sintassi {{}}. Alcuni linguaggi modello utilizzano attributi speciali per la loro logica avanzata. Alcuni linguaggi modello fanno uso del rientro degli spazi bianchi (regola off-side).

Quando si verificano cambiamenti nell'array todos (e dobbiamo aggiornare ciò che viene reso nel DOM con un linguaggio di modello) dovremo ri-renderizzare quel modello o calcolare dove nell'albero DOM dobbiamo riflettere il cambiamento nell'array todos .

In un'applicazione React, non esiste alcun linguaggio di template. Invece, usiamo JSX:

      {todos.map (todo =>     
  • {todo.body}   )}

Che, prima di essere utilizzato nel browser, viene tradotto in:

React.createElement (
  "Ul",
  nullo,
  todos.map (todo =>
    React.createElement ("li", null, todo.body)
  ),
);

React prende questo albero di oggetti e lo trasforma in un albero di elementi DOM. Dal nostro punto di vista, abbiamo finito con questo albero. Non gestiamo alcuna azione al riguardo. Gestiamo semplicemente le azioni nell'array todos stesso.

Creazione di componenti mediante le classi

React supporta anche la creazione di componenti attraverso la sintassi della classe JavaScript. Ecco lo stesso esempio del componente Button scritto con la sintassi della classe:

// jsdrops.com/bx7
Il pulsante class estende React.Component {
  render () {
    ritorno (
      
// Usalo (stessa sintassi)
ReactDOM.render (

In questa sintassi, si definisce una classe che estende React.Component, che è una delle classi principali nell'API di livello superiore di React. Un componente React basato sulla classe deve almeno definire un metodo di istanza chiamato render. Questo metodo di rendering restituisce l'elemento che rappresenta l'output di un oggetto istanziato dal componente. Ogni volta che utilizziamo il componente basato sulla classe Button (eseguendo il rendering di un

ReactDOM.render (

Questa variabile di conteggio sarà l'elemento di stato che dobbiamo introdurre nell'esempio. È un pezzo di dati da cui dipenderà l'interfaccia utente (perché la stiamo visualizzando) ed è un elemento di stato perché cambierà nel tempo.

Ogni volta che definisci una variabile nel tuo codice, introducerai uno stato e ogni volta che cambi il valore di quella variabile, stai mutando quello stato. Tienilo a mente.

Prima di poter modificare il valore dello stato di conteggio, è necessario conoscere gli eventi.

Risposta agli eventi dell'utente

È possibile aggiungere un gestore eventi con una proprietà "onEvent" (in questo caso all'elemento pulsante). Potrebbe trattarsi di onClick, onMouseOver, onScroll, onSubmit, ecc.

Ciò di cui abbiamo bisogno qui è un evento onClick e lo definiamo semplicemente come un attributo sull'elemento target. Ad esempio, per fare in modo che il programma registri un messaggio sulla console ogni volta che si fa clic sul pulsante, possiamo fare qualcosa del tipo:

pulsante const = () => {
  lascia contare = 0;
  ritorno (
    
ReactDOM.render (

A differenza della versione DOM dell'attributo onClick (che utilizza una stringa) l'attributo onClick di React utilizza un riferimento di funzione. Lo specifichi tra parentesi graffe.

funzione func () {}

Nota come abbiamo passato il riferimento di funzione (nome) come gestore onClick. Non abbiamo invocato funzioni lì. React invocherà func quando si fa clic sul pulsante.

Per l'evento onClick nel componente Button sopra, abbiamo "incorporato" una definizione di funzione che quando viene richiamata genererà un messaggio sulla console. Ogni volta che clicchiamo sul pulsante verrà richiamato il gestore onClick (la funzione freccia in linea) e vedremo quel messaggio.

Nota come il nome dell'evento è camel-case. Tutti gli attributi relativi al DOM (che sono gestiti da React) devono essere camel-case (e React mostrerà un errore se non è così). React supporta anche l'utilizzo di attributi HTML personalizzati e questi devono essere in formato tutto minuscolo.
Alcuni attributi DOM in React sono leggermente diversi da quelli che fanno nella normale API DOM. Un esempio è l'evento onChange. In un normale browser, di solito viene attivato quando si fa clic all'esterno di un campo del modulo (o si esce da una scheda). In React, onChange si attiva ogni volta che viene modificato il valore di un campo modulo (su ogni carattere aggiunto / rimosso).
Alcuni attributi in React sono chiamati in modo diverso dal loro equivalente HTML. Un esempio è l'attributo className in React che equivale a utilizzare l'attributo class in HTML. Per un elenco completo delle differenze tra gli attributi React e gli attributi DOM, consultare jscomplete.com/react-attributes.

Lettura e aggiornamento dello stato

Per tenere traccia degli aggiornamenti di stato e attivare la differenza DOM virtuale e la riconciliazione del DOM reale, React deve essere a conoscenza di eventuali modifiche che si verificano a tutti gli elementi di stato utilizzati all'interno dei componenti. Per fare ciò in modo efficiente, React richiede l'uso di getter e setter speciali per ogni elemento di stato che si introduce in un componente. È qui che entra in gioco il gancio useState. Definisce un elemento di stato e ci restituisce un getter e setter per esso!

Ecco cosa ci serve per l'elemento di stato di conteggio che stiamo cercando di implementare:

const [count, setCount] = React.useState (0);

La funzione useState restituisce un array con esattamente 2 elementi. Il primo elemento è un valore (getter) e il secondo elemento è una funzione (setter). Ho usato l'array destructuring per dare questi nomi agli oggetti. Puoi dare loro tutti i nomi che desideri ma [name, setName] è la convenzione.

Il primo elemento "valore" può essere una stringa, un numero, un array o altri tipi. In questo caso, avevamo bisogno di un numero e avevamo bisogno di inizializzare quel numero con 0. L'argomento di React.useStateis usato come valore iniziale dell'elemento state.

Il secondo elemento "funzione" cambierà il valore dell'elemento state quando viene invocato (e, se necessario, attiverà l'elaborazione DOM). Ogni volta che viene invocata la funzione setCount, React esegue nuovamente il rendering del componente Button che aggiornerà tutte le variabili definite nel componente (incluso il valore di conteggio). L'argomento che passiamo a setCount diventa il nuovo valore per count.

Quello che dobbiamo fare per incrementare il pulsante della sua etichetta è invocare la funzione setCount all'interno dell'evento onClick e passare ad esso il valore di conteggio attuale incrementato di 1. Ecco il codice completo dell'esempio del pulsante di incremento dell'etichetta:

pulsante const = () => {
  const [count, setCount] = useState (0);
  ritorno (
    
ReactDOM.render (

Vai avanti e provalo. Il pulsante ora aumenterà la sua etichetta ad ogni clic.

Nota come non abbiamo implementato alcuna azione per modificare l'interfaccia utente stessa. Invece, abbiamo implementato un'azione per modificare un oggetto JavaScript (in memoria)! La nostra implementazione dell'interfaccia utente stava sostanzialmente dicendo a React che vogliamo che l'etichetta del pulsante rifletta sempre il valore del countobject. Il nostro codice non ha effettuato alcun aggiornamento DOM. React ha fatto.

Nota anche come ho usato la parola chiave const per definire il conteggio sebbene sia un valore che viene modificato! Il nostro codice non cambierà quel valore. Reagirà quando utilizza una nuova chiamata della funzione Button per rendere l'interfaccia utente del suo nuovo stato. In quella nuova chiamata, la chiamata della funzione useState ci darà un nuovo valore di conteggio nuovo.

La funzione useState è disponibile a livello globale nel parco giochi. Questo è solo un alias di React.useState. Nel tuo codice, puoi usare le importazioni con nome per avere useState disponibile direttamente nell'ambito di un modulo:
import React, {useState} da 'reagire';

Avrai bisogno di qualche altro esempio per apprezzare questo potere. Aggiungiamo alcune altre funzionalità a questo esempio di base. Abbiamo molti pulsanti e facciamo in modo che tutti incrementino un singolo valore di conteggio condiviso.

Lavorare con più componenti

Dividiamo il componente Button che abbiamo finora in due componenti:

  • Mantenere un componente Button per rappresentare un elemento button, ma con un'etichetta statica.
  • Aggiungi un nuovo componente di visualizzazione per visualizzare il valore del conteggio.

Il nuovo componente Display sarà puramente presentazionale senza stato o interazioni proprie. È normale. Non tutti i componenti di React devono avere hook con stato o essere interattivi.

const Display = (oggetti di scena) => (
  
 CONTEGGIO VALORE QUI ... 
);

La responsabilità del componente Display è semplicemente visualizzare un valore che riceverà come prop. Ad esempio, il fatto che un pre-elemento sia stato utilizzato per ospitare il valore fa parte di tale responsabilità. Altri componenti di questa applicazione non hanno voce in capitolo!

Rendering di componenti di pari livello

Ora abbiamo due elementi da renderizzare: pulsante e display. Non possiamo renderli direttamente uno accanto all'altro in questo modo:

// Questo non funzionerà
ReactDOM.render (

Gli elementi adiacenti non possono essere renderizzati in questo modo in React perché ognuno di essi viene tradotto in una chiamata di funzione quando JSX viene convertito. Hai alcune opzioni per affrontare questo problema.

Innanzitutto, puoi passare una matrice di elementi a ReactDOM.render e inserire in quella matrice quanti elementi React desideri.

Opzione 1

ReactDOM.render ([

Questa è di solito una buona soluzione quando tutti gli elementi che stai visualizzando provengono da una fonte dinamica. Tuttavia, non è l'ideale per il caso che stiamo facendo qui.

Un'altra opzione è quella di rendere gli elementi React di pari livello figli di un altro elemento React. Ad esempio, possiamo semplicemente racchiuderli in un elemento div.

Opzione 2

ReactDOM.render (
  
            ,   mountNode );

L'API React supporta questo annidamento. In effetti, React ha un oggetto speciale se è necessario racchiudere più elementi adiacenti come questo senza introdurre un nuovo nodo padre DOM. Puoi usare React.Fragment:

Opzione n. 3

ReactDOM.render (
  
    
    
  ,
  mountNode
);

Questo caso è così comune in React che l'estensione JSX ha una scorciatoia per questo. Invece di digitare React.Fragment, puoi semplicemente usare un tag vuoto <> .

Opzione n. 3 +

ReactDOM.render (
  <>
    
    
  ,
  mountNode
);

Il tag vuoto verrà traspilato nella sintassi React.Fragment. Userò questa sintassi per continuare con l'esempio.

Tuttavia, dovresti sempre provare a fare il primo argomento su ReactDOM.render una chiamata a singolo componente invece dell'albero nidificato che abbiamo appena fatto. Questa è essenzialmente una preferenza di qualità del codice. Ti costringe a pensare alla gerarchia, ai nomi e alle relazioni dei tuoi componenti. Lo facciamo dopo.

Il componente di primo livello

Introduciamo un componente di livello superiore per ospitare sia i componenti Button che Display. La domanda ora è: come dovremmo chiamare questo nuovo componente genitore?

Che ci crediate o no, nominare i componenti e i loro elementi di stato / oggetti di scena è un compito molto difficile che influenzerà il modo in cui questi componenti funzionano ed eseguono. I nomi giusti ti costringeranno alle giuste decisioni di progettazione. Prenditi del tempo e pensa a ogni nuovo nome che introduci nelle tue app React.

Poiché questo nuovo componente principale ospiterà un display con un pulsante che incrementa il conteggio visualizzato, possiamo considerarlo come il gestore del valore di conteggio. Chiamiamolo CountManager.

const CountManager = () => {
  ritorno (
    <>
      
      
    
  );
};
ReactDOM.render (, mountNode);

Poiché visualizzeremo il valore del conteggio nel nuovo componente Display, non è più necessario mostrare il valore del conteggio come etichetta del pulsante. Invece, possiamo cambiare l'etichetta in qualcosa come "+1".

pulsante const = () => {
  ritorno (
    

Nota che ho anche rimosso l'elemento state dal componente Button perché non possiamo più averlo lì. Con il nuovo requisito, sia i componenti Button che Display devono accedere all'elemento state count. Il componente Display lo visualizzerà e il componente Button lo aggiornerà. Quando un componente deve accedere a un elemento di stato che appartiene al suo componente di pari livello, una soluzione è "alzare" tale elemento di livello di un livello superiore e definirlo all'interno del componente principale. In questo caso il genitore è il componente CountManager che abbiamo appena introdotto.

Spostando lo stato su CountManager, ora possiamo "far fluire" i dati dal genitore al figlio usando i puntelli dei componenti. Questo è ciò che dovremmo fare per visualizzare il valore di conteggio nel componente Display:

const Display = ({content}) => (
  
 {content} 
);
const CountManager = () => {
  const [count, setCount] = useState (0);
  ritorno (
    <>
      
      
    
  );
};
ReactDOM.render (, mountNode);

Nota come in CountManager ho usato esattamente la stessa rigaState che era nel componente Button. Stiamo sollevando lo stesso elemento di stato. Nota anche come quando ho trasferito il valore del conteggio verso il basso al componente Display tramite un puntello, ho usato un nome diverso per esso (contenuto). È normale. Non è necessario utilizzare lo stesso nome esatto. In effetti, in alcuni casi, l'introduzione di nuovi nomi generici è migliore per il componente figlio perché li rende più riutilizzabili. Il componente Display può essere riutilizzato per visualizzare altri valori numerici oltre al conteggio.

I componenti principali possono anche far fluire il comportamento verso i propri figli, che è quello che dobbiamo fare dopo.

Dato che l'elemento count state è ora nel componente CountManager, abbiamo bisogno di una funzione a quel livello per gestire l'aggiornamento. Chiamiamo questa funzione incrementCounter. La logica per questa funzione è in realtà la stessa logica che avevamo prima nella funzione handleClick nel componente Button. La nuova funzione incrementCounter aggiornerà lo stato di conteggio del componente CountManager per incrementare il valore utilizzando il valore precedente:

const CountManager = () => {
  // ....
  const incrementCounter = () => {
    setCount (count + 1);
  }
  // ...
};

Il gestore onClick nel componente Button ora deve cambiare. Vogliamo che esegua la funzione incrementCounter che si trova nel componente CountManager ma un componente può accedere solo alle proprie funzioni. Quindi, per rendere il componente Button in grado di invocare la funzione incrementCounter nel componente CountManager, possiamo passare un riferimento a incrementCounter al componente Button come prop. Sì, anche gli oggetti di scena possono contenere funzioni, non solo dati. Le funzioni sono solo oggetti in JavaScript e proprio come gli oggetti puoi passarli.

Possiamo nominare questo nuovo oggetto qualsiasi. Lo chiamerò clickAction e gli passerò un valore di incrementCounter, che è il riferimento alla funzione che abbiamo definito nel componente CountManager. È possibile utilizzare questo nuovo comportamento tramandato direttamente come valore del gestore onClick. Sarà un supporto per il componente Button:

pulsante const = ({clickAction}) => {
  ritorno (
    
// ...
const CountManager = () => {
  // ...
  ritorno (
    
      

Qui sta succedendo qualcosa di molto potente. Questa proprietà clickAction ha permesso al componente Button di richiamare la funzione incrementCounter del componente CountManager. È come quando si fa clic su quel pulsante, il componente Button raggiunge il componente CountManager e dice: "Ehi genitore, vai avanti e invoca quel comportamento del contatore di incrementi ora".

In realtà, il componente CountManager è quello che controlla qui e il componente Button segue solo regole generiche. Se analizzi il codice così com'è adesso, ti renderai conto di come il componente Button non abbia idea di cosa succede quando viene cliccato. Segue solo le regole definite dal genitore e invoca un'azione click-click generica. Il genitore controlla ciò che accade in quel comportamento generico. Ciò segue il concetto di isolamento della responsabilità. Ogni componente qui ha alcune responsabilità e si concentrano su quello.

Guarda il componente Display per un altro esempio. Dal suo punto di vista, il valore di conteggio non è uno stato. È solo un sostegno che il componente CountManager gli sta passando. Il componente Display mostrerà sempre quell'elica. Anche questa è una separazione di responsabilità.

Come progettista di questi componenti, puoi scegliere il loro livello di responsabilità. Ad esempio, avremmo potuto assumerci la responsabilità di visualizzare il valore di conteggio parte del componente CountManager stesso e di non utilizzare un nuovo componente di visualizzazione per questo.

Il componente CountManager ha la responsabilità di gestire lo stato di conteggio. Questa è una decisione di progettazione importante che abbiamo preso ed è quella che dovrai fare molto in un'applicazione React. Dove definire lo stato?

La pratica che seguo consiste nel definire un elemento di stato in un nodo principale condiviso il più vicino possibile a tutti i bambini che devono accedere a tale elemento di stato. Per una piccola applicazione come questa, ciò significa in genere il componente di livello superiore stesso. Nelle applicazioni più grandi, un sottoalbero potrebbe gestire il proprio "ramo" di stato anziché fare affidamento su elementi di stato globali definiti sul componente radice di livello superiore.

Il componente di primo livello viene comunemente utilizzato per gestire lo stato e le azioni delle applicazioni condivise perché è padre di tutti gli altri componenti. Prestare attenzione a questo progetto perché l'aggiornamento di un elemento di stato sul componente di livello superiore significa che l'intero albero dei componenti verrà ridistribuito (in memoria).

Ecco il codice completo per questo esempio finora:

// jsdrops.com/bx8
pulsante const = ({clickAction}) => {
  ritorno (
    
const Display = ({content}) => (
  
 {content} 
);
const CountManager = () => {
  const [count, setCount] = useState (0);
  const incrementCounter = () => {
    setCount (count + 1);
  };
  ritorno (
    
      

Rendere i componenti riutilizzabili

I componenti riguardano la riusabilità. Rendiamo riutilizzabile il componente Button modificandolo in modo che possa incrementare il conteggio globale con qualsiasi valore, non solo 1.

Iniziamo aggiungendo più elementi Button nel componente CountManager in modo da poter testare questa nuova funzionalità:

const CountManager = () => {
  // ..
  ritorno (
    <>
      

Tutti gli elementi Button visualizzati sopra avranno attualmente un'etichetta +1 e aumenteranno il conteggio di 1. Vogliamo far loro visualizzare un'etichetta diversa che è specifica per ogni pulsante e far loro eseguire un'azione diversa in base a un valore specifico a ciascuno di essi. Ricorda che puoi passare qualsiasi valore a un elemento React come prop.

Ecco l'interfaccia utente che ho in mente dopo aver fatto clic su ogni pulsante una volta:

Nello screenshot sopra, il conteggio è iniziato con 0. Ho aggiunto 1, quindi 5 e quindi 10 per arrivare a 16

Prima di completare questo esercizio, prenditi del tempo, pensaci e prova a implementarlo da solo. È per lo più semplice. Suggerimento: dovrai introdurre 1 nuovo oggetto di scena per Button. Dagli Un colpo. Torna quando sei pronto a confrontare la tua soluzione con la mia.

Aggiunta di nuovi oggetti di scena

La prima cosa che dobbiamo fare è rendere l'etichetta +1 nel componente Button personalizzabile.

Per rendere qualcosa personalizzabile in un componente React, introduciamo un nuovo prop (che il componente genitore può controllare) e facciamo in modo che il componente utilizzi il suo valore. Nel nostro esempio, possiamo fare in modo che il componente Button riceva l'importo da incrementare (1, 5, 10) come nuovo prop. Lo chiamerò clickValue. Possiamo cambiare il metodo di rendering in CountManager per passare i valori con cui vogliamo testare questo nuovo prop.

ritorno (
  <>
    

Nota un paio di cose su questo codice finora:

  • Non ho dato un nome alla nuova proprietà con alcun elemento relativo al conteggio. Il componente Button non deve essere consapevole del significato del suo evento click. Deve solo passare questo clickValue quando viene attivato il suo evento click. Ad esempio, nominare questa nuova proprietà countValue non sarebbe la scelta migliore perché ora, nel Componente pulsante, leggiamo il codice per capire che un elemento Button è correlato a un conteggio. Ciò rende il componente Button meno riutilizzabile. Ad esempio, se voglio usare lo stesso componente Button per aggiungere una lettera a una stringa, il suo codice sarebbe confuso.
  • Ho usato parentesi graffe per passare i valori della nuova proprietà clickValue (clickValue = {5}). Non ho usato stringhe lì (clickValue = "5"). Dato che ho un'operazione matematica a che fare con questi valori (ogni volta che si fa clic su un pulsante), ho bisogno che questi valori siano numeri. Se li passo come stringhe, dovrei fare una conversione da stringa a numero quando l'operazione di aggiunta deve essere eseguita.
Passare un numero come stringa è un errore comune in React. Vedi questo articolo per ulteriori errori comuni relativi a React.

Personalizzare i comportamenti

L'altra cosa che dobbiamo rendere generici nel componente CountManager è la funzione di azione incrementCounter. Non può avere un conteggio hardcoded + 1 come fa ora. Simile a quello che abbiamo fatto per il componente Button, per rendere generica una funzione facciamo in modo che riceva un argomento e utilizziamo il valore di tale argomento. Per esempio:

incrementCounter = (incrementValue) => {
  setCount (count + incrementValue);
};

Ora tutto ciò che dobbiamo fare è fare in modo che il componente Button usi la sua proprietà clickValue come etichetta e faccia invocare la sua azione onClick con il suo clickValue come argomento.

pulsante const = ({clickValue, clickAction}) => {
  ritorno (
    

Nota come ho dovuto avvolgere l'elica onClick con una funzione freccia in linea per renderlo associato al clickValue del Button. La chiusura JavaScript per questa nuova funzione freccia si occuperà di questo.

I tre pulsanti dovrebbero ora aumentare con i loro tre diversi valori di clic. Puoi vedere il codice di questo esempio su jsdrops.com/bx9.

Accettare input da parte dell'utente

Immagina di dover contare i caratteri che un utente digita in un'area di testo, proprio come il modulo tweet di Twitter. Con ciascun carattere digitato dall'utente, è necessario aggiornare l'interfaccia utente con il nuovo conteggio dei caratteri.

Ecco un componente che visualizza un elemento di input textarea con un div segnaposto per il conteggio dei caratteri:

// jsdrops.com/bx10
const CharacterCounter = () => {
  ritorno (