integrando react.js en aplicaciones symfony (desymfony 2016)

155
de Symfony 16-17 septiembre 2016 Madrid INTEGRANDO REACT.JS EN APLICACIONES SYMFONY Nacho Martín

Upload: ignacio-martin

Post on 08-Jan-2017

771 views

Category:

Software


1 download

TRANSCRIPT

Page 1: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

deSymfony 16-17 septiembre 2016 Madrid

INTEGRANDO REACT.JS EN APLICACIONES SYMFONYNacho Martín

Page 2: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

deSymfony

¡Muchas gracias a nuestros patrocinadores!

Page 3: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Programo en Limenius

Casi todos los proyectos necesitan un frontend rico, por una razón o por otra

Hacemos aplicaciones a medida

Así que le hemos dado unas cuantas vueltas

Page 4: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Objetivo: Mostrar cosas que nos encontramos al usar React desde Symfony, en tierra de nadie, y que ninguno de los dos va a documentar.

Page 5: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

¿A mí qué me importa el frontend?

Page 6: Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Page 7: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

¿Qué es React.js?

Page 8: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Echar huevos en sartén

La premisa fundamental

Cómo hacer una tortillaComprar huevos

Romper huevos

Page 9: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Echar huevos en sartén

Batir huevos

La premisa fundamental

Cómo hacer una tortillaComprar huevos

Romper huevos

Page 10: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Opciones:

La premisa fundamental

Page 11: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Opciones:

La premisa fundamental

1: Repintamos todo.

Page 12: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Opciones:

La premisa fundamental

1: Repintamos todo. Simple

Page 13: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Opciones:

La premisa fundamental

1: Repintamos todo. Simple Poco eficiente

Page 14: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Opciones:

2: Buscamos en el DOM dónde insertar, qué mover, qué eliminar.

La premisa fundamental

1: Repintamos todo. Simple Poco eficiente

Page 15: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Opciones:

2: Buscamos en el DOM dónde insertar, qué mover, qué eliminar.

La premisa fundamental

1: Repintamos todo. Simple

Complejo

Poco eficiente

Page 16: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Opciones:

2: Buscamos en el DOM dónde insertar, qué mover, qué eliminar.

La premisa fundamental

1: Repintamos todo. Simple

Muy eficienteComplejo

Poco eficiente

Page 17: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Opciones:

2: Buscamos en el DOM dónde insertar, qué mover, qué eliminar.

La premisa fundamental

1: Repintamos todo. Simple

Muy eficienteComplejo

Poco eficiente

React nos permite hacer 1, aunque en la sombra hace 2

Page 18: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Dame un estado y una función render() que dependa de él, y olvídate de cómo y cuándo pinto.*

La premisa fundamental

Page 19: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Dame un estado y una función render() que dependa de él, y olvídate de cómo y cuándo pinto.*

La premisa fundamental

* A menos que quieras tener control absoluto.

Page 20: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

¡Clícame! Clicks: 0

Nuestro primer componente

Page 21: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

¡Clícame! Clicks: 1

Nuestro primer componente

¡Clícame!

Page 22: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Nuestro primer componenteimport React, { Component } from 'react';

class Counter extends Component { constructor(props) { super(props); this.state = {count: 1}; }

tick() { this.setState({count: this.state.count + 1}); }

render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> ); }}

export default Counter;

Page 23: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Nuestro primer componenteimport React, { Component } from 'react';

class Counter extends Component { constructor(props) { super(props); this.state = {count: 1}; }

tick() { this.setState({count: this.state.count + 1}); }

render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> ); }}

export default Counter;

Sintaxis ES6 (opcional)

Page 24: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Nuestro primer componenteimport React, { Component } from 'react';

class Counter extends Component { constructor(props) { super(props); this.state = {count: 1}; }

tick() { this.setState({count: this.state.count + 1}); }

render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> ); }}

export default Counter;

Sintaxis ES6 (opcional)

Estado inicial

Page 25: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Nuestro primer componenteimport React, { Component } from 'react';

class Counter extends Component { constructor(props) { super(props); this.state = {count: 1}; }

tick() { this.setState({count: this.state.count + 1}); }

render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> ); }}

