Vai al contenuto

React: creiamo una semplice applicazione

Nel precedente articolo abbiamo introdotto React e abbiamo creato un progetto attraverso Create-React-App. In questo articolo svilupperemo step by step il codice per una semplice app di messaggistica. Eravamo rimasti più o meno qui…

Introduzione alla sintassi JSX

Consideriamo questa dichiarazione variabile:

const element = <h1>Hello, world!</h1>;

Questa sintassi non è né una stringa né HTML. Si chiama JSX ed è un’estensione di sintassi di JavaScript simile all’XML/HTML che viene usata per il rendering di HTML. JSX facilita il rimescolamento di codice: anzichè separare markup e logica in file separati, tutto viene scritto in un unico file JS anche se sembra di scrivere HTML nativo. JSX non è richiesto per usare React ma è sicuramente uno strumento utile quando si lavora con le interfacce utente all’interno del codice JavaScript. Accoppiato con NodeJs garantisce un flusso di lavoro piuttosto consistente.

In questo esempio, preso direttamente dalla guida ufficiale, dichiariamo una variabile chiamata name e la usiamo all’interno di JSX tra parentesi graffe:

const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;

ReactDOM.render(
  element,
  document.getElementById('root')
);

Possiamo inserire qualsiasi espressione JavaScript valida all’interno delle parentesi graffe in JSX. Ad esempio, 2 + 2, user.firstName, o formatName(user) sono tutte le espressioni JavaScript valide.

In quest’altro esempio della guida ufficiale, incorporiamo il risultato della chiamata della funzione formatName(user), in un elemento <h1>.

function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

const user = {
  firstName: 'Harper',
  lastName: 'Perez'
};

const element = (
  <h1>
    Hello, {formatName(user)}!
  </h1>
);

ReactDOM.render(
  element,
  document.getElementById('root')
);

Specifica degli attributi con JSX

È possibile utilizzare le virgolette per specificare stringhe come attributi:

const element = <div tabIndex="0"></div>;

È possibile anche utilizzare parentesi graffe per incorporare un’espressione JavaScript in un attributo:

const element = <img src={user.avatarUrl}></img>;

Tuttavia, non bisogna racchiudere dentro le virgolette le espressioni definite dentro le parentesi graffe. Bisogna utilizzare le virgolette (per le stringhe) o le parentesi graffe (per le espressioni), ma non entrambi nello stesso attributo. Poiché JSX è più vicino a JavaScript che a HTML, React DOM utilizza la convenzione camelCase per i nomi degli attributi HTML.

Ad esempio, tabindex diventa tabIndex, oppure onclick diventa onClick. Una piccola eccezione è data dall’attributo class che diventa className per evitare conflitti con la parola riservata class in Javascript.

La root dell’app

Tornando al nostro esempio, proviamo ad inserire, all’interno della cartella src, un file index.js con il seguente codice:

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(<h1>Hello World</h1>, document.getElementById('root'));

Avviamo nuovamente il server di sviluppo utilizzando npm start. Il browser dovrebbe mostrare una pagina bianca con la scritta Hello World.

Il file index.js è la radice del progetto in cui verranno renderizzati i componenti di React. Come funziona?

  • Riga 1: il pacchetto React viene importato per gestire l’elaborazione JSX
  • Riga 2: il pacchetto ReactDOM viene importato per renderizzare i componenti React.
  • Riga 4: Chiama la funzione render con due argomenti
    • <h1>Hello World</h1>: un elemento JSX
    • document.getElementById(‘root’): il contenitore HTML in cui verrà posizionato Hello World

Il contenitore HTML di cui si fa riferimento in index.js si trova nel file public/index.html. Sulla linea 28, si dovrebbe vedere l’elemento <div id=”root”></div> che è chiamato Root DOM e il contenuto all’interno di esso sarà gestito dal React DOM.

Dai un’occhiata al seguente esempio:

const element = <span>Hello,</span> <span>John</span;

In questo caso React produrrà un errore di sintassi, poichè tutti gli elementi devono seguire la stessa sintassi di HTML (se non te ne sei accordo, alla fine della riga manca il simbolo > per chiudere il tag span). Un blocco JSX può anche essere memorizzato in una variabile di tipo const, così:

const element = <div>
   <span>Hello, </span>
   <span>John</span>
</div>;

