React

React

In questo post parleremo di React: nuova libreria, creata da Facebook, da utilizzare nel front-end di applicazioni web. Si pone come diretto concorrente di AngularJS e soprattutto del futuro AngularJS 2. Implementeremo insieme la classica todo-list per cercare di capire insieme quali sono le principali caratteristiche di queste nuove API.

Caratteristiche

Uno dei concetti più interessanti di React è il virtual DOM: tutte le operazioni che React effettua sul DOM vengono in realtà “parcheggiate” in un DOM virtuale in memoria che fa da specchio a quello reale, alla fine di ogni ciclo di render vengono trasportate sul DOM reale esclusivamente le modifiche minime necessarie per allineare i due DOM. Questo meccanismo permette a React di essere estremamente efficiente.

Un’altra caratteristica interessante di React è quello di essere isomorfico, può essere cioè eseguito indifferente su un browser o su di un server Node.js, in questo modo si evita uno dei problemi maggiori di framework front-end complessi come AngularJS: l’assenza di indicizzazione da parte dei motori di ricerca. Infatti con React è possibile effettuare il primo render della pagina tramite il server e tutte le interazioni successive con il browser. In questo modo gli spider di Google sono in grado di analizzare il contenuto di una pagina e calcolare il PageRank del nostro sito. D’altro canto un’applicazione AngularJS non contiene informazioni fintanto che l’applicazione stessa non è completamente caricata e questo rende difficile agli spider l’analisi del sito.

Setup del progetto

Vediamo subito la index della nostra applicazione:

<html>
  <head>
    <title>React Todo-list</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
    <script src="https://fb.me/react-0.13.3.js"></script>
    <script src="https://fb.me/JSXTransformer-0.13.3.js"></script>
    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
    <link rel="icon" href="favicon.ico" type="image/x-icon"/>
    <link rel="shortcut icon" href="favicon.ico" type="image/x-icon"/>
  </head>
  <body>
    <div id="wrapper">

    </div>
    <script src="js/TodoRepository.js"></script>
    <script type="text/jsx" src="js/TodoApp.js"></script>
    <script type="text/jsx" src="js/TodoForm.js"></script>
    <script type="text/jsx" src="js/TodoList.js"></script>
    <script type="text/jsx" src="js/Todo.js"></script>
    <script type="text/jsx">
      React.render(<TodoApp/>,document.getElementById('wrapper'));
    </script>
  </body>
</html>

Due sono le cose da notare nell’indice: l’import del JSXTransformer e la presenza di script di tipo text/jsx. JSX è un estensione del JavaScript che permettere di aggiungere codice HTML all’interno di file di codice. Il JSXTransformer compila a runtime i file JSX in file Javascript puro compatibili con i browser, questa trasformazione è molto onerosa quindi è indispensabile una fase di deploy in produzione, nella quale tramite il tool react-tools potete effettuare questa trasformazione in modalità off-line guadagnando in velocità.

Componenti

L’unica riga di codice all’interno della nostra index è la seguente:

React.render(<TodoApp/>,document.getElementById('wrapper'));

Che non fa altro che aggiungere il componente TodoApp all’interno del div wrapper visto nell’index. Questa riga ci fa intuire la caratterista principe di React: ogni applicazione è composta esclusivamente da componenti, compresa l’applicazione stessa. Analizziamo l’elemento Todo, il tassello più piccolo della nostra todo-list.

var Todo = React.createClass({
  onDeleteClick:function(){
  	this.props.onDeleteCallback(this.props.value);
  },
  render: function() {
    return (
      <tr>
        <td>
      	{this.props.value}
        <button className="pull-right btn btn-default" onClick={this.onDeleteClick}>
          Done
        </button>
        </td>
      </tr>
    );
  }
});

Ecco in pratica l’utilizzo di JSX, ci permette di creare delle classi di React che contengono contemporaneamente business logic e markup. Per quanto possa sembrare un anti-pattern in realtà funziona se i componenti creati sono autoconsistenti. In questo modo sono facilmente testabili e mantenibili. Questi componenti possono essere facilmente composti tra di loro come possiamo vedere nel prossimo componente: la TodoList.

var TodoList = React.createClass({
	render: function() {
		var items = this.props.data.map(function(todo,i) {
			return (<Todo key={i} onDeleteCallback={this.props.onDeleteCallback} value={todo}></Todo>);
		},this);
		return (<table className="table">{items}</table>);
	}
});

Come vedete la TodoList non è nient’altro che una <table> con all’interno dei Todo (che a loro volta erano delle <tr>). In entrambi gli esempi è presente l’oggetto props che contiene gli attributi dei vari componenti (data in questo caso, value e onDeleteCallback nel caso precedente).

Passiamo ora alla form di inserimento di un nuovo elemento:

var TodoForm = React.createClass({
	saveTodo:function(){

		var inputComponent = React.findDOMNode(this.refs.todoInput);

		this.props.onSave(inputComponent.value);

		inputComponent.value = "";
	},
	render: function() {
		return (
			<div className="container">
			  <div className="row">
			  	<div className="col-sm-8">
			  		<input type="text" className="form-control" id="textInput" ref="todoInput" placeholder="Add Todo..."/>
			  	</div>
			  	<div className="col-sm-4">
			  		<button className="btn btn-default" onClick={this.saveTodo}>
			  			Save
			  		</button>
			  	</div>
			  </div>
			</div>
		);
	}
});

In questo caso abbiamo bisogno di recuperare il valore da un <input> e per farlo dobbiamo utilizzare l’attributo ref e la funzione React.findDOMNode.

Ultimo componente da analizzare è la TodoApp stessa:

var TodoApp = React.createClass({
  getInitialState: function() {
  	return {data: todoRepository.list()};
  },
  onSave:function(todo){
    this.setState({data:todoRepository.store(todo)});
  },
  onDelete:function(todo){
    this.setState({data:todoRepository.delete(todo)});
  },
  render: function() {
    return (
    	<div>
    		<h1 className="text-center">TodoApp</h1>
        <TodoForm onSave={this.onSave}/>
        <hr/>
    		<TodoList onDeleteCallback={this.onDelete} data={this.state.data}/>
    	</div>
    );
  }
});

In questo componente possiamo finalmente capire come React gestisce lo stato dei componenti. Nella funzione getInitialState, viene inizializzato quasi fosse un costruttore, dopodiché attraverso il metodo setState possiamo modificare lo stato a nostro piacimento. Ogni cambiamento di stato in qualsiasi componente scatenerà un render totale di tutta l’applicazione in maniera trasparente. Nel nostro caso questo avviene quando salviamo o eliminiamo un todo. Questo è il risultato finale.

React todo-list

React todo-list

Conclusioni

Dopo questo piccolo tutorial sarete in grado di iniziare a creare progetti con React. Qualcuno di voi si starà chiedendo: “ma il routing? la dependency injection? c’era bisogno di tutte quelle callback?” ma dovete tenere presente che React è un tool e non un framework completo come AngularJS. Quindi potete creare il vostro stack mescolando tool differenti. La galassia React si sta popolando di un numero enorme di progetti e best practices che saranno oggetto di analisi in appositi post. Consiglio caldamente a tutti di studiare un po’ di React in quanto potrebbe diventare “the next big thing” dello sviluppo web.

Come al solito i più curiosi possono trovare il sorgente di questa applicazione sul repository GitHub di CNJ. Alla prossima!

Francesco Strazzullo

Faccio il Front-end engineer per extrategy dove mi occupo di applicazioni Angular, React e applicazioni mobile. Da sempre sono appassionato di Architetture software e cerco di applicarle in maniera innovativa allo sviluppo frontend in generale.