export default Counter;

Sintaxis ES6 (opcional)

Modificar estado

Estado inicial

Page 26: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Nuestro primer componenteimport React, { Component } from 'react';

class Counter extends Component { constructor(props) { super(props); this.state = {count: 1}; }

tick() { this.setState({count: this.state.count + 1}); }

render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> ); }}

export default Counter;

Sintaxis ES6 (opcional)

Modificar estado

render(), lo llama React

Estado inicial

Page 27: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Trabajar con el estado

Page 28: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Trabajar con el estado

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

Estado inicial

Page 29: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Trabajar con el estado

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

Estado inicial

this.setState({count: this.state.count + 1});

Asignar estado

Page 30: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Trabajar con el estado

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

Estado inicial

this.setState({count: this.state.count + 1});

Asignar estado

this.state.count = this.state.count + 1;

Simplemente recordar evitar

Page 31: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

render() y JSXrender() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> );}

No es HTML, es JSX. React lo transforma internamente a elementos. Buena práctica: Dejar render() lo más limpio posible, solo un return.

Page 32: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

render() y JSXrender() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> );}

No es HTML, es JSX. React lo transforma internamente a elementos.

Algunas cosas cambian

Buena práctica: Dejar render() lo más limpio posible, solo un return.

Page 33: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

render() y JSXrender() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> );}

No es HTML, es JSX. React lo transforma internamente a elementos.

Algunas cosas cambian

Entre {} podemos insertar expresiones JS

Buena práctica: Dejar render() lo más limpio posible, solo un return.

Page 34: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Thinking in Reactrender() {

return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> );}

Page 35: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Thinking in Reactrender() {

return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> );}

Aquí no modificar el estado

Page 36: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Thinking in Reactrender() {

return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> );}

Aquí no Ajax

Page 37: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Thinking in Reactrender() {

return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> );}

Aquí no calcular decimales de pi y enviar un email

Page 38: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Importante: pensar la jerarquía

Page 39: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Importante: pensar la jerarquía

Page 40: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Jerarquía de componentes: propsclass CounterGroup extends Component { render() { return ( <div> <Counter name="amigo"/> <Counter name="señor"/> </div> ); }}

Page 41: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}> Clícame {this.props.name} </button> <span>Clicks: {this.state.count}</span> </div> );}

y en Counter…

Jerarquía de componentes: propsclass CounterGroup extends Component { render() { return ( <div> <Counter name="amigo"/> <Counter name="señor"/> </div> ); }}

Page 42: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}> Clícame {this.props.name} </button> <span>Clicks: {this.state.count}</span> </div> );}

y en Counter…

Jerarquía de componentes: propsclass CounterGroup extends Component { render() { return ( <div> <Counter name="amigo"/> <Counter name="señor"/> </div> ); }}

Page 43: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Pro tip: componentes sin estado

const Saludador = (props) => { <div> <div>Hola {props.name}</div> </div>}

Page 44: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Todo depende del estado, por tanto:

Page 45: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Todo depende del estado, por tanto:

•Podemos reproducir estados,

Page 46: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Todo depende del estado, por tanto:

•Podemos reproducir estados,•rebobinar,

Page 47: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Todo depende del estado, por tanto:

•Podemos reproducir estados,•rebobinar,•loguear cambios de estado,

Page 48: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Todo depende del estado, por tanto:

•Podemos reproducir estados,•rebobinar,•loguear cambios de estado,•hacer álbum de estilo

Page 49: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Todo depende del estado, por tanto:

•Podemos reproducir estados,•rebobinar,•loguear cambios de estado,•hacer álbum de estilo•…

Page 50: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Learn once, write everywhere

Page 51: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

¿Y si en lugar de algo así…

render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> ); }

Page 52: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

…tenemos algo así?render () { return ( <View> <ListView dataSource={dataSource} renderRow={(rowData) => <TouchableOpacity > <View> <Text>{rowData.name}</Text> <View> <SwitchIOS onValueChange={(value) => this.setMissing(item, value)} value={item.missing} /> </View> </View> </TouchableOpacity> } /> </View> );}