Oppure è possibile richiamare variabili e oggetti più complessi all’interno di altri blocchi JSX, in questo modo:

const name = "John";
const element = <p>Hello, {name}</p>

oppure in questo modo:

const user = {
  firstName: "John",
  lastName: "Doe"
}
const element = <p>Hello, {user.firstName} {user.lastName}</p>

Dichiariamo i componenti

Creiamo adesso un altro file all’interno di src e lo chiamiamo App.js. Al suo interno scriveremo:

import React, { Component } from 'react';

class App extends Component {

  render(){
    return (
      <div>
        Hello World Again!
      </div>
    )
  }
}

export default App;

In questo modo creiamo un componente React definendo una classe JavaScript che estende la superclasse React.Component. Così facendo, non ci resta che definire la funzione di rendering che restituisce un elemento JSX (abbiamo inoltre inserito del codice JSX aggiuntivo all’interno del tag div). Successivamente, basta aggiornare il file root index.js in questa maniera:

import React from 'react';
import ReactDOM from 'react-dom';

import App from './App';

ReactDOM.render(<App/>, document.getElementById('root'));

Cos’è stato cambiato? Per prima cosa abbiamo importato il componente App (riga 4: attenzione alla notazione camelCase). Poi ril contenuto di App è stato renderizzato utilizzando la notazione JSX in questo modo <App /> (riga 6): questo passaggio è fondamentale affinché App possa essere inviato al React DOM. Dopo aver salvato le modifiche, possiamo dare un’occhiatina al browser.

JSX e questione di stile

Ci sono due modi per modellare gli elementi JSX:

  1. all’interno del JSX
  2. Fogli di stile esterni

Di seguito è riportato un esempio di come è possibile implementare gli stili all’interno di JSX:

// src/App.js

…
  render() {
    const headerStyle = {
      color: '#ff0000',
      textDecoration: 'underline'
    }
    return (
      <div>
        <h2 style={headerStyle}>Hello World Again!</h2>
      </div>
    )
  }
…

Lo styling di React assomiglia molto a CSS, ma ci sono alcune differenze chiave. Ad esempio, headerStyle è un oggetto letterale. Non possiamo usare il punto e virgola come si fa normalmente nei CSS. Inoltre, un certo numero di dichiarazioni CSS sono modificate per renderle compatibili con la sintassi JavaScript. Ad esempio, al posto di text-decoration, si usa textDecoration. Fondamentalmente, in accordo alla notazione camelCase tali dichiarazioni CSS, ad eccezione dei prefissi dei fornitori come WebkitTransition, devono iniziare con una lettera maiuscola.

Il secondo metodo utilizza i fogli di stile esterni. All’interno della cartella src, viene creato un file denominato App.css con, ad esempio, il seguente codice:

h2 {
  font-size: 4rem;
}

Poi si aggiunge una dichiarazione di import alla riga 2 o 3 del file src/App.js:

// src/App.js
…
import './App.css';
…

Dopo aver salvato, il contenuto del testo del browser dovrebbe cambiare drasticamente le sue dimensioni.

I compomenti stateless e statefull

In React, generalmente gestiamo due tipi di dati: Props e State.

  • Props è un oggetto, funzione, stringa, numero, array, ecc. che vogliamo inviare ad un componente React. Le props devono essere read-only. Ciò sta a significare che un componente non deve mai modificare le props che riceve in input. Se tentate di modificare i props, otterrete il TypeError “Cannot assign to read-only”.
  • State è definito all’interno di un componente e può cambiare durante il ciclo di vita di un componente. Il suo ambito di applicazione è limitato al componente corrente. Un componente può inizializzare il proprio state e aggiornarlo ogni volta che è necessario.

In sostanza, i componenti stateless (noti anche come componenti stupidi) usano props per archiviare i dati, mentre i componenti stateful (noti anche come componenti intelligenti) utilizzano state. Per ottenere una migliore comprensione, esaminiamo i seguenti esempi pratici. All’interno della cartella src, crea una cartella messaggi. All’interno di tale cartella, crea un file view-messaggi.js e digitare il codice seguente per creare un componente stateless:

import React from 'react';

