SlideShare a Scribd company logo
1 of 155
Download to read offline
deSymfony 16-17 septiembre 2016
Madrid
INTEGRANDO REACT.JS
EN APLICACIONES
SYMFONY
Nacho Martín
deSymfony
¡Muchas gracias a nuestros
patrocinadores!
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
Objetivo:
Mostrar cosas que nos
encontramos al usar React desde
Symfony, en tierra de nadie, y que
ninguno de los dos va a
documentar.
¿A mí qué me importa el
frontend?
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
¿Qué es React.js?
Echar huevos en sartén
La premisa fundamental
Cómo hacer una tortilla
Comprar huevos
Romper huevos
Echar huevos en sartén
Batir huevos
La premisa fundamental
Cómo hacer una tortilla
Comprar huevos
Romper huevos
Opciones:
La premisa fundamental
Opciones:
La premisa fundamental
1: Repintamos todo.
Opciones:
La premisa fundamental
1: Repintamos todo. Simple
Opciones:
La premisa fundamental
1: Repintamos todo. Simple Poco eficiente
Opciones:
2: Buscamos en el DOM
dónde insertar, qué
mover, qué eliminar.
La premisa fundamental
1: Repintamos todo. Simple Poco eficiente
Opciones:
2: Buscamos en el DOM
dónde insertar, qué
mover, qué eliminar.
La premisa fundamental
1: Repintamos todo. Simple
Complejo
Poco eficiente
Opciones:
2: Buscamos en el DOM
dónde insertar, qué
mover, qué eliminar.
La premisa fundamental
1: Repintamos todo. Simple
Muy eficienteComplejo
Poco eficiente
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
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
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.
¡Clícame! Clicks: 0
Nuestro primer componente
¡Clícame! Clicks: 1
Nuestro primer componente
¡Clícame!
Nuestro primer componente
import 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;
Nuestro primer componente
import 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)
Nuestro primer componente
import 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
Nuestro primer componente
import 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
Nuestro primer componente
import 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
Trabajar con el estado
Trabajar con el estado
constructor(props) {
super(props);
this.state = {count: 1};
}
Estado inicial
Trabajar con el estado
constructor(props) {
super(props);
this.state = {count: 1};
}
Estado inicial
this.setState({count: this.state.count + 1});
Asignar estado
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
render() y JSX
render() {
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.
render() y JSX
render() {
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.
render() y JSX
render() {
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.
Thinking in React
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Clícame!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
Thinking in React
render() {
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
Thinking in React
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Clícame!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
Aquí no Ajax
Thinking in React
render() {
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
Importante: pensar la jerarquía
Importante: pensar la jerarquía
Jerarquía de componentes: props
class CounterGroup extends Component {
render() {
return (
<div>
<Counter name="amigo"/>
<Counter name="señor"/>
</div>
);
}
}
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: props
class CounterGroup extends Component {
render() {
return (
<div>
<Counter name="amigo"/>
<Counter name="señor"/>
</div>
);
}
}
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: props
class CounterGroup extends Component {
render() {
return (
<div>
<Counter name="amigo"/>
<Counter name="señor"/>
</div>
);
}
}
Pro tip: componentes sin estado
const Saludador = (props) => {
<div>
<div>Hola {props.name}</div>
</div>
}
Todo depende del estado, por tanto:
Todo depende del estado, por tanto:
•Podemos reproducir estados,
Todo depende del estado, por tanto:
•Podemos reproducir estados,
•rebobinar,
Todo depende del estado, por tanto:
•Podemos reproducir estados,
•rebobinar,
•loguear cambios de estado,
Todo depende del estado, por tanto:
•Podemos reproducir estados,
•rebobinar,
•loguear cambios de estado,
•hacer álbum de estilo
Todo depende del estado, por tanto:
•Podemos reproducir estados,
•rebobinar,
•loguear cambios de estado,
•hacer álbum de estilo
•…
Learn once,
write everywhere
¿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>
);
}
…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>
);
}
…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
React Targets
•Web - react-dom
•Mobile - React Native
•Gl shaders - gl-react
•Canvas - react-canvas
•Terminal - react-blessed
react-blessed (terminal)
Setup
¿Assetic?
Setup recomendado
Setup recomendado
Webpack
Pros
Webpack
• Gestiona dependencias por nosotros.
Pros
Webpack
• Gestiona dependencias por nosotros.
• Permite varios entornos: producción, desarrollo, ….
Pros
Webpack
• Gestiona dependencias por nosotros.
• Permite varios entornos: producción, desarrollo, ….
• Recarga automática de página (e incluso hot reload).
Pros
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
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
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
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.
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
Inserción
<div id="react-placeholder"></div>
import ReactDOM from 'react-dom';
ReactDOM.render(
<Counter name="amigo">,
document.getElementById('react-placeholder')
);
HTML
JavaScript
Integración con Symfony
https://github.com/Limenius/ReactBundle
https://github.com/shakacode/react_on_rails
Basado en
ReactBundle
{{ react_component('RecipesApp', {'props': props}) }}
import ReactOnRails from 'react-on-rails';
import RecipesApp from './RecipesAppServer';
ReactOnRails.register({ RecipesApp });
Twig:
JavaScript:
ReactBundle
{{ react_component('RecipesApp', {'props': props}) }}
import ReactOnRails from 'react-on-rails';
import RecipesApp from './RecipesAppServer';
ReactOnRails.register({ RecipesApp });
Twig:
JavaScript:
ReactBundle
{{ react_component('RecipesApp', {'props': props}) }}
import ReactOnRails from 'react-on-rails';
import RecipesApp from './RecipesAppServer';
ReactOnRails.register({ RecipesApp });
Twig:
JavaScript:
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:
Aplicaciones universales
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
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.
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.
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.
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.
Client-side
{{ react_component('RecipesApp', {'props': props,
'rendering': 'client-side'}}) }}
TWIG
Client-side
{{ react_component('RecipesApp', {'props': props,
'rendering': 'client-side'}}) }}
TWIG
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
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>
Client-side y server-side
{{ react_component('RecipesApp', {'props': props,
'rendering': 'both'}}) }}
TWIG
Client-side y server-side
{{ react_component('RecipesApp', {'props': props,
'rendering': 'both'}}) }}
TWIG
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>
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
Aplicaciones universales: Opciones
Opción 1: llamar a subproceso node.js
Llamamos a node.js con el componente Process de
Symfony
* Cómodo (si tenemos node.js instalado).
* Lento.
Librería: https://github.com/nacmartin/phpexecjs
Opción 2: v8js
Usamos 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
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:
Opción 3: Servidor externo
Tenemos 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
Configuración Opción 3
limenius_react:
serverside_rendering:
mode: “external”
server-socket-path: “../tal_y_tal/node.sock”
config.yml:
Lo mejor de los dos mundos
En desarrollo usar llamada a node.js o v8js con
phpexecjs.
En producción tener un servidor externo.
Si podemos cachear, menos problema.
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:
¿Vale la pena una app universal?
¿Vale la pena una app universal?
En ocasiones sí, pero introduce
complejidad.
Soporte para Redux
(+brevísima introducción a Redux)
Redux: una cuestión de estado
guardar
Tu nombre: Juan
Hola, Juan
Cosas de Juan:
Redux: una cuestión de estado
guardar
Tu nombre: Juan
Hola, Juan
Cosas de Juan:
Redux: una cuestión de estado
guardar
Tu nombre: Juan
Hola, Juan
Cosas de Juan:
state.name
callback para cambiarlo
dispatch(changeName(‘Juan'));
Componente
dispatch(changeName(‘Juan'));
Componente
changeName = (name) => {
return {
type: ‘CHANGE_NAME',
name
}
}
Action
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
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
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
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') }}
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') }}
Compartir store entre componentes
Compartir store entre componentes
React
React
React
Twig
Twig
React
Al compartir store comparten estado
Twig
Formularios, un caso especial
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).
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
El problema
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, ...);
};
Forms en HTML
$form->createView();
state.usuario
Forms en HTML
$form->createView();
state.usuario
Forms en HTML
$form->createView();
submit
país: España
España
Portugal
direcciones:
C tal-
+state.usuario
Forms en HTML
$form->createView();
submit
país: España
España
Portugal
direcciones:
C tal-
+state.usuario
Forms en HTML
$form->createView();
submit
país: España
España
Portugal
direcciones:
C tal-
+state.usuario
POST bien formadito
con country:’es’
y no ‘España’, ‘espana', ‘spain', ‘0’…
Forms en HTML
$form->createView();
$form->submit($request);
submit
país: España
España
Portugal
direcciones:
C tal-
+state.usuario
POST bien formadito
con country:’es’
y no ‘España’, ‘espana', ‘spain', ‘0’…
Forms en API
$form;
state.usuario
Forms en API
$form;
submit
país: España
España
Portugal
direcciones:
C tal-
+state.usuario
✘
Forms en API
$form;
submit
país: España
España
Portugal
direcciones:
C tal-
+state.usuario
✘
¿Cómo sabemos los campos,
o los choices?
¡A documentar! :(
Forms en API
$form;
$form->submit($request);
submit
país: España
España
Portugal
direcciones:
C tal-
+state.usuario
POST “voy a tener suerte”
✘
¿Cómo sabemos los campos,
o los choices?
¡A documentar! :(
Forms en API
$form;
$form->submit($request);
submit
país: España
España
Portugal
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
Forms en API
$form;
$form->submit($request);
submit
país: España
España
Portugal
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
The value you selected is not a valid choice!!
Forms en API
$form;
$form->submit($request);
submit
país: España
España
Portugal
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
The value you selected is not a valid choice!!One or more of the given values is invalid!! :D
Forms en API
$form;
$form->submit($request);
submit
país: España
España
Portugal
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
The value you selected is not a valid choice!!One or more of the given values is invalid!! :DMUHAHAHAHAHA!!!!!
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
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?
Caso: Wizard complejo
Caso: Wizard complejo
Caso: Wizard complejo
Lo que necesitamos
$form->createView();
HTML
API
$miTransformador->transform($form);
Lo que necesitamos
$form->createView();
HTML
¡Serializar! Vale, ¿A qué formato?
API
$miTransformador->transform($form);
JSON Schema
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
A partir del schema generamos form
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.
Generadores client-side: diferencias
Cada 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
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
Cómo serializar: resolvers + transformers
$transformer = $resolver->resolve($form);
$jsonSchema = $transformer->transform($form);
Ejemplo de esta técnica: https://github.com/Limenius/LiformBundle
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
Transformer
Misió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;
}
}
Transformer
Recopila información de cada Form Field.
Podemos sacar mucha información:
•Valores por defecto & placeholders.
•Atributos del formulario.
•Validadores.
Ejemplo: validación ‘pattern’
protected function addPattern($form, &$schema)
{
if ($attr = $form->getConfig()->getOption('attr')) {
if (isset($attr['pattern'])) {
$schema['pattern'] = $attr['pattern'];
}
}
}
Esta técnica vale también
para Angular, Backbone,
mobile…
Repaso:
Repaso:
• Qué es React
• Setup
• Apps universales (ReactBundle)
• Para qué sirve Redux
• El problema de los formularios
• Cómo aliviarlo con JSON Schema
MADRID · NOV 27-28 · 2015
¡Gracias!
@nacmartin
nacho@limenius.com
http://limenius.com
Formación, consultoría
y desarrollo de proyectos

More Related Content

What's hot

Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js + Expres...
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js +  Expres...Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js +  Expres...
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js + Expres...Edureka!
 
도메인 주도 설계의 본질
도메인 주도 설계의 본질도메인 주도 설계의 본질
도메인 주도 설계의 본질Young-Ho Cho
 
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...Edureka!
 
컴포넌트 관점에서 개발하기
컴포넌트 관점에서 개발하기컴포넌트 관점에서 개발하기
컴포넌트 관점에서 개발하기우영 주
 
Коды, исправляющие ошибки. Простейшие границы. Коды Варшамова—Тененгольца.
Коды, исправляющие ошибки. Простейшие границы. Коды Варшамова—Тененгольца.Коды, исправляющие ошибки. Простейшие границы. Коды Варшамова—Тененгольца.
Коды, исправляющие ошибки. Простейшие границы. Коды Варшамова—Тененгольца.Alex Dainiak
 
Angular - Chapter 3 - Components
Angular - Chapter 3 - ComponentsAngular - Chapter 3 - Components
Angular - Chapter 3 - ComponentsWebStackAcademy
 
Node JS Crash Course
Node JS Crash CourseNode JS Crash Course
Node JS Crash CourseHaim Michael
 
Angular 2.0 forms
Angular 2.0 formsAngular 2.0 forms
Angular 2.0 formsEyal Vardi
 
Server side rendering with React and Symfony
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and SymfonyIgnacio Martín
 
Aprendendo Angular com a CLI
Aprendendo Angular com a CLIAprendendo Angular com a CLI
Aprendendo Angular com a CLIVanessa Me Tonini
 
Spring Meetup Paris - Back to the basics of Spring (Boot)
Spring Meetup Paris - Back to the basics of Spring (Boot)Spring Meetup Paris - Back to the basics of Spring (Boot)
Spring Meetup Paris - Back to the basics of Spring (Boot)Eric SIBER
 
[115]14일만에 깃헙 스타 1,000개 받은 차트 오픈소스 개발기
[115]14일만에 깃헙 스타 1,000개 받은 차트 오픈소스 개발기[115]14일만에 깃헙 스타 1,000개 받은 차트 오픈소스 개발기
[115]14일만에 깃헙 스타 1,000개 받은 차트 오픈소스 개발기NAVER D2
 

What's hot (20)

DDD 준비 서문래
DDD 준비 서문래DDD 준비 서문래
DDD 준비 서문래
 
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js + Expres...
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js +  Expres...Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js +  Expres...
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js + Expres...
 
도메인 주도 설계의 본질
도메인 주도 설계의 본질도메인 주도 설계의 본질
도메인 주도 설계의 본질
 
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
 
spring-boot-fr.pdf
spring-boot-fr.pdfspring-boot-fr.pdf
spring-boot-fr.pdf
 
Overview of PHP and MYSQL
Overview of PHP and MYSQLOverview of PHP and MYSQL
Overview of PHP and MYSQL
 
컴포넌트 관점에서 개발하기
컴포넌트 관점에서 개발하기컴포넌트 관점에서 개발하기
컴포넌트 관점에서 개발하기
 
Коды, исправляющие ошибки. Простейшие границы. Коды Варшамова—Тененгольца.
Коды, исправляющие ошибки. Простейшие границы. Коды Варшамова—Тененгольца.Коды, исправляющие ошибки. Простейшие границы. Коды Варшамова—Тененгольца.
Коды, исправляющие ошибки. Простейшие границы. Коды Варшамова—Тененгольца.
 
Angular - Chapter 3 - Components
Angular - Chapter 3 - ComponentsAngular - Chapter 3 - Components
Angular - Chapter 3 - Components
 
Node JS Crash Course
Node JS Crash CourseNode JS Crash Course
Node JS Crash Course
 
Angular 2.0 forms
Angular 2.0 formsAngular 2.0 forms
Angular 2.0 forms
 
Server side rendering with React and Symfony
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and Symfony
 
Introdução CSS
Introdução CSSIntrodução CSS
Introdução CSS
 
Express node js
Express node jsExpress node js
Express node js
 
Angular modules in depth
Angular modules in depthAngular modules in depth
Angular modules in depth
 
Aprendendo Angular com a CLI
Aprendendo Angular com a CLIAprendendo Angular com a CLI
Aprendendo Angular com a CLI
 
Spring Meetup Paris - Back to the basics of Spring (Boot)
Spring Meetup Paris - Back to the basics of Spring (Boot)Spring Meetup Paris - Back to the basics of Spring (Boot)
Spring Meetup Paris - Back to the basics of Spring (Boot)
 
Slides Css3
Slides Css3 Slides Css3
Slides Css3
 
[115]14일만에 깃헙 스타 1,000개 받은 차트 오픈소스 개발기
[115]14일만에 깃헙 스타 1,000개 받은 차트 오픈소스 개발기[115]14일만에 깃헙 스타 1,000개 받은 차트 오픈소스 개발기
[115]14일만에 깃헙 스타 1,000개 받은 차트 오픈소스 개발기
 
Introdução APIs RESTful
Introdução APIs RESTfulIntrodução APIs RESTful
Introdução APIs RESTful
 

Viewers also liked

Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...Ryan Weaver
 
Refactorizando Pccomponentes.com con Symfony
Refactorizando Pccomponentes.com con SymfonyRefactorizando Pccomponentes.com con Symfony
Refactorizando Pccomponentes.com con SymfonyMario Marín
 
Integrating React.js Into a PHP Application
Integrating React.js Into a PHP ApplicationIntegrating React.js Into a PHP Application
Integrating React.js Into a PHP ApplicationAndrew Rota
 
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreSymfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreRyan Weaver
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDAleix Vergés
 
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...Alexander Lisachenko
 
When cqrs meets event sourcing
When cqrs meets event sourcingWhen cqrs meets event sourcing
When cqrs meets event sourcingManel Sellés
 
Desarrollo código mantenible en WordPress utilizando Symfony
Desarrollo código mantenible en WordPress utilizando SymfonyDesarrollo código mantenible en WordPress utilizando Symfony
Desarrollo código mantenible en WordPress utilizando SymfonyAsier Marqués
 
Multi kernelowa aplikacja w oparciu o Symfony 3 i microkernele
Multi kernelowa aplikacja w oparciu o Symfony 3 i microkerneleMulti kernelowa aplikacja w oparciu o Symfony 3 i microkernele
Multi kernelowa aplikacja w oparciu o Symfony 3 i microkerneleRadek Baczynski
 
Introducción al ecosistema de React.js
Introducción al ecosistema de React.jsIntroducción al ecosistema de React.js
Introducción al ecosistema de React.jsJoaquín Salvachúa
 
Getting your open source company to contribution
Getting your open source company to contributionGetting your open source company to contribution
Getting your open source company to contributionAsavin Wattanajantra
 
WordCamp Cantabria - Código mantenible con WordPress
WordCamp Cantabria  - Código mantenible con WordPressWordCamp Cantabria  - Código mantenible con WordPress
WordCamp Cantabria - Código mantenible con WordPressAsier Marqués
 
Nuvola: a tale of migration to AWS
Nuvola: a tale of migration to AWSNuvola: a tale of migration to AWS
Nuvola: a tale of migration to AWSMatteo Moretti
 
How Symfony Changed My Life
How Symfony Changed My LifeHow Symfony Changed My Life
How Symfony Changed My LifeMatthias Noback
 
You Got React.js in My PHP
You Got React.js in My PHPYou Got React.js in My PHP
You Got React.js in My PHPTaylor Lovett
 

Viewers also liked (20)

Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
 
Refactorizando Pccomponentes.com con Symfony
Refactorizando Pccomponentes.com con SymfonyRefactorizando Pccomponentes.com con Symfony
Refactorizando Pccomponentes.com con Symfony
 
Integrating React.js Into a PHP Application
Integrating React.js Into a PHP ApplicationIntegrating React.js Into a PHP Application
Integrating React.js Into a PHP Application
 
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreSymfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDD
 
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...
 
When cqrs meets event sourcing
When cqrs meets event sourcingWhen cqrs meets event sourcing
When cqrs meets event sourcing
 
Desarrollo código mantenible en WordPress utilizando Symfony
Desarrollo código mantenible en WordPress utilizando SymfonyDesarrollo código mantenible en WordPress utilizando Symfony
Desarrollo código mantenible en WordPress utilizando Symfony
 
Multi kernelowa aplikacja w oparciu o Symfony 3 i microkernele
Multi kernelowa aplikacja w oparciu o Symfony 3 i microkerneleMulti kernelowa aplikacja w oparciu o Symfony 3 i microkernele
Multi kernelowa aplikacja w oparciu o Symfony 3 i microkernele
 
Introducción al ecosistema de React.js
Introducción al ecosistema de React.jsIntroducción al ecosistema de React.js
Introducción al ecosistema de React.js
 
From * to Symfony2
From * to Symfony2From * to Symfony2
From * to Symfony2
 
SaaS con Symfony2
SaaS con Symfony2SaaS con Symfony2
SaaS con Symfony2
 
Scaling symfony apps
Scaling symfony appsScaling symfony apps
Scaling symfony apps
 
Symfony day 2016
Symfony day 2016Symfony day 2016
Symfony day 2016
 
Getting your open source company to contribution
Getting your open source company to contributionGetting your open source company to contribution
Getting your open source company to contribution
 
Doctrine2 sf2Vigo
Doctrine2 sf2VigoDoctrine2 sf2Vigo
Doctrine2 sf2Vigo
 
WordCamp Cantabria - Código mantenible con WordPress
WordCamp Cantabria  - Código mantenible con WordPressWordCamp Cantabria  - Código mantenible con WordPress
WordCamp Cantabria - Código mantenible con WordPress
 
Nuvola: a tale of migration to AWS
Nuvola: a tale of migration to AWSNuvola: a tale of migration to AWS
Nuvola: a tale of migration to AWS
 
How Symfony Changed My Life
How Symfony Changed My LifeHow Symfony Changed My Life
How Symfony Changed My Life
 
You Got React.js in My PHP
You Got React.js in My PHPYou Got React.js in My PHP
You Got React.js in My PHP
 

Similar to Integrando React.js en aplicaciones Symfony (deSymfony 2016)

Introducción a react + redux
Introducción a react + reduxIntroducción a react + redux
Introducción a react + reduxMiguel Mendoza
 
Android Bootcamp - GTUG Uruguay
Android Bootcamp - GTUG UruguayAndroid Bootcamp - GTUG Uruguay
Android Bootcamp - GTUG Uruguaygtuguruguay
 
Tutorial JPA Parte 1 : CRUD BASICO CON JPA Y SWING en NETBEANS
Tutorial  JPA Parte 1  : CRUD BASICO CON JPA Y SWING en NETBEANSTutorial  JPA Parte 1  : CRUD BASICO CON JPA Y SWING en NETBEANS
Tutorial JPA Parte 1 : CRUD BASICO CON JPA Y SWING en NETBEANSWilliam Diaz S
 
Tutorial Nro 1 de Desarrollo de Aplicaciones Móviles con Android
Tutorial Nro 1 de Desarrollo de Aplicaciones Móviles con AndroidTutorial Nro 1 de Desarrollo de Aplicaciones Móviles con Android
Tutorial Nro 1 de Desarrollo de Aplicaciones Móviles con AndroidLuis Ernesto Castillo Alfaro
 
Modulo 1 - Proteus
Modulo 1 - ProteusModulo 1 - Proteus
Modulo 1 - ProteusRaniellys
 
Introducción al desarrollo Web: Frontend con Angular 6
Introducción al desarrollo Web: Frontend con Angular 6Introducción al desarrollo Web: Frontend con Angular 6
Introducción al desarrollo Web: Frontend con Angular 6Gabriela Bosetti
 
Reactvolution
ReactvolutionReactvolution
Reactvolution_Lagash
 
Cross development - React para desarrolladores de asp.net
Cross development - React para desarrolladores de asp.netCross development - React para desarrolladores de asp.net
Cross development - React para desarrolladores de asp.netAlberto Diaz Martin
 
CrossDvlpu - REACT para desarrolladores de ASP.NET
CrossDvlpu - REACT para desarrolladores de ASP.NETCrossDvlpu - REACT para desarrolladores de ASP.NET
CrossDvlpu - REACT para desarrolladores de ASP.NETAlberto Diaz Martin
 
Buenas Prácticas de desarrollo en Ruby on Rails
Buenas Prácticas de desarrollo en Ruby on RailsBuenas Prácticas de desarrollo en Ruby on Rails
Buenas Prácticas de desarrollo en Ruby on RailsSergio Gil
 
Como crear un proyecto en visual Studio 2010
Como crear un proyecto en visual Studio 2010Como crear un proyecto en visual Studio 2010
Como crear un proyecto en visual Studio 2010a9788398
 

Similar to Integrando React.js en aplicaciones Symfony (deSymfony 2016) (20)

Introducción a react + redux
Introducción a react + reduxIntroducción a react + redux
Introducción a react + redux
 
React
ReactReact
React
 
Android Bootcamp - GTUG Uruguay
Android Bootcamp - GTUG UruguayAndroid Bootcamp - GTUG Uruguay
Android Bootcamp - GTUG Uruguay
 
Manual
ManualManual
Manual
 
Tutorial JPA Parte 1 : CRUD BASICO CON JPA Y SWING en NETBEANS
Tutorial  JPA Parte 1  : CRUD BASICO CON JPA Y SWING en NETBEANSTutorial  JPA Parte 1  : CRUD BASICO CON JPA Y SWING en NETBEANS
Tutorial JPA Parte 1 : CRUD BASICO CON JPA Y SWING en NETBEANS
 
Tutorial Nro 1 de Desarrollo de Aplicaciones Móviles con Android
Tutorial Nro 1 de Desarrollo de Aplicaciones Móviles con AndroidTutorial Nro 1 de Desarrollo de Aplicaciones Móviles con Android
Tutorial Nro 1 de Desarrollo de Aplicaciones Móviles con Android
 
Modulo 1 - Proteus
Modulo 1 - ProteusModulo 1 - Proteus
Modulo 1 - Proteus
 
Report
ReportReport
Report
 
Hi Fiber!
Hi Fiber!Hi Fiber!
Hi Fiber!
 
React redux
React redux React redux
React redux
 
Introducción al desarrollo Web: Frontend con Angular 6
Introducción al desarrollo Web: Frontend con Angular 6Introducción al desarrollo Web: Frontend con Angular 6
Introducción al desarrollo Web: Frontend con Angular 6
 
Android bootcamp 101 v2.0
Android bootcamp 101 v2.0Android bootcamp 101 v2.0
Android bootcamp 101 v2.0
 
Reactvolution
ReactvolutionReactvolution
Reactvolution
 
sERVIDORES
sERVIDORESsERVIDORES
sERVIDORES
 
Cross development - React para desarrolladores de asp.net
Cross development - React para desarrolladores de asp.netCross development - React para desarrolladores de asp.net
Cross development - React para desarrolladores de asp.net
 
CrossDvlpu - REACT para desarrolladores de ASP.NET
CrossDvlpu - REACT para desarrolladores de ASP.NETCrossDvlpu - REACT para desarrolladores de ASP.NET
CrossDvlpu - REACT para desarrolladores de ASP.NET
 
Reportes
ReportesReportes
Reportes
 
Programación i
Programación iProgramación i
Programación i
 
Buenas Prácticas de desarrollo en Ruby on Rails
Buenas Prácticas de desarrollo en Ruby on RailsBuenas Prácticas de desarrollo en Ruby on Rails
Buenas Prácticas de desarrollo en Ruby on Rails
 
Como crear un proyecto en visual Studio 2010
Como crear un proyecto en visual Studio 2010Como crear un proyecto en visual Studio 2010
Como crear un proyecto en visual Studio 2010
 

More from Ignacio Martín

Elixir/OTP for PHP developers
Elixir/OTP for PHP developersElixir/OTP for PHP developers
Elixir/OTP for PHP developersIgnacio Martín
 
Introduction to React Native Workshop
Introduction to React Native WorkshopIntroduction to React Native Workshop
Introduction to React Native WorkshopIgnacio Martín
 
Symfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusSymfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusIgnacio Martín
 
Server Side Rendering of JavaScript in PHP
Server Side Rendering of JavaScript in PHPServer Side Rendering of JavaScript in PHP
Server Side Rendering of JavaScript in PHPIgnacio Martín
 
Extending Redux in the Server Side
Extending Redux in the Server SideExtending Redux in the Server Side
Extending Redux in the Server SideIgnacio Martín
 
Redux Sagas - React Alicante
Redux Sagas - React AlicanteRedux Sagas - React Alicante
Redux Sagas - React AlicanteIgnacio Martín
 
React Native Workshop - React Alicante
React Native Workshop - React AlicanteReact Native Workshop - React Alicante
React Native Workshop - React AlicanteIgnacio Martín
 
Asegurando APIs en Symfony con JWT
Asegurando APIs en Symfony con JWTAsegurando APIs en Symfony con JWT
Asegurando APIs en Symfony con JWTIgnacio Martín
 
Redux saga: managing your side effects. Also: generators in es6
Redux saga: managing your side effects. Also: generators in es6Redux saga: managing your side effects. Also: generators in es6
Redux saga: managing your side effects. Also: generators in es6Ignacio Martín
 
Integrating React.js with PHP projects
Integrating React.js with PHP projectsIntegrating React.js with PHP projects
Integrating React.js with PHP projectsIgnacio Martín
 
Keeping the frontend under control with Symfony and Webpack
Keeping the frontend under control with Symfony and WebpackKeeping the frontend under control with Symfony and Webpack
Keeping the frontend under control with Symfony and WebpackIgnacio Martín
 
Adding Realtime to your Projects
Adding Realtime to your ProjectsAdding Realtime to your Projects
Adding Realtime to your ProjectsIgnacio Martín
 
Symfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worldsSymfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worldsIgnacio Martín
 

More from Ignacio Martín (16)

Elixir/OTP for PHP developers
Elixir/OTP for PHP developersElixir/OTP for PHP developers
Elixir/OTP for PHP developers
 
Introduction to React Native Workshop
Introduction to React Native WorkshopIntroduction to React Native Workshop
Introduction to React Native Workshop
 
Symfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusSymfony 4 Workshop - Limenius
Symfony 4 Workshop - Limenius
 
Server Side Rendering of JavaScript in PHP
Server Side Rendering of JavaScript in PHPServer Side Rendering of JavaScript in PHP
Server Side Rendering of JavaScript in PHP
 
Extending Redux in the Server Side
Extending Redux in the Server SideExtending Redux in the Server Side
Extending Redux in the Server Side
 
Redux Sagas - React Alicante
Redux Sagas - React AlicanteRedux Sagas - React Alicante
Redux Sagas - React Alicante
 
React Native Workshop - React Alicante
React Native Workshop - React AlicanteReact Native Workshop - React Alicante
React Native Workshop - React Alicante
 
Asegurando APIs en Symfony con JWT
Asegurando APIs en Symfony con JWTAsegurando APIs en Symfony con JWT
Asegurando APIs en Symfony con JWT
 
Redux saga: managing your side effects. Also: generators in es6
Redux saga: managing your side effects. Also: generators in es6Redux saga: managing your side effects. Also: generators in es6
Redux saga: managing your side effects. Also: generators in es6
 
Integrating React.js with PHP projects
Integrating React.js with PHP projectsIntegrating React.js with PHP projects
Integrating React.js with PHP projects
 
Introduction to Redux
Introduction to ReduxIntroduction to Redux
Introduction to Redux
 
Keeping the frontend under control with Symfony and Webpack
Keeping the frontend under control with Symfony and WebpackKeeping the frontend under control with Symfony and Webpack
Keeping the frontend under control with Symfony and Webpack
 
Adding Realtime to your Projects
Adding Realtime to your ProjectsAdding Realtime to your Projects
Adding Realtime to your Projects
 
Symfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worldsSymfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worlds
 
Symfony 2 CMF
Symfony 2 CMFSymfony 2 CMF
Symfony 2 CMF
 
Presentacion git
Presentacion gitPresentacion git
Presentacion git
 

Recently uploaded

Formato de práctica reflexiva ante una problemática social.docx.pdf
Formato de práctica reflexiva ante una problemática social.docx.pdfFormato de práctica reflexiva ante una problemática social.docx.pdf
Formato de práctica reflexiva ante una problemática social.docx.pdfjuanrubenc78
 
Virus -Josue Cabascango _20240322_194349_0000.pdf
Virus -Josue Cabascango _20240322_194349_0000.pdfVirus -Josue Cabascango _20240322_194349_0000.pdf
Virus -Josue Cabascango _20240322_194349_0000.pdfMiSpotify
 
Algoritmos Paralelos - Actividad 14 - UNIBE.pdf
Algoritmos Paralelos - Actividad 14 - UNIBE.pdfAlgoritmos Paralelos - Actividad 14 - UNIBE.pdf
Algoritmos Paralelos - Actividad 14 - UNIBE.pdfdarosario3d
 
Herramientas de Mantenimiento_Soporte Técnico_David Andrade.pdf
Herramientas de Mantenimiento_Soporte Técnico_David Andrade.pdfHerramientas de Mantenimiento_Soporte Técnico_David Andrade.pdf
Herramientas de Mantenimiento_Soporte Técnico_David Andrade.pdfdaa100407
 
Diseño de Algoritmos Paralelos. Mejorando la eficiencia computacional aprovec...
Diseño de Algoritmos Paralelos. Mejorando la eficiencia computacional aprovec...Diseño de Algoritmos Paralelos. Mejorando la eficiencia computacional aprovec...
Diseño de Algoritmos Paralelos. Mejorando la eficiencia computacional aprovec...AlexaRamirez39
 
02 - RUP_Introduccion_Definicion.pdf
02 - RUP_Introduccion_Definicion.pdf02 - RUP_Introduccion_Definicion.pdf
02 - RUP_Introduccion_Definicion.pdfRodrigo Cerón
 
Los mejores simuladores electrónicos que se pueden utilizar
Los mejores simuladores electrónicos que se pueden utilizarLos mejores simuladores electrónicos que se pueden utilizar
Los mejores simuladores electrónicos que se pueden utilizarjosuesj13
 
03 - RUP_Elaboracion_Construccion_1_2024.pdf
03 - RUP_Elaboracion_Construccion_1_2024.pdf03 - RUP_Elaboracion_Construccion_1_2024.pdf
03 - RUP_Elaboracion_Construccion_1_2024.pdfRodrigo Cerón
 
Simuladores de circuitos electrónicos.pdf
Simuladores de circuitos electrónicos.pdfSimuladores de circuitos electrónicos.pdf
Simuladores de circuitos electrónicos.pdfLeonardoOa4
 

Recently uploaded (9)

Formato de práctica reflexiva ante una problemática social.docx.pdf
Formato de práctica reflexiva ante una problemática social.docx.pdfFormato de práctica reflexiva ante una problemática social.docx.pdf
Formato de práctica reflexiva ante una problemática social.docx.pdf
 
Virus -Josue Cabascango _20240322_194349_0000.pdf
Virus -Josue Cabascango _20240322_194349_0000.pdfVirus -Josue Cabascango _20240322_194349_0000.pdf
Virus -Josue Cabascango _20240322_194349_0000.pdf
 
Algoritmos Paralelos - Actividad 14 - UNIBE.pdf
Algoritmos Paralelos - Actividad 14 - UNIBE.pdfAlgoritmos Paralelos - Actividad 14 - UNIBE.pdf
Algoritmos Paralelos - Actividad 14 - UNIBE.pdf
 
Herramientas de Mantenimiento_Soporte Técnico_David Andrade.pdf
Herramientas de Mantenimiento_Soporte Técnico_David Andrade.pdfHerramientas de Mantenimiento_Soporte Técnico_David Andrade.pdf
Herramientas de Mantenimiento_Soporte Técnico_David Andrade.pdf
 
Diseño de Algoritmos Paralelos. Mejorando la eficiencia computacional aprovec...
Diseño de Algoritmos Paralelos. Mejorando la eficiencia computacional aprovec...Diseño de Algoritmos Paralelos. Mejorando la eficiencia computacional aprovec...
Diseño de Algoritmos Paralelos. Mejorando la eficiencia computacional aprovec...
 
02 - RUP_Introduccion_Definicion.pdf
02 - RUP_Introduccion_Definicion.pdf02 - RUP_Introduccion_Definicion.pdf
02 - RUP_Introduccion_Definicion.pdf
 
Los mejores simuladores electrónicos que se pueden utilizar
Los mejores simuladores electrónicos que se pueden utilizarLos mejores simuladores electrónicos que se pueden utilizar
Los mejores simuladores electrónicos que se pueden utilizar
 
03 - RUP_Elaboracion_Construccion_1_2024.pdf
03 - RUP_Elaboracion_Construccion_1_2024.pdf03 - RUP_Elaboracion_Construccion_1_2024.pdf
03 - RUP_Elaboracion_Construccion_1_2024.pdf
 
Simuladores de circuitos electrónicos.pdf
Simuladores de circuitos electrónicos.pdfSimuladores de circuitos electrónicos.pdf
Simuladores de circuitos electrónicos.pdf
 

Integrando React.js en aplicaciones Symfony (deSymfony 2016)

  • 1. deSymfony 16-17 septiembre 2016 Madrid INTEGRANDO REACT.JS EN APLICACIONES SYMFONY Nacho Martín
  • 2. deSymfony ¡Muchas gracias a nuestros patrocinadores!
  • 3. 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
  • 4. Objetivo: Mostrar cosas que nos encontramos al usar React desde Symfony, en tierra de nadie, y que ninguno de los dos va a documentar.
  • 5. ¿A mí qué me importa el frontend?
  • 8. Echar huevos en sartén La premisa fundamental Cómo hacer una tortilla Comprar huevos Romper huevos
  • 9. Echar huevos en sartén Batir huevos La premisa fundamental Cómo hacer una tortilla Comprar huevos Romper huevos
  • 12. Opciones: La premisa fundamental 1: Repintamos todo. Simple
  • 13. Opciones: La premisa fundamental 1: Repintamos todo. Simple Poco eficiente
  • 14. Opciones: 2: Buscamos en el DOM dónde insertar, qué mover, qué eliminar. La premisa fundamental 1: Repintamos todo. Simple Poco eficiente
  • 15. Opciones: 2: Buscamos en el DOM dónde insertar, qué mover, qué eliminar. La premisa fundamental 1: Repintamos todo. Simple Complejo Poco eficiente
  • 16. Opciones: 2: Buscamos en el DOM dónde insertar, qué mover, qué eliminar. La premisa fundamental 1: Repintamos todo. Simple Muy eficienteComplejo Poco eficiente
  • 17. 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
  • 18. 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
  • 19. 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.
  • 20. ¡Clícame! Clicks: 0 Nuestro primer componente
  • 21. ¡Clícame! Clicks: 1 Nuestro primer componente ¡Clícame!
  • 22. Nuestro primer componente import 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;
  • 23. Nuestro primer componente import 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)
  • 24. Nuestro primer componente import 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
  • 25. Nuestro primer componente import 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
  • 26. Nuestro primer componente import 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
  • 27. Trabajar con el estado
  • 28. Trabajar con el estado constructor(props) { super(props); this.state = {count: 1}; } Estado inicial
  • 29. Trabajar con el estado constructor(props) { super(props); this.state = {count: 1}; } Estado inicial this.setState({count: this.state.count + 1}); Asignar estado
  • 30. 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
  • 31. render() y JSX render() { 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.
  • 32. render() y JSX render() { 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.
  • 33. render() y JSX render() { 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.
  • 34. Thinking in React render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> ); }
  • 35. Thinking in React render() { 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
  • 36. Thinking in React render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> ); } Aquí no Ajax
  • 37. Thinking in React render() { 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
  • 38. Importante: pensar la jerarquía
  • 39. Importante: pensar la jerarquía
  • 40. Jerarquía de componentes: props class CounterGroup extends Component { render() { return ( <div> <Counter name="amigo"/> <Counter name="señor"/> </div> ); } }
  • 41. 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: props class CounterGroup extends Component { render() { return ( <div> <Counter name="amigo"/> <Counter name="señor"/> </div> ); } }
  • 42. 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: props class CounterGroup extends Component { render() { return ( <div> <Counter name="amigo"/> <Counter name="señor"/> </div> ); } }
  • 43. Pro tip: componentes sin estado const Saludador = (props) => { <div> <div>Hola {props.name}</div> </div> }
  • 44. Todo depende del estado, por tanto:
  • 45. Todo depende del estado, por tanto: •Podemos reproducir estados,
  • 46. Todo depende del estado, por tanto: •Podemos reproducir estados, •rebobinar,
  • 47. Todo depende del estado, por tanto: •Podemos reproducir estados, •rebobinar, •loguear cambios de estado,
  • 48. Todo depende del estado, por tanto: •Podemos reproducir estados, •rebobinar, •loguear cambios de estado, •hacer álbum de estilo
  • 49. Todo depende del estado, por tanto: •Podemos reproducir estados, •rebobinar, •loguear cambios de estado, •hacer álbum de estilo •…
  • 51. ¿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> ); }
  • 52. …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> ); }
  • 53. …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
  • 54. React Targets •Web - react-dom •Mobile - React Native •Gl shaders - gl-react •Canvas - react-canvas •Terminal - react-blessed
  • 56. Setup
  • 60. Webpack • Gestiona dependencias por nosotros. Pros
  • 61. Webpack • Gestiona dependencias por nosotros. • Permite varios entornos: producción, desarrollo, …. Pros
  • 62. Webpack • Gestiona dependencias por nosotros. • Permite varios entornos: producción, desarrollo, …. • Recarga automática de página (e incluso hot reload). Pros
  • 63. 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
  • 64. 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
  • 65. 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
  • 66. 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.
  • 67. 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
  • 68. Inserción <div id="react-placeholder"></div> import ReactDOM from 'react-dom'; ReactDOM.render( <Counter name="amigo">, document.getElementById('react-placeholder') ); HTML JavaScript
  • 71. ReactBundle {{ react_component('RecipesApp', {'props': props}) }} import ReactOnRails from 'react-on-rails'; import RecipesApp from './RecipesAppServer'; ReactOnRails.register({ RecipesApp }); Twig: JavaScript:
  • 72. ReactBundle {{ react_component('RecipesApp', {'props': props}) }} import ReactOnRails from 'react-on-rails'; import RecipesApp from './RecipesAppServer'; ReactOnRails.register({ RecipesApp }); Twig: JavaScript:
  • 73. ReactBundle {{ react_component('RecipesApp', {'props': props}) }} import ReactOnRails from 'react-on-rails'; import RecipesApp from './RecipesAppServer'; ReactOnRails.register({ RecipesApp }); Twig: JavaScript:
  • 74. 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:
  • 77. 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
  • 78. 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.
  • 79. 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.
  • 80. 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.
  • 81. 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.
  • 82. Client-side {{ react_component('RecipesApp', {'props': props, 'rendering': 'client-side'}}) }} TWIG
  • 83. Client-side {{ react_component('RecipesApp', {'props': props, 'rendering': 'client-side'}}) }} TWIG
  • 84. 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
  • 85. 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>
  • 86. Client-side y server-side {{ react_component('RecipesApp', {'props': props, 'rendering': 'both'}}) }} TWIG
  • 87. Client-side y server-side {{ react_component('RecipesApp', {'props': props, 'rendering': 'both'}}) }} TWIG
  • 88. 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>
  • 89. 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
  • 91. Opción 1: llamar a subproceso node.js Llamamos a node.js con el componente Process de Symfony * Cómodo (si tenemos node.js instalado). * Lento. Librería: https://github.com/nacmartin/phpexecjs
  • 92. Opción 2: v8js Usamos 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
  • 93. 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:
  • 94. Opción 3: Servidor externo Tenemos 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
  • 95. Configuración Opción 3 limenius_react: serverside_rendering: mode: “external” server-socket-path: “../tal_y_tal/node.sock” config.yml:
  • 96. Lo mejor de los dos mundos En desarrollo usar llamada a node.js o v8js con phpexecjs. En producción tener un servidor externo. Si podemos cachear, menos problema.
  • 97. 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:
  • 98. ¿Vale la pena una app universal?
  • 99. ¿Vale la pena una app universal? En ocasiones sí, pero introduce complejidad.
  • 100. Soporte para Redux (+brevísima introducción a Redux)
  • 101. Redux: una cuestión de estado guardar Tu nombre: Juan Hola, Juan Cosas de Juan:
  • 102. Redux: una cuestión de estado guardar Tu nombre: Juan Hola, Juan Cosas de Juan:
  • 103. Redux: una cuestión de estado guardar Tu nombre: Juan Hola, Juan Cosas de Juan: state.name callback para cambiarlo
  • 105. dispatch(changeName(‘Juan')); Componente changeName = (name) => { return { type: ‘CHANGE_NAME', name } } Action
  • 106. 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
  • 107. 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
  • 108. 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
  • 109. 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') }}
  • 110. 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') }}
  • 111. Compartir store entre componentes
  • 112. Compartir store entre componentes React React React Twig Twig React Al compartir store comparten estado Twig
  • 113. Formularios, un caso especial
  • 114. 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).
  • 117. 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, ...); };
  • 120. Forms en HTML $form->createView(); submit país: España España Portugal direcciones: C tal- +state.usuario
  • 121. Forms en HTML $form->createView(); submit país: España España Portugal direcciones: C tal- +state.usuario
  • 122. Forms en HTML $form->createView(); submit país: España España Portugal direcciones: C tal- +state.usuario POST bien formadito con country:’es’ y no ‘España’, ‘espana', ‘spain', ‘0’…
  • 123. Forms en HTML $form->createView(); $form->submit($request); submit país: España España Portugal direcciones: C tal- +state.usuario POST bien formadito con country:’es’ y no ‘España’, ‘espana', ‘spain', ‘0’…
  • 125. Forms en API $form; submit país: España España Portugal direcciones: C tal- +state.usuario ✘
  • 126. Forms en API $form; submit país: España España Portugal direcciones: C tal- +state.usuario ✘ ¿Cómo sabemos los campos, o los choices? ¡A documentar! :(
  • 127. Forms en API $form; $form->submit($request); submit país: España España Portugal direcciones: C tal- +state.usuario POST “voy a tener suerte” ✘ ¿Cómo sabemos los campos, o los choices? ¡A documentar! :(
  • 128. Forms en API $form; $form->submit($request); submit país: España España Portugal 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
  • 129. Forms en API $form; $form->submit($request); submit país: España España Portugal 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 The value you selected is not a valid choice!!
  • 130. Forms en API $form; $form->submit($request); submit país: España España Portugal 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 The value you selected is not a valid choice!!One or more of the given values is invalid!! :D
  • 131. Forms en API $form; $form->submit($request); submit país: España España Portugal 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 The value you selected is not a valid choice!!One or more of the given values is invalid!! :DMUHAHAHAHAHA!!!!!
  • 134. Definir (y mantener) por triplicado Form SF API docs Form Cliente :( ¿Cuántos programadores hacen falta para hacer un formulario?
  • 139. Lo que necesitamos $form->createView(); HTML ¡Serializar! Vale, ¿A qué formato? API $miTransformador->transform($form);
  • 142. 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
  • 143. A partir del schema generamos form
  • 144. 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.
  • 145. Generadores client-side: diferencias Cada 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
  • 146. 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
  • 147. Cómo serializar: resolvers + transformers $transformer = $resolver->resolve($form); $jsonSchema = $transformer->transform($form); Ejemplo de esta técnica: https://github.com/Limenius/LiformBundle
  • 148. 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
  • 149. Transformer Misió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; } }
  • 150. Transformer Recopila información de cada Form Field. Podemos sacar mucha información: •Valores por defecto & placeholders. •Atributos del formulario. •Validadores.
  • 151. Ejemplo: validación ‘pattern’ protected function addPattern($form, &$schema) { if ($attr = $form->getConfig()->getOption('attr')) { if (isset($attr['pattern'])) { $schema['pattern'] = $attr['pattern']; } } }
  • 152. Esta técnica vale también para Angular, Backbone, mobile…
  • 154. Repaso: • Qué es React • Setup • Apps universales (ReactBundle) • Para qué sirve Redux • El problema de los formularios • Cómo aliviarlo con JSON Schema
  • 155. MADRID · NOV 27-28 · 2015 ¡Gracias! @nacmartin nacho@limenius.com http://limenius.com Formación, consultoría y desarrollo de proyectos