Page 53: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

…tenemos algo así?render () { return ( <View> <ListView dataSource={dataSource} renderRow={(rowData) => <TouchableOpacity > <View> <Text>{rowData.name}</Text> <View> <SwitchIOS onValueChange={(value) => this.setMissing(item, value)} value={item.missing} /> </View> </View> </TouchableOpacity> } /> </View> );}

React Native

Page 54: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

React Targets

• Web - react-dom • Mobile - React Native • Gl shaders - gl-react • Canvas - react-canvas • Terminal - react-blessed

Page 55: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

react-blessed (terminal)

Page 56: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Setup

Page 57: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

¿Assetic?

Setup recomendado

Page 58: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Setup recomendado

Page 59: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

WebpackPros

Page 60: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Webpack

• Gestiona dependencias por nosotros.Pros

Page 61: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Webpack

• Gestiona dependencias por nosotros.• Permite varios entornos: producción, desarrollo, ….

Pros

Page 62: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Webpack

• Gestiona dependencias por nosotros.• Permite varios entornos: producción, desarrollo, ….• Recarga automática de página (e incluso hot reload).

Pros

Page 63: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Webpack

• Gestiona dependencias por nosotros.• Permite varios entornos: producción, desarrollo, ….• Recarga automática de página (e incluso hot reload).• Uso de preprocesadores/“transpiladores”, como Babel.

Pros

Page 64: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Webpack

• Gestiona dependencias por nosotros.• Permite varios entornos: producción, desarrollo, ….• Recarga automática de página (e incluso hot reload).• Uso de preprocesadores/“transpiladores”, como Babel.• Los programadores de frontend no nos arquearán la ceja.

Pros

Page 65: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Webpack

• Gestiona dependencias por nosotros.• Permite varios entornos: producción, desarrollo, ….• Recarga automática de página (e incluso hot reload).• Uso de preprocesadores/“transpiladores”, como Babel.• Los programadores de frontend no nos arquearán la ceja.

Pros

Contras

Page 66: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Webpack

• Gestiona dependencias por nosotros.• Permite varios entornos: producción, desarrollo, ….• Recarga automática de página (e incluso hot reload).• Uso de preprocesadores/“transpiladores”, como Babel.• Los programadores de frontend no nos arquearán la ceja.

Pros

Contras• Tiene su curva de aprendizaje.

Page 67: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Webpack

• Gestiona dependencias por nosotros.• Permite varios entornos: producción, desarrollo, ….• Recarga automática de página (e incluso hot reload).• Uso de preprocesadores/“transpiladores”, como Babel.• Los programadores de frontend no nos arquearán la ceja.

Pros

Contras• Tiene su curva de aprendizaje.

Ejemplo completo: https://github.com/Limenius/symfony-react-sandbox

Page 68: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Inserción

<div id="react-placeholder"></div>

import ReactDOM from 'react-dom';

ReactDOM.render( <Counter name="amigo">, document.getElementById('react-placeholder'));

HTML

JavaScript

Page 69: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Integración con Symfony https://github.com/Limenius/ReactBundle

Page 70: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

https://github.com/shakacode/react_on_rails

Basado en

Page 71: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

ReactBundle

{{ react_component('RecipesApp', {'props': props}) }}

import ReactOnRails from 'react-on-rails';import RecipesApp from './RecipesAppServer';

ReactOnRails.register({ RecipesApp });

Twig:

JavaScript:

Page 72: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

ReactBundle

{{ react_component('RecipesApp', {'props': props}) }}

import ReactOnRails from 'react-on-rails';import RecipesApp from './RecipesAppServer';

ReactOnRails.register({ RecipesApp });

Twig:

JavaScript:

Page 73: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

ReactBundle

{{ react_component('RecipesApp', {'props': props}) }}

import ReactOnRails from 'react-on-rails';import RecipesApp from './RecipesAppServer';

ReactOnRails.register({ RecipesApp });

Twig:

JavaScript:

Page 74: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

ReactBundle

{{ react_component('RecipesApp', {'props': props}) }}

import ReactOnRails from 'react-on-rails';import RecipesApp from './RecipesAppServer';

ReactOnRails.register({ RecipesApp });

Twig:

JavaScript:

<div class="js-react-on-rails-component" style="display:none" data-component-name=“RecipesApp” data-props=“[mi Array en JSON]" data-trace=“false" data-dom-id=“sfreact-57d05640f2f1a”></div>

HTML generado:

Page 75: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Aplicaciones universales

Page 76: Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Page 77: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Dame un estado y una función render() que dependa de él, y olvídate de cómo y cuándo pinto.

La premisa fundamental

Page 78: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Dame un estado y una función render() que dependa de él, y olvídate de cómo y cuándo pinto.

La premisa fundamental

Podemos renderizar componentes desde el servidor.

Page 79: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Dame un estado y una función render() que dependa de él, y olvídate de cómo y cuándo pinto.

La premisa fundamental

Podemos renderizar componentes desde el servidor.

• SEO friendly.

Page 80: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Dame un estado y una función render() que dependa de él, y olvídate de cómo y cuándo pinto.

La premisa fundamental

Podemos renderizar componentes desde el servidor.

• SEO friendly.• Carga de página más rápida.

Page 81: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Dame un estado y una función render() que dependa de él, y olvídate de cómo y cuándo pinto.

La premisa fundamental

Podemos renderizar componentes desde el servidor.

• SEO friendly.• Carga de página más rápida.• Podemos cachear.

Page 82: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Client-side

{{ react_component('RecipesApp', {'props': props, 'rendering': 'client-side'}}) }}

TWIG

Page 83: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Client-side

{{ react_component('RecipesApp', {'props': props, 'rendering': 'client-side'}}) }}

TWIG

Page 84: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Client-side

{{ react_component('RecipesApp', {'props': props, 'rendering': 'client-side'}}) }}

TWIG

<div class="js-react-on-rails-component" style="display:none" data-component-name=“RecipesApp” data-props=“…” data-dom-id=“sfreact-57d05640f2f1a”></div>

HTML que devuelve el servidor

Page 85: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Client-side

{{ react_component('RecipesApp', {'props': props, 'rendering': 'client-side'}}) }}

TWIG

<div class="js-react-on-rails-component" style="display:none" data-component-name=“RecipesApp” data-props=“…” data-dom-id=“sfreact-57d05640f2f1a”></div>

HTML que devuelve el servidor

Generado en el navegador<div id="sfreact-57d05640f2f1a"><div data-reactroot="" data-reactid="1" data-react-checksum="2107256409"><ol class="breadcrumb" data-reactid="2"><li class="active" data-reactid=“3">Recipes</li>……</div>

Page 86: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Client-side y server-side

{{ react_component('RecipesApp', {'props': props, 'rendering': 'both'}}) }}

TWIG

Page 87: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Client-side y server-side

{{ react_component('RecipesApp', {'props': props, 'rendering': 'both'}}) }}

TWIG

Page 88: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Client-side y server-side

{{ react_component('RecipesApp', {'props': props, 'rendering': 'both'}}) }}

TWIG

HTML que devuelve el servidor<div id="sfreact-57d05640f2f1a"><div data-reactroot="" data-reactid="1" data-react-checksum="2107256409"><ol class="breadcrumb" data-reactid="2"><li class="active" data-reactid=“3">Recipes</li>……</div>

Page 89: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Client-side y server-side

{{ react_component('RecipesApp', {'props': props, 'rendering': 'both'}}) }}

TWIG

HTML que devuelve el servidor<div id="sfreact-57d05640f2f1a"><div data-reactroot="" data-reactid="1" data-react-checksum="2107256409"><ol class="breadcrumb" data-reactid="2"><li class="active" data-reactid=“3">Recipes</li>……</div>

Y React en el navegador toma el control al evaluar el código

Page 90: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Aplicaciones universales: Opciones

Page 91: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Opción 1: llamar a subproceso node.jsLlamamos a node.js con el componente Process de Symfony

* Cómodo (si tenemos node.js instalado).

* Lento.

Librería: https://github.com/nacmartin/phpexecjs

Page 92: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Opción 2: v8jsUsamos la extensión de PHP v8js

* Cómodo (aunque puede que haya que compilar la extensión y v8).

* Lento por ahora, potencialmente podríamos tener v8 precargada usando php-pm.

Librería: https://github.com/nacmartin/phpexecjs

Page 93: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Configuración Opciones 1 y 2

limenius_react: serverside_rendering: mode: "phpexecjs"

phpexecjs detecta si tenemos la extensión v8js, y si no llama a node.js

config.yml:

Page 94: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Opción 3: Servidor externoTenemos un servidor node.js “tonto” que nos renderiza React.

Es un servidor de <100 líneas, que es independiente de nuestra lógica.

* “Incómodo” (hay que mantener el servidor node.js corriendo, que tampoco es para morirse).

* Rápido.

Ver ejemplo en https://github.com/Limenius/symfony-react-sandbox

Page 95: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Configuración Opción 3

limenius_react: serverside_rendering: mode: “external” server-socket-path: “../tal_y_tal/node.sock”

config.yml:

Page 96: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Lo mejor de los dos mundosEn desarrollo usar llamada a node.js o v8js con phpexecjs.

En producción tener un servidor externo.

Si podemos cachear, menos problema.

Page 97: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Es decir:

limenius_react: serverside_rendering: mode: “external” server-socket-path: “../tal_y_tal/node.sock”

config.yml:

limenius_react: serverside_rendering: mode: "phpexecjs"

config_dev.yml:

Page 98: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

¿Vale la pena una app universal?

Page 99: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

¿Vale la pena una app universal?

En ocasiones sí, pero introduce complejidad.

Page 100: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Soporte para Redux (+brevísima introducción a Redux)

Page 101: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Redux: una cuestión de estado

guardar

Tu nombre: Juan

Hola, Juan

Cosas de Juan:

Page 102: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Redux: una cuestión de estado

guardar

Tu nombre: Juan

Hola, Juan

Cosas de Juan:

Page 103: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Redux: una cuestión de estado

guardar

Tu nombre: Juan

Hola, Juan

Cosas de Juan:

state.name

callback para cambiarlo

Page 104: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

dispatch(changeName(‘Juan'));

Componente

Page 105: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

dispatch(changeName(‘Juan'));

Componente

changeName = (name) => { return { type: ‘CHANGE_NAME', name }}

Action

Page 106: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

dispatch(changeName(‘Juan'));

Componente

changeName = (name) => { return { type: ‘CHANGE_NAME', name }}

Action

const todo = (state = {name: null}, action) => { switch (action.type) { case 'CHANGE_USER': return { name: action.name } }}

Reducer

Page 107: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

dispatch(changeName(‘Juan'));

Componente

changeName = (name) => { return { type: ‘CHANGE_NAME', name }}

Action

const todo = (state = {name: null}, action) => { switch (action.type) { case 'CHANGE_USER': return { name: action.name } }}

Reducer

Store

Page 108: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

this.props.name == ‘Juan';dispatch(changeName(‘Juan'));

Componente

changeName = (name) => { return { type: ‘CHANGE_NAME', name }}

Action

const todo = (state = {name: null}, action) => { switch (action.type) { case 'CHANGE_USER': return { name: action.name } }}

Reducer

Store

Page 109: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Redux en ReactBundle

Ver ejemplo en https://github.com/Limenius/symfony-react-sandbox

import ReactOnRails from 'react-on-rails';import RecipesApp from './RecipesAppClient';import recipesStore from '../store/recipesStore';

ReactOnRails.registerStore({recipesStore})ReactOnRails.register({ RecipesApp });

Twig:

JavaScript:

{{ redux_store('recipesStore', props) }}{{ react_component('RecipesApp') }}

Page 110: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Redux en ReactBundle

Ver ejemplo en https://github.com/Limenius/symfony-react-sandbox

import ReactOnRails from 'react-on-rails';import RecipesApp from './RecipesAppClient';import recipesStore from '../store/recipesStore';

ReactOnRails.registerStore({recipesStore})ReactOnRails.register({ RecipesApp });

Twig:

JavaScript:

{{ redux_store('recipesStore', props) }}{{ react_component('RecipesApp') }}{{ react_component('OtroComponente') }}

Page 111: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Compartir store entre componentes

Page 112: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Compartir store entre componentesReact

ReactReact

Twig

Twig

React

Al compartir store comparten estado

Twig

Page 113: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Formularios, un caso especial

Page 114: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Formularios muy dinámicos•Dentro de aplicaciones React.

•Formularios importantísimos en los que un detalle de usabilidad vale mucho dinero.

•Formularios muy específicos.

•Formularios muy dinámicos que no cansen (ver typeform por ejemplo).

Page 115: Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Page 116: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

El problema

Page 117: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Supongamos un form así

public function buildForm(FormBuilderInterface $builder, array $options){ $builder ->add('country', ChoiceType::class, [ 'choices' => [ 'España' => 'es', 'Portugal' => 'pt', ] ]) ->add('addresses', CollectionType::class, ...);};

Page 118: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Forms en HTML

$form->createView();

state.usuario

Page 119: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Forms en HTML

$form->createView();

state.usuario

Page 120: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Forms en HTML

$form->createView();

submit

país: España

EspañaPortugal

direcciones:

C\ tal-+state.usuario

Page 121: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Forms en HTML

$form->createView();

submit

país: España

EspañaPortugal

direcciones:

C\ tal-+state.usuario

Page 122: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Forms en HTML

$form->createView();

submit

país: España

EspañaPortugal

direcciones:

C\ tal-+state.usuario

POST bien formadito con country:’es’

y no ‘España’, ‘espana', ‘spain', ‘0’…

Page 123: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Forms en HTML

$form->createView();

$form->submit($request);

submit

país: España

EspañaPortugal

direcciones:

C\ tal-+state.usuario

POST bien formadito con country:’es’

y no ‘España’, ‘espana', ‘spain', ‘0’…

Page 124: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Forms en API

$form;

state.usuario

Page 125: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Forms en API

$form;

submit

país: España

EspañaPortugal

direcciones:

C\ tal-+state.usuario

Page 126: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Forms en API

$form;

submit

país: España

EspañaPortugal

direcciones:

C\ tal-+state.usuario

✘¿Cómo sabemos los campos,

o los choices? ¡A documentar! :(

Page 127: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Forms en API

$form;

$form->submit($request);

submit

país: España

EspañaPortugal

direcciones:

C\ tal-+state.usuario

POST “voy a tener suerte”

✘¿Cómo sabemos los campos,

o los choices? ¡A documentar! :(

Page 128: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Forms en API

$form;

$form->submit($request);

submit

país: España

EspañaPortugal

direcciones:

C\ tal-+state.usuario

POST “voy a tener suerte”

✘¿Cómo sabemos los campos,

o los choices? ¡A documentar! :(

This form should not contain extra fields!!1

Page 129: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Forms en API

$form;

$form->submit($request);

submit

país: España

EspañaPortugal

direcciones:

C\ tal-+state.usuario

POST “voy a tener suerte”

✘¿Cómo sabemos los campos,

o los choices? ¡A documentar! :(

This form should not contain extra fields!!1The value you selected is not a valid choice!!

Page 130: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Forms en API

$form;

$form->submit($request);

submit

país: España

EspañaPortugal

direcciones:

C\ tal-+state.usuario

POST “voy a tener suerte”

✘¿Cómo sabemos los campos,

o los choices? ¡A documentar! :(

This form should not contain extra fields!!1The value you selected is not a valid choice!!One or more of the given values is invalid!! :D

Page 131: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Forms en API

$form;

$form->submit($request);

submit

país: España

EspañaPortugal

direcciones:

C\ tal-+state.usuario

POST “voy a tener suerte”

✘¿Cómo sabemos los campos,

o los choices? ¡A documentar! :(

This form should not contain extra fields!!1The value you selected is not a valid choice!!One or more of the given values is invalid!! :DMUHAHAHAHAHA!!!!!

Page 132: Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Page 133: Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Page 134: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Definir (y mantener) por triplicado

Form SF API docs Form Cliente

:(

¿Cuántos programadores hacen falta para hacer un formulario?

Page 135: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Caso: Wizard complejo

Page 136: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Caso: Wizard complejo

Page 137: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Caso: Wizard complejo

Page 138: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Lo que necesitamos

$form->createView();

HTML

API$miTransformador->transform($form);

Page 139: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Lo que necesitamos

$form->createView();

HTML

¡Serializar! Vale, ¿A qué formato?

API$miTransformador->transform($form);

Page 140: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

JSON Schema

Page 141: Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Page 142: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Qué pinta tiene{ "$schema": "http://json-schema.org/draft-04/schema#", "title": "Product", "description": "A product from Acme's catalog", "type": "object", "properties": { "name": { "description": "Name of the product", "type": "string" }, "price": { "type": "number", "minimum": 0, "exclusiveMinimum": true }, "tags": { "type": "array", "items": { "type": "string" }, "minItems": 1, "uniqueItems": true } }, "required": ["id", "name", "price"]}

definiciones, tipos, reglas de validación :)

Nuevo recurso: mi-api/products/form

Page 143: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

A partir del schema generamos form

Page 144: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Generadores client-side

• jdorn/json-editor: no React, es un veterano.

• mozilla/react-jsonschema-form: React. creado por un antiguo Symfonero (Niko Perriault).

• limenius/liform-react: React + redux; integrado con redux-form (I ♥ redux-form)

• …

• Crear el nuestro puede ser conveniente.

Page 145: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Generadores client-side: diferenciasCada uno amplía json-schema a su manera para

especificar detalles UI: Orden de campos, qué widget específico usar, etc.

Si queremos usarlos al máximo hay que generar un json-schema específico para cada uno

(no son totalmente intercambiables).

Ejemplo: usar editor Wysiwyg en un campo texto

Page 146: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Ejemplo: LiformBundle y liform-react

limenius/LiformBundle: Genera json-schema a partir de formularios Symfony.

limenius/liform-react: Generador de formularios React (con redux-form) a partir de json-schema.

Son Work in progress

Page 147: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Cómo serializar: resolvers + transformers

$transformer = $resolver->resolve($form);

$jsonSchema = $transformer->transform($form);

Ejemplo de esta técnica: https://github.com/Limenius/LiformBundle

Page 148: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Resolver

public function resolve(FormInterface $form){ $types = FormUtil::typeAncestry($form);

foreach ($types as $type) { if (isset($this->transformers[$type])) { return $this->transformers[$type]; } }}

Misión: Encuentra el transformer apropiado para el form

Page 149: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

TransformerMisión: Inspecciona el form y crea un array.

Si es compuesto resuelve+transforma los hijos.class IntegerTransformer extends AbstractTransformer{ public function transform(FormInterface $form) { $schema = [ 'type' => 'integer', ]; if ($liform = $form->getConfig()->getOption('liform')) { if ($format = $liform['format']) { $schema['format'] = $format; } } $this->addCommonSpecs($form, $schema);

return $schema; }} protected function addLabel($form, &$schema) { if ($label = $form->getConfig()->getOption('label')) { $schema['title'] = $label; } }

Page 150: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

TransformerRecopila información de cada Form Field.

Podemos sacar mucha información: •Valores por defecto & placeholders. •Atributos del formulario. •Validadores.

Page 151: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Ejemplo: validación ‘pattern’

protected function addPattern($form, &$schema){ if ($attr = $form->getConfig()->getOption('attr')) { if (isset($attr['pattern'])) { $schema['pattern'] = $attr['pattern']; } }}

Page 152: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Esta técnica vale también para Angular, Backbone, mobile…

Page 153: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Repaso:

Page 154: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Repaso:• Qué es React • Setup • Apps universales (ReactBundle) • Para qué sirve Redux • El problema de los formularios • Cómo aliviarlo con JSON Schema

Page 155: Integrando React.js en aplicaciones Symfony (deSymfony 2016)

MADRID · NOV 27-28 · 2015

¡Gracias!@nacmartin

[email protected]

http://limenius.com Formación, consultoría

y desarrollo de proyectos