class ViewMessaggi extends Component {
  render() {
    return(
      <div className="container">
        <div className="mittente">
          <span className="etichetta">Da: </span>
          <span className="valore">Mario</span>
        </div>
        <div className="stato">
          <span className="etichetta">Stato: </span>
          <span className="valore"> Non letto</span>
        </div>
        <div className="messaggio">
          <span className="etichetta">Messaggio: </span>
          <span className="valore">Hello world!</span>
        </div>
      </div>
    )
  }
}

export default ViewMessaggi;

A questo punto, aggiungiamo in src/App.css il seguente codice:

container {
  margin-left: 40px;
}

.etichetta{
  font-weight: bold;
  font-size: 1.2rem;
}

.valore{
  color: #474747;
  position: absolute;
  left: 200px;
}

.messaggio .valore {
  font-style: italic;
}

Infine, modifichiamo src/App.js in questo modo:

import React, { Component } from 'react';

import './App.css';
import ViewMessaggi from './messaggi/view-messaggi';

class App extends Component {
  render(){
    return (
      <ViewMessaggi />
    )
  }
}

export default App;

Il risultato su browser sarà il seguente:

Abbiamo creato un componente React stateless. Tuttavia, non è completo poiché è necessario integrarlo con un container stateful. Attualmente, ViewMessage sta visualizzando dati statici. Dobbiamo modificarlo in modo che possa accettare i parametri di input e lo facciamo usando this.props. Assegniamo una variabile chiamata messaggi in this.props e contrassegniamo come obbligatoria la variabile messaggi usando il pacchetto proptypes. Questo serve per rendere più facile il debug del nostro progetto man mano che cresce. Il codice quindi va aggiornato in questo modo:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class ViewMessaggi extends Component {
  render() {
    const msg = this.props.messaggio;
	
    return(
      <div className="container">
        <div className="mittente">
          <span className="etichetta">Da: </span>
          <span className="valore">{msg.mittente}</span>
        </div>
        <div className="stato">
          <span className="etichetta">Stato: </span>
          <span className="valore">{msg.stato}</span>
        </div>
        <div className="messaggio">
          <span className="etichetta">Messaggio: </span>
          <span className="valore">{msg.messaggio}</span>
        </div>
      </div>
    )
  }
}

ViewMessaggi.propTypes = {
  messaggio: PropTypes.object.isRequired
}

export default ViewMessaggi;

Successivamente, creeremo un componente stateful che fungerà da elemento principale del componente ViewMessaggi. Faremo uso del tipo di dati state per memorizzare un messaggio che passeremo a ViewMessaggi. Per fare questo, creiamo il file list-messaggi.js all’interno di src/messaggi con il seguente codice:

import React, { Component } from 'react';
import ViewMessaggi from './view-messaggi';

class ListMessaggi extends Component {

  state = {
    singoloMessaggio: {
        mittente: 'Mario',
        messaggio: 'messaggio 1 di prova',
        stato: 'letto'
      }
  }

  render() {
    return(
      <div>
        <h1>Lista messaggi</h1>
        <ViewMessaggi messaggio={this.state.singoloMessaggio} />
      </div>
    )
  }
}

export default ListMessaggi;

Infine, dobbiamo poter visualizzare più messaggi usando tante istanze di MessageView quanti sono i messaggi contenuti nell’array. Innanzitutto, cambiamo singoloMessaggio in un array e lo rinominiamo in arrayMessaggi. Quindi, usiamo la funzione map per generare l’istanza di ciascun un messaggio presente nell’array (in questo caso 2). Dovremo inoltre popolare un attributo speciale denominato key con un valore univoco index. React ha bisogno di questo per tenere traccia di quali elementi nell’elenco sono stati modificati, aggiunti o rimossi. Il codice finale è questo:

import React, { Component } from 'react';
import ViewMessaggi from './view-messaggi';

class ListMessaggi extends Component {

  state = {
    arrayMessaggi: [
	  {
        mittente: 'Mario',
        messaggio: 'messaggio 1 di prova',
        stato: 'letto'
      },
	  {
        mittente: 'Luigi',
        messaggio: 'messaggio 2 di prova',
        stato: 'non letto'
      }
	]
	  
  }

  render() {
    const mv = this.state.arrayMessaggi.map(function(message, index) {
      return(
        <ViewMessaggi key={index} messaggio={message} />
      )
    })
    return(
      <div>
        <h1>Lista messaggi</h1>
        {mv}
      </div>
    )
  }
}

export default ListMessaggi;

Il risultato nel browser sarà:

Tag: