SlideShare une entreprise Scribd logo
1  sur  167
Télécharger pour lire hors ligne
PHP UK Conference
February 2017
Integrating React.js with
PHP Projects
Nacho Martín
@nacmartin
My name is Nacho Martin.
Almost every project needs a rich frontend,
for one reason or another.
We build tailor-made projects.
So we have been thinking about this for some time.
I write code at Limenius
Why (as a PHP developer) should I
care about the frontend?
What is React.js?
Pour eggs in the pan
The fundamental premise
How to cook an omelette
Buy eggs
Break eggs
Pour eggs in the pan
Beat eggs
The fundamental premise
How to cook an omelette
Buy eggs
Break eggs
Options:
The fundamental premise
Options:
The fundamental premise
1: Re-render everything.
Options:
The fundamental premise
1: Re-render everything. Simple
Options:
The fundamental premise
1: Re-render everything. Simple Not efficient
Options:
2: Find in the DOM where to
insert elements, what to move,
what to remove…
The fundamental premise
1: Re-render everything. Simple Not efficient
Options:
2: Find in the DOM where to
insert elements, what to move,
what to remove…
The fundamental premise
1: Re-render everything. Simple
Complex
Not efficient
Options:
2: Find in the DOM where to
insert elements, what to move,
what to remove…
The fundamental premise
1: Re-render everything. Simple
EfficientComplex
Not efficient
Options:
2: Find in the DOM where to
insert elements, what to move,
what to remove…
The fundamental premise
1: Re-render everything. Simple
EfficientComplex
Not efficient
React allows us to do 1, although it does 2 behind the scenes
Give me a state and a render() method that depends
on it and forget about how and when to render.*
The fundamental premise
Give me a state and a render() method that depends
on it and forget about how and when to render.*
The fundamental premise
* Unless you want more control, which is possible.
Click me! Clicks: 0
Our first component
Click me! Clicks: 1Click me!
Our first component
Our first component
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)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
}
export default Counter;
Our first component
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)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
}
export default Counter;
ES6 Syntax (optional but great)
Our first component
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)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
}
export default Counter;
ES6 Syntax (optional but great)
Initial state
Our first component
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)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
}
export default Counter;
ES6 Syntax (optional but great)
Set new state
Initial state
Our first component
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)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
}
export default Counter;
ES6 Syntax (optional but great)
Set new state
render(), called by React
Initial state
Working with state
Working with state
constructor(props) {
super(props);
this.state = {count: 1};
}
Initial state
Working with state
constructor(props) {
super(props);
this.state = {count: 1};
}
Initial state
this.setState({count: this.state.count + 1});
Assign state
Working with state
constructor(props) {
super(props);
this.state = {count: 1};
}
Initial state
this.setState({count: this.state.count + 1});
Assign state
this.state.count = this.state.count + 1;
Just remember: avoid this
render() and JSX
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Clícame!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
It is not HTML, it is JSX.
React transforms it internally to HTML elements.
Good practice: make render() as clean as possible, only a return.
render() and JSX
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Clícame!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
It is not HTML, it is JSX.
React transforms it internally to HTML elements.
Some things change
Good practice: make render() as clean as possible, only a return.
render() and JSX
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Clícame!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
It is not HTML, it is JSX.
React transforms it internally to HTML elements.
Some things change
We can insert JS expressions between {}
Good practice: make render() as clean as possible, only a return.
Thinking in React
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
Thinking in React
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
Here we don’t modify state
Thinking in React
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
Here we don’t make Ajax calls
Thinking in React
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
Here we don’t calculate decimals of PI and send an e-mail
with the result
Important: think our hierarchy
Important: think our hierarchy
Component hierarchy: 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)}>
Click me! {this.props.name}
</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
and in Counter…
Component hierarchy: 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)}>
Click me! {this.props.name}
</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
and in Counter…
Component hierarchy: props
class CounterGroup extends Component {
render() {
return (
<div>
<Counter name="amigo"/>
<Counter name="señor"/>
</div>
);
}
}
Pro tip: Stateless components
const Greeter = (props) => (
<div>
<div>Hi {props.name}!</div>
</div>
)
Tip: Presentational and Container components
const TaskList = (props) => (
<div>
{props.tasks.map((task, idx) => {
<div key={idx}>{task.name}</div>
})}
</div>
)
class TasksListContainer extends React.Component {
constructor(props) {
super(props)
this.state = {tasks: []}
}
componentDidMount() {
// Load data with Ajax and whatnot
}
render() {
return <TaskList tasks={this.state.tasks}/>
}
}
Everything depends on the state, therefore we can:
Everything depends on the state, therefore we can:
•Reproduce states,
Everything depends on the state, therefore we can:
•Reproduce states,
•Rewind,
Everything depends on the state, therefore we can:
•Reproduce states,
•Rewind,
•Log state changes,
Everything depends on the state, therefore we can:
•Reproduce states,
•Rewind,
•Log state changes,
•Make storybooks,
Everything depends on the state, therefore we can:
•Reproduce states,
•Rewind,
•Log state changes,
•Make storybooks,
•…
Learn once,
write everywhere
What if instead of this…
render() {
return (
<div className="App">
<button onClick={this.tick.bind(this)}>Click me!</button>
<span>Clicks: {this.state.count}</span>
</div>
);
}
…we have something like this?
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>
);
}
…we have something like this?
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
Recommended setup
Webpack
Pros
Webpack
• Manages dependencies
Pros
Webpack
• Manages dependencies
• Allows several environments: production, development, ….
Pros
Webpack
• Manages dependencies
• Allows several environments: production, development, ….
• Automatic page reload (even hot reload).
Pros
Webpack
• Manages dependencies
• Allows several environments: production, development, ….
• Automatic page reload (even hot reload).
• Can use preprocessors/“transpilers”, like Babel.
Pros
Webpack
• Manages dependencies
• Allows several environments: production, development, ….
• Automatic page reload (even hot reload).
• Can use preprocessors/“transpilers”, like Babel.
Pros
Cons
Webpack
• Manages dependencies
• Allows several environments: production, development, ….
• Automatic page reload (even hot reload).
• Can use preprocessors/“transpilers”, like Babel.
Pros
Cons
• It has a non trivial learning curve.
Webpack
• Manages dependencies
• Allows several environments: production, development, ….
• Automatic page reload (even hot reload).
• Can use preprocessors/“transpilers”, like Babel.
Pros
Cons
• It has a non trivial learning curve.
I maintain a sandbox: https://github.com/Limenius/symfony-react-sandbox
Insertion
<div id="react-placeholder"></div>
import ReactDOM from 'react-dom';
ReactDOM.render(
<Counter name="amigo">,
document.getElementById('react-placeholder')
);
HTML
JavaScript
Integration with PHP
https://github.com/Limenius/ReactRenderer
https://github.com/shakacode/react_on_rails
Based on
ReactRenderer
{{ react_component('RecipesApp', {'props': props}) }}
import ReactOnRails from 'react-on-rails';
import RecipesApp from './RecipesAppServer';
ReactOnRails.register({ RecipesApp });
Twig:
JavaScript:
ReactRenderer
{{ react_component('RecipesApp', {'props': props}) }}
import ReactOnRails from 'react-on-rails';
import RecipesApp from './RecipesAppServer';
ReactOnRails.register({ RecipesApp });
Twig:
JavaScript:
ReactRenderer
{{ 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=“[my Array in JSON]" data-trace=“false" data-dom-id=“sfreact-57d05640f2f1a”></div>
Generated HTML:
Server-side rendering
Give me a state and a render() method that depends
on it and forget about how and when to render.
The fundamental premise
Give me a state and a render() method that depends
on it and forget about how and when to render.
The fundamental premise
We can render components in the server
Give me a state and a render() method that depends
on it and forget about how and when to render.
The fundamental premise
We can render components in the server
• SEO friendly.
Give me a state and a render() method that depends
on it and forget about how and when to render.
The fundamental premise
We can render components in the server
• SEO friendly.
• Faster perceived page loads.
Give me a state and a render() method that depends
on it and forget about how and when to render.
The fundamental premise
We can render components in the server
• SEO friendly.
• Faster perceived page loads.
• We can cache.
Client-side + Server-side
{{ react_component('RecipesApp', {'props': props, rendering': 'both'}}) }}
TWIG
Client-side + Server-side
{{ react_component('RecipesApp', {'props': props, rendering': 'both'}}) }}
TWIG
HTML returned by the server
<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 + Server-side
{{ react_component('RecipesApp', {'props': props, rendering': 'both'}}) }}
TWIG
HTML returned by the server
<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>
An then React in the browser takes control over the component
Universal applications: Options
Option 1: Call a node.js subprocess
Make a call to node.js using Symfony Process component
* Easy (if we have node.js installed).
* Slow.
Library: https://github.com/nacmartin/phpexecjs
Option 2: v8js
Use PHP extension v8js
* Easy (although compiling the extension and v8 is not a breeze).
* Currently slow, maybe we could have v8 preloaded using php-pm so it
is not destroyed after every request-response cycle.
Library: https://github.com/nacmartin/phpexecjs
Option 3: External node.js server
We have “stupid” node.js server used only to render React components.
It has <100 LoC, and it doesn’t know anything about our logic.
* “Annoying” (we have to keep it running, which is not super annoying
either).
* Faster.
There is an example a dummy server for this purpose at
https://github.com/Limenius/symfony-react-sandbox
Options 1 & 2
$renderer = new PhpExecJsReactRenderer(‘path_to/server-bundle.js’);
$ext = new ReactRenderExtension($renderer, 'both');
$twig->addExtension($ext);
phpexecjs detects the presence of the extension v8js,
if not, calls node.js
Option 3
$renderer = new ExternalServerReactRenderer(‘../some_path/node.sock’);
$ext = new ReactRenderExtension($renderer, 'both');
$twig->addExtension($ext);
The best of the two worlds
In development use node.js or v8js with phpexecjs.
In production use an external server.
If we can cache server-side responses, even better.
Server side rendering, is it worth
it?
Server side rendering, is it worth
it?
Sometimes yes, but it introduces
complexity
Redux support
(+very brief introduction to Redux)
Redux: a matter of state
save
Your name: John
Hi, John
John’s stuff
Redux: a matter of state
save
Your name: John
Hi, John
John’s stuff
Redux: a matter of state
save
Your name: John
Hi, John
John’s stuff
state.name
callback to change it
dispatch(changeName(‘John'));
Component
dispatch(changeName(‘John'));
Component
changeName = (name) => {
return {
type: ‘CHANGE_NAME',
name
}
}
Action
dispatch(changeName(‘John'));
Component
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(‘John'));
Component
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 == ‘John';dispatch(changeName(‘John'));
Component
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 with ReactRenderer
Sample code in
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 with ReactRenderer
Sample code in
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('AnotherComponent') }}
Share store between components
React
React
React
Twig
Twig
React
By sharing store they can share state
Twig
Share store between components
Forms, a special case
Dynamic forms, why?
•Inside of React components.
•Important forms where UX means better conversions.
•Very specific forms.
•Very dynamic forms that aren’t boring (see Typeform for instance).
Typically PHP frameworks have a Form Component
Typically PHP frameworks have a Form Component
$form (e.g. Form Symfony Component)
Typically PHP frameworks have a Form Component
$form (e.g. Form Symfony Component)
Initial values
Typically PHP frameworks have a Form Component
$form (e.g. Form Symfony Component)
Initial values
UI hints (widgets, attributes)
Typically PHP frameworks have a Form Component
$form (e.g. Form Symfony Component)
Initial values
UI hints (widgets, attributes)
Bind incoming data
Typically PHP frameworks have a Form Component
$form (e.g. Form Symfony Component)
Initial values
UI hints (widgets, attributes)
Bind incoming data
Deserialize
Typically PHP frameworks have a Form Component
$form (e.g. Form Symfony Component)
Initial values
UI hints (widgets, attributes)
Bind incoming data
Deserialize
Validate
Typically PHP frameworks have a Form Component
$form (e.g. Form Symfony Component)
Initial values
UI hints (widgets, attributes)
Bind incoming data
Deserialize
Validate
Return errors
Typically PHP frameworks have a Form Component
$form (e.g. Form Symfony Component) $form->createView() (helpers in other Fws not Sf)
Initial values
UI hints (widgets, attributes)
Bind incoming data
Deserialize
Validate
Return errors
Typically PHP frameworks have a Form Component
$form (e.g. Form Symfony Component) $form->createView() (helpers in other Fws not Sf)
Initial values
UI hints (widgets, attributes)
Bind incoming data
Deserialize
Validate
Return errors
Render view
Typically PHP frameworks have a Form Component
$form (e.g. Form Symfony Component) $form->createView() (helpers in other Fws not Sf)
Initial values
UI hints (widgets, attributes)
Bind incoming data
Deserialize
Validate
Return errors
Render view
Show errors after Submit
Typically PHP frameworks have a Form Component
$form (e.g. Form Symfony Component) $form->createView() (helpers in other Fws not Sf)
Initial values
UI hints (widgets, attributes)
Bind incoming data
Deserialize
Validate
Return errors
Render view
Some client-side validation (HTML5)
Show errors after Submit
Using forms in an API
$form $form->createView() (helpers in other Fws not Sf)
Initial values
UI hints (widgets, attributes)
Bind incoming data
Deserialize
Validate
Return errors
Render view
Some client-side validation (HTML5)
Show errors after Submit
…and we want more
$form $form->createView() (helpers in other Fws not Sf)
Initial values
UI hints (widgets, attributes)
Bind incoming data
Deserialize
Validate
Return errors
Render view
On Submit validation
Some client-side validation (HTML5)
…and we want more
$form $form->createView() (helpers in other Fws not Sf)
Initial values
UI hints (widgets, attributes)
Bind incoming data
Deserialize
Validate
Return errors
Render view
On blur sync validation
On Submit validation
Some client-side validation (HTML5)
…and we want more
$form $form->createView() (helpers in other Fws not Sf)
Initial values
UI hints (widgets, attributes)
Bind incoming data
Deserialize
Validate
Return errors
Render view
On blur sync validation
On Submit validation
On blur async validation
Some client-side validation (HTML5)
…and we want more
$form $form->createView() (helpers in other Fws not Sf)
Initial values
UI hints (widgets, attributes)
Bind incoming data
Deserialize
Validate
Return errors
Render view
On blur sync validation
On Submit validation
On blur async validation
Some client-side validation (HTML5)
All the dynamic goodies
Suppose this Symfony form
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('country', ChoiceType::class, [
'choices' => [
'United Kingdom' => 'gb',
'Deutschland' => 'de',
'España' => 'es',
]
])
->add('addresses', CollectionType::class, ...);
};
Forms rendered to HTML
$form->createView();
state.usuario
Forms rendered to HTML
$form->createView();
state.usuario
Forms rendered to HTML
$form->createView();
submit
Country: España
Deutschland
España
Addresses:
Some St.-
+state.usuario
Forms rendered to HTML
$form->createView();
submit
Country: España
Deutschland
España
Addresses:
Some St.-
+state.usuario
POST well formed
with country:’es’
and not ‘España’, ‘espana', ‘spain', ‘0’…
Forms rendered to HTML
$form->createView();
$form->submit($request);
submit
Country: España
Deutschland
España
Addresses:
Some St.-
+state.usuario
POST well formed
with country:’es’
and not ‘España’, ‘espana', ‘spain', ‘0’…
state.usuario
Forms in APIs
$form;
submit
Country: España
Deutschland
España
Addresses:
Some St.-
+state.usuario
Forms in APIs
$form; ✘
How do we know the visible choices or values?
Read the docs!
submit
Country: España
Deutschland
España
Addresses:
Some St.-
+state.usuario
Forms in APIs
$form;
$form->submit($request);
POST “I'm Feeling Lucky”
✘
How do we know the visible choices or values?
Read the docs!
submit
Country: España
Deutschland
España
Addresses:
Some St.-
+state.usuario This form should not contain extra fields!!1
Forms in APIs
$form;
$form->submit($request);
POST “I'm Feeling Lucky”
✘
How do we know the visible choices or values?
Read the docs!
submit
Country: España
Deutschland
España
Addresses:
Some St.-
+state.usuario This form should not contain extra fields!!1
The value you selected is not a valid choice!!
Forms in APIs
$form;
$form->submit($request);
POST “I'm Feeling Lucky”
✘
How do we know the visible choices or values?
Read the docs!
submit
Country: España
Deutschland
España
Addresses:
Some St.-
+state.usuario 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 in APIs
$form;
$form->submit($request);
POST “I'm Feeling Lucky”
✘
How do we know the visible choices or values?
Read the docs!
submit
Country: España
Deutschland
España
Addresses:
Some St.-
+state.usuario 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!!!!!
Forms in APIs
$form;
$form->submit($request);
POST “I'm Feeling Lucky”
✘
How do we know the visible choices or values?
Read the docs!
Define, maintain and keep in sync in triplicate
Form Server API docs Form Client
:_(
How many devs does it take to write a form?
Wizard type form?
“While you code this
we will be preparing
different versions for
other use cases”
Case: Complex Wizard Form
Case: Complex Wizard Form
Case: Complex Wizard Form
What we need
$form->createView();
HTML
API
$mySerializer->serialize($form);
What we need
$form->createView();
HTML
Serialize! Ok, into which format?
API
$mySerializer->serialize($form);
JSON Schema
json-schema.org
How does it look like
{
"$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"]
}
Definitions
Types,
Validation rules
:_)
New resource: my-api/products/form
Liform & LiformBundle
https://github.com/Limenius/Liform
use LimeniusLiformResolver;
use LimeniusLiformLiform;
$resolver = new Resolver();
$resolver->setDefaultTransformers();
$liform = new Liform($resolver);
$form = $this->createForm(CarType::class, $car, ['csrf_protection' => false]);
$schema = json_encode($liform->transform($form));
Transform piece by piece
{"title":"task",
"type":"object",
"properties":{
"name":{
"type":"string",
"title":"Name",
"default":"I'm a placeholder",
"propertyOrder":1
},
"description":{
"type":"string",
"widget":"textarea",
"title":"Description",
"description":"An explanation of the task",
"propertyOrder":2
},
"dueTo":{
"type":"string",
"title":"Due to",
"widget":"datetime",
"format":"date-time",
"propertyOrder":3
}
},
“required":[ “name", "description","dueTo"]}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name',TypeTextType::class, [
'label' => 'Name',
'required' => true,
'attr' => ['placeholder' => 'I'm a placeholder’]])
->add('description',TypeTextType::class, [
'label' => 'Description',
'liform' => [
'widget' => 'textarea',
'description' => 'An explanation of the task',
]])
->add('dueTo',TypeDateTimeType::class, [
'label' => 'Due to',
'widget' => 'single_text']
)
;
}
Transform piece by piece
{"title":"task",
"type":"object",
"properties":{
"name":{
"type":"string",
"title":"Name",
"default":"I'm a placeholder",
"propertyOrder":1
},
"description":{
"type":"string",
"widget":"textarea",
"title":"Description",
"description":"An explanation of the task",
"propertyOrder":2
},
"dueTo":{
"type":"string",
"title":"Due to",
"widget":"datetime",
"format":"date-time",
"propertyOrder":3
}
},
“required":[ “name", "description","dueTo"]}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name',TypeTextType::class, [
'label' => 'Name',
'required' => true,
'attr' => ['placeholder' => 'I'm a placeholder’]])
->add('description',TypeTextType::class, [
'label' => 'Description',
'liform' => [
'widget' => 'textarea',
'description' => 'An explanation of the task',
]])
->add('dueTo',TypeDateTimeType::class, [
'label' => 'Due to',
'widget' => 'single_text']
)
;
}
Transform piece by piece
{"title":"task",
"type":"object",
"properties":{
"name":{
"type":"string",
"title":"Name",
"default":"I'm a placeholder",
"propertyOrder":1
},
"description":{
"type":"string",
"widget":"textarea",
"title":"Description",
"description":"An explanation of the task",
"propertyOrder":2
},
"dueTo":{
"type":"string",
"title":"Due to",
"widget":"datetime",
"format":"date-time",
"propertyOrder":3
}
},
“required":[ “name", "description","dueTo"]}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name',TypeTextType::class, [
'label' => 'Name',
'required' => true,
'attr' => ['placeholder' => 'I'm a placeholder’]])
->add('description',TypeTextType::class, [
'label' => 'Description',
'liform' => [
'widget' => 'textarea',
'description' => 'An explanation of the task',
]])
->add('dueTo',TypeDateTimeType::class, [
'label' => 'Due to',
'widget' => 'single_text']
)
;
}
Transform piece by piece
{"title":"task",
"type":"object",
"properties":{
"name":{
"type":"string",
"title":"Name",
"default":"I'm a placeholder",
"propertyOrder":1
},
"description":{
"type":"string",
"widget":"textarea",
"title":"Description",
"description":"An explanation of the task",
"propertyOrder":2
},
"dueTo":{
"type":"string",
"title":"Due to",
"widget":"datetime",
"format":"date-time",
"propertyOrder":3
}
},
“required":[ “name", "description","dueTo"]}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name',TypeTextType::class, [
'label' => 'Name',
'required' => true,
'attr' => ['placeholder' => 'I'm a placeholder’]])
->add('description',TypeTextType::class, [
'label' => 'Description',
'liform' => [
'widget' => 'textarea',
'description' => 'An explanation of the task',
]])
->add('dueTo',TypeDateTimeType::class, [
'label' => 'Due to',
'widget' => 'single_text']
)
;
}
Transformers
Transformers extract information from each Form Field.
We can extract a lot of information:
•Default values and placeholders.
•Field attributes.
•Validation rules.
Also important
•FormView serializer for initial values.
•Form serializer that extracts errors.
{ "code":null,
"message":"Validation Failed",
"errors":{
"children":{
"name":{
"errors":[
"This value should not be equal to Beetlejuice."
]
},
"description":[],
"dueTo":[]
}
}
}
So far we have:
$form $form->createView() (helpers in other Fws not Sf)
Initial values
UI hints (widgets, attributes)
Bind incoming data
Deserialize
Validate
Return errors
Render view
On blur sync validation
On Submit validation
On blur async validation
Some client-side validation (HTML5)
All the dynamic goodies
Form Server API docs Form Client
$form Json-schema
Liform
Leverage json-schema: Validators
let valid = ajv.validate(schema, data)
if (!valid) console.log(ajv.errors)
https://github.com/epoberezkin/ajv
Leverage json-schema: Form generators
• mozilla/react-jsonschema-form: React.
• limenius/liform-react: React + Redux; integrated with redux-form
(we ♥ redux-form).
• …
• Creating our own generator is not so difficult (you typically only
need all the widgets, only a subset)
liform-react
By using redux-form we can:
• Have sane and powerful representation of state in Redux.
• Integrate on-blur validation, async validation & on Submit
validation.
• Define our own widgets/themes.
• Know from the beginning that it is flexible enough.
liform-react
import React from 'react'
import { createStore, combineReducers } from 'redux'
import { reducer as formReducer } from 'redux-form'
import { Provider } from 'react-redux'
import Liform from 'liform-react'
const MyForm = () => {
const reducer = combineReducers({ form: formReducer })
const store = createStore(reducer)
const schema = {
//…
}
}
return (
<Provider store={store}>
<Liform schema={schema} onSubmit={(v) => {console.log(v)}}/>
</Provider>
)
}
liform-react
{
"properties": {
"name": {
"title":"Task name",
"type":"string",
"minLength": 2
},
"description": {
"title":"Description",
"type":"string",
"widget":"textarea"
},
"dueTo": {
"title":"Due to",
"type":"string",
"widget":"datetime",
"format":"date-time"
}
},
"required":["name"]
}
Form Server API docs Form Client
$form Json-schema React form
Liform liform-react
=:D
$form $form->createView()
Initial values
UI hints (widgets, attributes)
Bind incoming data
Deserialize
Validate
Return errors
Render view
On blur sync validation
On Submit validation
On blur async validation
Some client-side validation (HTML5)
All the dynamic goodies
https://github.com/Limenius/symfony-react-sandbox
Example with
• Webpack
• ReactRenderer/ReactBundle
• Liform/LiformBudle & liform-react
MADRID · NOV 27-28 · 2015
Thanks!
@nacmartin
nacho@limenius.com
http://limenius.com
Formation, Consulting and
Development.

Contenu connexe

Tendances

Introduction to React JS for beginners | Namespace IT
Introduction to React JS for beginners | Namespace ITIntroduction to React JS for beginners | Namespace IT
Introduction to React JS for beginners | Namespace ITnamespaceit
 
How native is React Native? | React Native vs Native App Development
How native is React Native? | React Native vs Native App DevelopmentHow native is React Native? | React Native vs Native App Development
How native is React Native? | React Native vs Native App DevelopmentDevathon
 
Introduction to Node.js
Introduction to Node.jsIntroduction to Node.js
Introduction to Node.jsVikash Singh
 
React JS: A Secret Preview
React JS: A Secret PreviewReact JS: A Secret Preview
React JS: A Secret Previewvaluebound
 
Intro To React Native
Intro To React NativeIntro To React Native
Intro To React NativeFITC
 
React JS - A quick introduction tutorial
React JS - A quick introduction tutorialReact JS - A quick introduction tutorial
React JS - A quick introduction tutorialMohammed Fazuluddin
 
Browser Automation with Playwright – for integration, RPA, UI testing and mor...
Browser Automation with Playwright – for integration, RPA, UI testing and mor...Browser Automation with Playwright – for integration, RPA, UI testing and mor...
Browser Automation with Playwright – for integration, RPA, UI testing and mor...Lucas Jellema
 
Introduction to React JS
Introduction to React JSIntroduction to React JS
Introduction to React JSArno Lordkronos
 
What is component in reactjs
What is component in reactjsWhat is component in reactjs
What is component in reactjsmanojbkalla
 
Real Life Clean Architecture
Real Life Clean ArchitectureReal Life Clean Architecture
Real Life Clean ArchitectureMattia Battiston
 

Tendances (20)

Introduction to React JS for beginners | Namespace IT
Introduction to React JS for beginners | Namespace ITIntroduction to React JS for beginners | Namespace IT
Introduction to React JS for beginners | Namespace IT
 
How native is React Native? | React Native vs Native App Development
How native is React Native? | React Native vs Native App DevelopmentHow native is React Native? | React Native vs Native App Development
How native is React Native? | React Native vs Native App Development
 
Introduction to React Native
Introduction to React NativeIntroduction to React Native
Introduction to React Native
 
Introduction to Node.js
Introduction to Node.jsIntroduction to Node.js
Introduction to Node.js
 
React JS: A Secret Preview
React JS: A Secret PreviewReact JS: A Secret Preview
React JS: A Secret Preview
 
React hooks
React hooksReact hooks
React hooks
 
ReactJS presentation.pptx
ReactJS presentation.pptxReactJS presentation.pptx
ReactJS presentation.pptx
 
Intro To React Native
Intro To React NativeIntro To React Native
Intro To React Native
 
React JS - A quick introduction tutorial
React JS - A quick introduction tutorialReact JS - A quick introduction tutorial
React JS - A quick introduction tutorial
 
Browser Automation with Playwright – for integration, RPA, UI testing and mor...
Browser Automation with Playwright – for integration, RPA, UI testing and mor...Browser Automation with Playwright – for integration, RPA, UI testing and mor...
Browser Automation with Playwright – for integration, RPA, UI testing and mor...
 
Its time to React.js
Its time to React.jsIts time to React.js
Its time to React.js
 
Ngrx slides
Ngrx slidesNgrx slides
Ngrx slides
 
Introduction to React JS
Introduction to React JSIntroduction to React JS
Introduction to React JS
 
Learn react-js
Learn react-jsLearn react-js
Learn react-js
 
React Native
React NativeReact Native
React Native
 
React JS
React JSReact JS
React JS
 
What is component in reactjs
What is component in reactjsWhat is component in reactjs
What is component in reactjs
 
Real Life Clean Architecture
Real Life Clean ArchitectureReal Life Clean Architecture
Real Life Clean Architecture
 
reactJS
reactJSreactJS
reactJS
 
React workshop
React workshopReact workshop
React workshop
 

En vedette

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
 
API Platform 2.1: when Symfony meets ReactJS (Symfony Live 2017)
API Platform 2.1: when Symfony meets ReactJS (Symfony Live 2017)API Platform 2.1: when Symfony meets ReactJS (Symfony Live 2017)
API Platform 2.1: when Symfony meets ReactJS (Symfony Live 2017)Les-Tilleuls.coop
 
We Built It, And They Didn't Come!
We Built It, And They Didn't Come!We Built It, And They Didn't Come!
We Built It, And They Didn't Come!Lukas Fittl
 
Object Calisthenics Applied to PHP
Object Calisthenics Applied to PHPObject Calisthenics Applied to PHP
Object Calisthenics Applied to PHPGuilherme Blanco
 
Programming objects with android
Programming objects with androidProgramming objects with android
Programming objects with androidfirenze-gtug
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix itRafael Dohms
 
Kicking the Bukkit: Anatomy of an open source meltdown
Kicking the Bukkit: Anatomy of an open source meltdownKicking the Bukkit: Anatomy of an open source meltdown
Kicking the Bukkit: Anatomy of an open source meltdownRyanMichela
 
Understanding PHP objects
Understanding PHP objectsUnderstanding PHP objects
Understanding PHP objectsjulien pauli
 
Visual guide to selling software as a service by @prezly
Visual guide to selling software as a service by @prezlyVisual guide to selling software as a service by @prezly
Visual guide to selling software as a service by @prezlyPrezly
 
Rethinking Best Practices
Rethinking Best PracticesRethinking Best Practices
Rethinking Best Practicesfloydophone
 
Composer The Right Way - 010PHP
Composer The Right Way - 010PHPComposer The Right Way - 010PHP
Composer The Right Way - 010PHPRafael Dohms
 
10 commandments for better android development
10 commandments for better android development10 commandments for better android development
10 commandments for better android developmentTrey Robinson
 
Enterprise PHP: mappers, models and services
Enterprise PHP: mappers, models and servicesEnterprise PHP: mappers, models and services
Enterprise PHP: mappers, models and servicesAaron Saray
 
From development environments to production deployments with Docker, Compose,...
From development environments to production deployments with Docker, Compose,...From development environments to production deployments with Docker, Compose,...
From development environments to production deployments with Docker, Compose,...Jérôme Petazzoni
 
Enterprise PHP Architecture through Design Patterns and Modularization (Midwe...
Enterprise PHP Architecture through Design Patterns and Modularization (Midwe...Enterprise PHP Architecture through Design Patterns and Modularization (Midwe...
Enterprise PHP Architecture through Design Patterns and Modularization (Midwe...Aaron Saray
 
Functional Programming Patterns (BuildStuff '14)
Functional Programming Patterns (BuildStuff '14)Functional Programming Patterns (BuildStuff '14)
Functional Programming Patterns (BuildStuff '14)Scott Wlaschin
 
From Idea to Execution: Spotify's Discover Weekly
From Idea to Execution: Spotify's Discover WeeklyFrom Idea to Execution: Spotify's Discover Weekly
From Idea to Execution: Spotify's Discover WeeklyChris Johnson
 
Linux Profiling at Netflix
Linux Profiling at NetflixLinux Profiling at Netflix
Linux Profiling at NetflixBrendan Gregg
 

En vedette (20)

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
 
API Platform 2.1: when Symfony meets ReactJS (Symfony Live 2017)
API Platform 2.1: when Symfony meets ReactJS (Symfony Live 2017)API Platform 2.1: when Symfony meets ReactJS (Symfony Live 2017)
API Platform 2.1: when Symfony meets ReactJS (Symfony Live 2017)
 
We Built It, And They Didn't Come!
We Built It, And They Didn't Come!We Built It, And They Didn't Come!
We Built It, And They Didn't Come!
 
Object Calisthenics Applied to PHP
Object Calisthenics Applied to PHPObject Calisthenics Applied to PHP
Object Calisthenics Applied to PHP
 
Programming objects with android
Programming objects with androidProgramming objects with android
Programming objects with android
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix it
 
Kicking the Bukkit: Anatomy of an open source meltdown
Kicking the Bukkit: Anatomy of an open source meltdownKicking the Bukkit: Anatomy of an open source meltdown
Kicking the Bukkit: Anatomy of an open source meltdown
 
Understanding PHP objects
Understanding PHP objectsUnderstanding PHP objects
Understanding PHP objects
 
Visual guide to selling software as a service by @prezly
Visual guide to selling software as a service by @prezlyVisual guide to selling software as a service by @prezly
Visual guide to selling software as a service by @prezly
 
Rethinking Best Practices
Rethinking Best PracticesRethinking Best Practices
Rethinking Best Practices
 
Composer The Right Way - 010PHP
Composer The Right Way - 010PHPComposer The Right Way - 010PHP
Composer The Right Way - 010PHP
 
10 commandments for better android development
10 commandments for better android development10 commandments for better android development
10 commandments for better android development
 
Clean code
Clean codeClean code
Clean code
 
How to Design Indexes, Really
How to Design Indexes, ReallyHow to Design Indexes, Really
How to Design Indexes, Really
 
Enterprise PHP: mappers, models and services
Enterprise PHP: mappers, models and servicesEnterprise PHP: mappers, models and services
Enterprise PHP: mappers, models and services
 
From development environments to production deployments with Docker, Compose,...
From development environments to production deployments with Docker, Compose,...From development environments to production deployments with Docker, Compose,...
From development environments to production deployments with Docker, Compose,...
 
Enterprise PHP Architecture through Design Patterns and Modularization (Midwe...
Enterprise PHP Architecture through Design Patterns and Modularization (Midwe...Enterprise PHP Architecture through Design Patterns and Modularization (Midwe...
Enterprise PHP Architecture through Design Patterns and Modularization (Midwe...
 
Functional Programming Patterns (BuildStuff '14)
Functional Programming Patterns (BuildStuff '14)Functional Programming Patterns (BuildStuff '14)
Functional Programming Patterns (BuildStuff '14)
 
From Idea to Execution: Spotify's Discover Weekly
From Idea to Execution: Spotify's Discover WeeklyFrom Idea to Execution: Spotify's Discover Weekly
From Idea to Execution: Spotify's Discover Weekly
 
Linux Profiling at Netflix
Linux Profiling at NetflixLinux Profiling at Netflix
Linux Profiling at Netflix
 

Similaire à Integrating React.js with PHP projects

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
 
React + Redux. Best practices
React + Redux.  Best practicesReact + Redux.  Best practices
React + Redux. Best practicesClickky
 
Into React-DOM.pptx
Into React-DOM.pptxInto React-DOM.pptx
Into React-DOM.pptxRan Wahle
 
Battle of React State Managers in frontend applications
Battle of React State Managers in frontend applicationsBattle of React State Managers in frontend applications
Battle of React State Managers in frontend applicationsEvangelia Mitsopoulou
 
React new features and intro to Hooks
React new features and intro to HooksReact new features and intro to Hooks
React new features and intro to HooksSoluto
 
Introduction to React for Frontend Developers
Introduction to React for Frontend DevelopersIntroduction to React for Frontend Developers
Introduction to React for Frontend DevelopersSergio Nakamura
 
Building user interface with react
Building user interface with reactBuilding user interface with react
Building user interface with reactAmit Thakkar
 
Recompacting your react application
Recompacting your react applicationRecompacting your react application
Recompacting your react applicationGreg Bergé
 
React.js workshop by tech47.in
React.js workshop by tech47.inReact.js workshop by tech47.in
React.js workshop by tech47.inJaikant Kumaran
 
Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3 Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3 DreamLab
 
Enhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentEnhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentYao Nien Chung
 
Intro to React | DreamLab Academy
Intro to React | DreamLab AcademyIntro to React | DreamLab Academy
Intro to React | DreamLab AcademyDreamLab
 
React & Redux for noobs
React & Redux for noobsReact & Redux for noobs
React & Redux for noobs[T]echdencias
 
React + Redux Introduction
React + Redux IntroductionReact + Redux Introduction
React + Redux IntroductionNikolaus Graf
 

Similaire à Integrating React.js with PHP projects (20)

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
 
Introduction to Redux
Introduction to ReduxIntroduction to Redux
Introduction to Redux
 
React + Redux. Best practices
React + Redux.  Best practicesReact + Redux.  Best practices
React + Redux. Best practices
 
React redux
React reduxReact redux
React redux
 
React hooks
React hooksReact hooks
React hooks
 
React lecture
React lectureReact lecture
React lecture
 
React outbox
React outboxReact outbox
React outbox
 
Into React-DOM.pptx
Into React-DOM.pptxInto React-DOM.pptx
Into React-DOM.pptx
 
Battle of React State Managers in frontend applications
Battle of React State Managers in frontend applicationsBattle of React State Managers in frontend applications
Battle of React State Managers in frontend applications
 
React new features and intro to Hooks
React new features and intro to HooksReact new features and intro to Hooks
React new features and intro to Hooks
 
Introduction to React for Frontend Developers
Introduction to React for Frontend DevelopersIntroduction to React for Frontend Developers
Introduction to React for Frontend Developers
 
Building user interface with react
Building user interface with reactBuilding user interface with react
Building user interface with react
 
Recompacting your react application
Recompacting your react applicationRecompacting your react application
Recompacting your react application
 
React.js workshop by tech47.in
React.js workshop by tech47.inReact.js workshop by tech47.in
React.js workshop by tech47.in
 
Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3 Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3
 
Enhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentEnhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order component
 
Intro to React | DreamLab Academy
Intro to React | DreamLab AcademyIntro to React | DreamLab Academy
Intro to React | DreamLab Academy
 
React & Redux for noobs
React & Redux for noobsReact & Redux for noobs
React & Redux for noobs
 
React render props
React render propsReact render props
React render props
 
React + Redux Introduction
React + Redux IntroductionReact + Redux Introduction
React + Redux Introduction
 

Plus de 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
 
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
 
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)Ignacio 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
 

Plus de Ignacio Martín (15)

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
 
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
 
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
 
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
 
Doctrine2 sf2Vigo
Doctrine2 sf2VigoDoctrine2 sf2Vigo
Doctrine2 sf2Vigo
 
Presentacion git
Presentacion gitPresentacion git
Presentacion git
 

Dernier

『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书rnrncn29
 
Company Snapshot Theme for Business by Slidesgo.pptx
Company Snapshot Theme for Business by Slidesgo.pptxCompany Snapshot Theme for Business by Slidesgo.pptx
Company Snapshot Theme for Business by Slidesgo.pptxMario
 
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书zdzoqco
 
IP addressing and IPv6, presented by Paul Wilson at IETF 119
IP addressing and IPv6, presented by Paul Wilson at IETF 119IP addressing and IPv6, presented by Paul Wilson at IETF 119
IP addressing and IPv6, presented by Paul Wilson at IETF 119APNIC
 
TRENDS Enabling and inhibiting dimensions.pptx
TRENDS Enabling and inhibiting dimensions.pptxTRENDS Enabling and inhibiting dimensions.pptx
TRENDS Enabling and inhibiting dimensions.pptxAndrieCagasanAkio
 
Top 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptxTop 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptxDyna Gilbert
 
ETHICAL HACKING dddddddddddddddfnandni.pptx
ETHICAL HACKING dddddddddddddddfnandni.pptxETHICAL HACKING dddddddddddddddfnandni.pptx
ETHICAL HACKING dddddddddddddddfnandni.pptxNIMMANAGANTI RAMAKRISHNA
 
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书rnrncn29
 
SCM Symposium PPT Format Customer loyalty is predi
SCM Symposium PPT Format Customer loyalty is prediSCM Symposium PPT Format Customer loyalty is predi
SCM Symposium PPT Format Customer loyalty is predieusebiomeyer
 
Film cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasaFilm cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasa494f574xmv
 
Unidad 4 – Redes de ordenadores (en inglés).pptx
Unidad 4 – Redes de ordenadores (en inglés).pptxUnidad 4 – Redes de ordenadores (en inglés).pptx
Unidad 4 – Redes de ordenadores (en inglés).pptxmibuzondetrabajo
 

Dernier (11)

『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
 
Company Snapshot Theme for Business by Slidesgo.pptx
Company Snapshot Theme for Business by Slidesgo.pptxCompany Snapshot Theme for Business by Slidesgo.pptx
Company Snapshot Theme for Business by Slidesgo.pptx
 
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
 
IP addressing and IPv6, presented by Paul Wilson at IETF 119
IP addressing and IPv6, presented by Paul Wilson at IETF 119IP addressing and IPv6, presented by Paul Wilson at IETF 119
IP addressing and IPv6, presented by Paul Wilson at IETF 119
 
TRENDS Enabling and inhibiting dimensions.pptx
TRENDS Enabling and inhibiting dimensions.pptxTRENDS Enabling and inhibiting dimensions.pptx
TRENDS Enabling and inhibiting dimensions.pptx
 
Top 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptxTop 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptx
 
ETHICAL HACKING dddddddddddddddfnandni.pptx
ETHICAL HACKING dddddddddddddddfnandni.pptxETHICAL HACKING dddddddddddddddfnandni.pptx
ETHICAL HACKING dddddddddddddddfnandni.pptx
 
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
 
SCM Symposium PPT Format Customer loyalty is predi
SCM Symposium PPT Format Customer loyalty is prediSCM Symposium PPT Format Customer loyalty is predi
SCM Symposium PPT Format Customer loyalty is predi
 
Film cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasaFilm cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasa
 
Unidad 4 – Redes de ordenadores (en inglés).pptx
Unidad 4 – Redes de ordenadores (en inglés).pptxUnidad 4 – Redes de ordenadores (en inglés).pptx
Unidad 4 – Redes de ordenadores (en inglés).pptx
 

Integrating React.js with PHP projects

  • 1. PHP UK Conference February 2017 Integrating React.js with PHP Projects Nacho Martín @nacmartin
  • 2. My name is Nacho Martin. Almost every project needs a rich frontend, for one reason or another. We build tailor-made projects. So we have been thinking about this for some time. I write code at Limenius
  • 3. Why (as a PHP developer) should I care about the frontend?
  • 4.
  • 6. Pour eggs in the pan The fundamental premise How to cook an omelette Buy eggs Break eggs
  • 7. Pour eggs in the pan Beat eggs The fundamental premise How to cook an omelette Buy eggs Break eggs
  • 9. Options: The fundamental premise 1: Re-render everything.
  • 10. Options: The fundamental premise 1: Re-render everything. Simple
  • 11. Options: The fundamental premise 1: Re-render everything. Simple Not efficient
  • 12. Options: 2: Find in the DOM where to insert elements, what to move, what to remove… The fundamental premise 1: Re-render everything. Simple Not efficient
  • 13. Options: 2: Find in the DOM where to insert elements, what to move, what to remove… The fundamental premise 1: Re-render everything. Simple Complex Not efficient
  • 14. Options: 2: Find in the DOM where to insert elements, what to move, what to remove… The fundamental premise 1: Re-render everything. Simple EfficientComplex Not efficient
  • 15. Options: 2: Find in the DOM where to insert elements, what to move, what to remove… The fundamental premise 1: Re-render everything. Simple EfficientComplex Not efficient React allows us to do 1, although it does 2 behind the scenes
  • 16. Give me a state and a render() method that depends on it and forget about how and when to render.* The fundamental premise
  • 17. Give me a state and a render() method that depends on it and forget about how and when to render.* The fundamental premise * Unless you want more control, which is possible.
  • 18. Click me! Clicks: 0 Our first component
  • 19. Click me! Clicks: 1Click me! Our first component
  • 20. Our first component 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)}>Click me!</button> <span>Clicks: {this.state.count}</span> </div> ); } } export default Counter;
  • 21. Our first component 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)}>Click me!</button> <span>Clicks: {this.state.count}</span> </div> ); } } export default Counter; ES6 Syntax (optional but great)
  • 22. Our first component 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)}>Click me!</button> <span>Clicks: {this.state.count}</span> </div> ); } } export default Counter; ES6 Syntax (optional but great) Initial state
  • 23. Our first component 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)}>Click me!</button> <span>Clicks: {this.state.count}</span> </div> ); } } export default Counter; ES6 Syntax (optional but great) Set new state Initial state
  • 24. Our first component 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)}>Click me!</button> <span>Clicks: {this.state.count}</span> </div> ); } } export default Counter; ES6 Syntax (optional but great) Set new state render(), called by React Initial state
  • 26. Working with state constructor(props) { super(props); this.state = {count: 1}; } Initial state
  • 27. Working with state constructor(props) { super(props); this.state = {count: 1}; } Initial state this.setState({count: this.state.count + 1}); Assign state
  • 28. Working with state constructor(props) { super(props); this.state = {count: 1}; } Initial state this.setState({count: this.state.count + 1}); Assign state this.state.count = this.state.count + 1; Just remember: avoid this
  • 29. render() and JSX render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> ); It is not HTML, it is JSX. React transforms it internally to HTML elements. Good practice: make render() as clean as possible, only a return.
  • 30. render() and JSX render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> ); It is not HTML, it is JSX. React transforms it internally to HTML elements. Some things change Good practice: make render() as clean as possible, only a return.
  • 31. render() and JSX render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Clícame!</button> <span>Clicks: {this.state.count}</span> </div> ); It is not HTML, it is JSX. React transforms it internally to HTML elements. Some things change We can insert JS expressions between {} Good practice: make render() as clean as possible, only a return.
  • 32. Thinking in React render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Click me!</button> <span>Clicks: {this.state.count}</span> </div> ); }
  • 33. Thinking in React render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Click me!</button> <span>Clicks: {this.state.count}</span> </div> ); } Here we don’t modify state
  • 34. Thinking in React render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Click me!</button> <span>Clicks: {this.state.count}</span> </div> ); } Here we don’t make Ajax calls
  • 35. Thinking in React render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Click me!</button> <span>Clicks: {this.state.count}</span> </div> ); } Here we don’t calculate decimals of PI and send an e-mail with the result
  • 36. Important: think our hierarchy
  • 37. Important: think our hierarchy
  • 38. Component hierarchy: props class CounterGroup extends Component { render() { return ( <div> <Counter name="amigo"/> <Counter name="señor"/> </div> ); } }
  • 39. render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}> Click me! {this.props.name} </button> <span>Clicks: {this.state.count}</span> </div> ); } and in Counter… Component hierarchy: props class CounterGroup extends Component { render() { return ( <div> <Counter name="amigo"/> <Counter name="señor"/> </div> ); } }
  • 40. render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}> Click me! {this.props.name} </button> <span>Clicks: {this.state.count}</span> </div> ); } and in Counter… Component hierarchy: props class CounterGroup extends Component { render() { return ( <div> <Counter name="amigo"/> <Counter name="señor"/> </div> ); } }
  • 41. Pro tip: Stateless components const Greeter = (props) => ( <div> <div>Hi {props.name}!</div> </div> )
  • 42. Tip: Presentational and Container components const TaskList = (props) => ( <div> {props.tasks.map((task, idx) => { <div key={idx}>{task.name}</div> })} </div> ) class TasksListContainer extends React.Component { constructor(props) { super(props) this.state = {tasks: []} } componentDidMount() { // Load data with Ajax and whatnot } render() { return <TaskList tasks={this.state.tasks}/> } }
  • 43. Everything depends on the state, therefore we can:
  • 44. Everything depends on the state, therefore we can: •Reproduce states,
  • 45. Everything depends on the state, therefore we can: •Reproduce states, •Rewind,
  • 46. Everything depends on the state, therefore we can: •Reproduce states, •Rewind, •Log state changes,
  • 47. Everything depends on the state, therefore we can: •Reproduce states, •Rewind, •Log state changes, •Make storybooks,
  • 48. Everything depends on the state, therefore we can: •Reproduce states, •Rewind, •Log state changes, •Make storybooks, •…
  • 50. What if instead of this… render() { return ( <div className="App"> <button onClick={this.tick.bind(this)}>Click me!</button> <span>Clicks: {this.state.count}</span> </div> ); }
  • 51. …we have something like this? 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> ); }
  • 52. …we have something like this? 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
  • 53. React Targets •Web - react-dom •Mobile - react-native •Gl shaders - gl-react •Canvas - react-canvas •Terminal - react-blessed
  • 55. Setup
  • 59. Webpack • Manages dependencies • Allows several environments: production, development, …. Pros
  • 60. Webpack • Manages dependencies • Allows several environments: production, development, …. • Automatic page reload (even hot reload). Pros
  • 61. Webpack • Manages dependencies • Allows several environments: production, development, …. • Automatic page reload (even hot reload). • Can use preprocessors/“transpilers”, like Babel. Pros
  • 62. Webpack • Manages dependencies • Allows several environments: production, development, …. • Automatic page reload (even hot reload). • Can use preprocessors/“transpilers”, like Babel. Pros Cons
  • 63. Webpack • Manages dependencies • Allows several environments: production, development, …. • Automatic page reload (even hot reload). • Can use preprocessors/“transpilers”, like Babel. Pros Cons • It has a non trivial learning curve.
  • 64. Webpack • Manages dependencies • Allows several environments: production, development, …. • Automatic page reload (even hot reload). • Can use preprocessors/“transpilers”, like Babel. Pros Cons • It has a non trivial learning curve. I maintain a sandbox: https://github.com/Limenius/symfony-react-sandbox
  • 65. Insertion <div id="react-placeholder"></div> import ReactDOM from 'react-dom'; ReactDOM.render( <Counter name="amigo">, document.getElementById('react-placeholder') ); HTML JavaScript
  • 68. ReactRenderer {{ react_component('RecipesApp', {'props': props}) }} import ReactOnRails from 'react-on-rails'; import RecipesApp from './RecipesAppServer'; ReactOnRails.register({ RecipesApp }); Twig: JavaScript:
  • 69. ReactRenderer {{ react_component('RecipesApp', {'props': props}) }} import ReactOnRails from 'react-on-rails'; import RecipesApp from './RecipesAppServer'; ReactOnRails.register({ RecipesApp }); Twig: JavaScript:
  • 70. ReactRenderer {{ 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=“[my Array in JSON]" data-trace=“false" data-dom-id=“sfreact-57d05640f2f1a”></div> Generated HTML:
  • 72.
  • 73. Give me a state and a render() method that depends on it and forget about how and when to render. The fundamental premise
  • 74. Give me a state and a render() method that depends on it and forget about how and when to render. The fundamental premise We can render components in the server
  • 75. Give me a state and a render() method that depends on it and forget about how and when to render. The fundamental premise We can render components in the server • SEO friendly.
  • 76. Give me a state and a render() method that depends on it and forget about how and when to render. The fundamental premise We can render components in the server • SEO friendly. • Faster perceived page loads.
  • 77. Give me a state and a render() method that depends on it and forget about how and when to render. The fundamental premise We can render components in the server • SEO friendly. • Faster perceived page loads. • We can cache.
  • 78. Client-side + Server-side {{ react_component('RecipesApp', {'props': props, rendering': 'both'}}) }} TWIG
  • 79. Client-side + Server-side {{ react_component('RecipesApp', {'props': props, rendering': 'both'}}) }} TWIG HTML returned by the server <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>
  • 80. Client-side + Server-side {{ react_component('RecipesApp', {'props': props, rendering': 'both'}}) }} TWIG HTML returned by the server <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> An then React in the browser takes control over the component
  • 82. Option 1: Call a node.js subprocess Make a call to node.js using Symfony Process component * Easy (if we have node.js installed). * Slow. Library: https://github.com/nacmartin/phpexecjs
  • 83. Option 2: v8js Use PHP extension v8js * Easy (although compiling the extension and v8 is not a breeze). * Currently slow, maybe we could have v8 preloaded using php-pm so it is not destroyed after every request-response cycle. Library: https://github.com/nacmartin/phpexecjs
  • 84. Option 3: External node.js server We have “stupid” node.js server used only to render React components. It has <100 LoC, and it doesn’t know anything about our logic. * “Annoying” (we have to keep it running, which is not super annoying either). * Faster. There is an example a dummy server for this purpose at https://github.com/Limenius/symfony-react-sandbox
  • 85. Options 1 & 2 $renderer = new PhpExecJsReactRenderer(‘path_to/server-bundle.js’); $ext = new ReactRenderExtension($renderer, 'both'); $twig->addExtension($ext); phpexecjs detects the presence of the extension v8js, if not, calls node.js
  • 86. Option 3 $renderer = new ExternalServerReactRenderer(‘../some_path/node.sock’); $ext = new ReactRenderExtension($renderer, 'both'); $twig->addExtension($ext);
  • 87. The best of the two worlds In development use node.js or v8js with phpexecjs. In production use an external server. If we can cache server-side responses, even better.
  • 88. Server side rendering, is it worth it?
  • 89. Server side rendering, is it worth it? Sometimes yes, but it introduces complexity
  • 90. Redux support (+very brief introduction to Redux)
  • 91. Redux: a matter of state save Your name: John Hi, John John’s stuff
  • 92. Redux: a matter of state save Your name: John Hi, John John’s stuff
  • 93. Redux: a matter of state save Your name: John Hi, John John’s stuff state.name callback to change it
  • 95. dispatch(changeName(‘John')); Component changeName = (name) => { return { type: ‘CHANGE_NAME', name } } Action
  • 96. dispatch(changeName(‘John')); Component 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
  • 97. dispatch(changeName(‘John')); Component 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
  • 98. this.props.name == ‘John';dispatch(changeName(‘John')); Component 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
  • 99.
  • 100.
  • 101. Redux with ReactRenderer Sample code in 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') }}
  • 102. Redux with ReactRenderer Sample code in 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('AnotherComponent') }}
  • 103. Share store between components
  • 104. React React React Twig Twig React By sharing store they can share state Twig Share store between components
  • 106. Dynamic forms, why? •Inside of React components. •Important forms where UX means better conversions. •Very specific forms. •Very dynamic forms that aren’t boring (see Typeform for instance).
  • 107.
  • 108. Typically PHP frameworks have a Form Component
  • 109. Typically PHP frameworks have a Form Component $form (e.g. Form Symfony Component)
  • 110. Typically PHP frameworks have a Form Component $form (e.g. Form Symfony Component) Initial values
  • 111. Typically PHP frameworks have a Form Component $form (e.g. Form Symfony Component) Initial values UI hints (widgets, attributes)
  • 112. Typically PHP frameworks have a Form Component $form (e.g. Form Symfony Component) Initial values UI hints (widgets, attributes) Bind incoming data
  • 113. Typically PHP frameworks have a Form Component $form (e.g. Form Symfony Component) Initial values UI hints (widgets, attributes) Bind incoming data Deserialize
  • 114. Typically PHP frameworks have a Form Component $form (e.g. Form Symfony Component) Initial values UI hints (widgets, attributes) Bind incoming data Deserialize Validate
  • 115. Typically PHP frameworks have a Form Component $form (e.g. Form Symfony Component) Initial values UI hints (widgets, attributes) Bind incoming data Deserialize Validate Return errors
  • 116. Typically PHP frameworks have a Form Component $form (e.g. Form Symfony Component) $form->createView() (helpers in other Fws not Sf) Initial values UI hints (widgets, attributes) Bind incoming data Deserialize Validate Return errors
  • 117. Typically PHP frameworks have a Form Component $form (e.g. Form Symfony Component) $form->createView() (helpers in other Fws not Sf) Initial values UI hints (widgets, attributes) Bind incoming data Deserialize Validate Return errors Render view
  • 118. Typically PHP frameworks have a Form Component $form (e.g. Form Symfony Component) $form->createView() (helpers in other Fws not Sf) Initial values UI hints (widgets, attributes) Bind incoming data Deserialize Validate Return errors Render view Show errors after Submit
  • 119. Typically PHP frameworks have a Form Component $form (e.g. Form Symfony Component) $form->createView() (helpers in other Fws not Sf) Initial values UI hints (widgets, attributes) Bind incoming data Deserialize Validate Return errors Render view Some client-side validation (HTML5) Show errors after Submit
  • 120. Using forms in an API $form $form->createView() (helpers in other Fws not Sf) Initial values UI hints (widgets, attributes) Bind incoming data Deserialize Validate Return errors Render view Some client-side validation (HTML5) Show errors after Submit
  • 121. …and we want more $form $form->createView() (helpers in other Fws not Sf) Initial values UI hints (widgets, attributes) Bind incoming data Deserialize Validate Return errors Render view On Submit validation Some client-side validation (HTML5)
  • 122. …and we want more $form $form->createView() (helpers in other Fws not Sf) Initial values UI hints (widgets, attributes) Bind incoming data Deserialize Validate Return errors Render view On blur sync validation On Submit validation Some client-side validation (HTML5)
  • 123. …and we want more $form $form->createView() (helpers in other Fws not Sf) Initial values UI hints (widgets, attributes) Bind incoming data Deserialize Validate Return errors Render view On blur sync validation On Submit validation On blur async validation Some client-side validation (HTML5)
  • 124. …and we want more $form $form->createView() (helpers in other Fws not Sf) Initial values UI hints (widgets, attributes) Bind incoming data Deserialize Validate Return errors Render view On blur sync validation On Submit validation On blur async validation Some client-side validation (HTML5) All the dynamic goodies
  • 125. Suppose this Symfony form public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('country', ChoiceType::class, [ 'choices' => [ 'United Kingdom' => 'gb', 'Deutschland' => 'de', 'España' => 'es', ] ]) ->add('addresses', CollectionType::class, ...); };
  • 126. Forms rendered to HTML $form->createView(); state.usuario
  • 127. Forms rendered to HTML $form->createView(); state.usuario
  • 128. Forms rendered to HTML $form->createView(); submit Country: España Deutschland España Addresses: Some St.- +state.usuario
  • 129. Forms rendered to HTML $form->createView(); submit Country: España Deutschland España Addresses: Some St.- +state.usuario POST well formed with country:’es’ and not ‘España’, ‘espana', ‘spain', ‘0’…
  • 130. Forms rendered to HTML $form->createView(); $form->submit($request); submit Country: España Deutschland España Addresses: Some St.- +state.usuario POST well formed with country:’es’ and not ‘España’, ‘espana', ‘spain', ‘0’…
  • 132. submit Country: España Deutschland España Addresses: Some St.- +state.usuario Forms in APIs $form; ✘ How do we know the visible choices or values? Read the docs!
  • 133. submit Country: España Deutschland España Addresses: Some St.- +state.usuario Forms in APIs $form; $form->submit($request); POST “I'm Feeling Lucky” ✘ How do we know the visible choices or values? Read the docs!
  • 134. submit Country: España Deutschland España Addresses: Some St.- +state.usuario This form should not contain extra fields!!1 Forms in APIs $form; $form->submit($request); POST “I'm Feeling Lucky” ✘ How do we know the visible choices or values? Read the docs!
  • 135. submit Country: España Deutschland España Addresses: Some St.- +state.usuario This form should not contain extra fields!!1 The value you selected is not a valid choice!! Forms in APIs $form; $form->submit($request); POST “I'm Feeling Lucky” ✘ How do we know the visible choices or values? Read the docs!
  • 136. submit Country: España Deutschland España Addresses: Some St.- +state.usuario 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 in APIs $form; $form->submit($request); POST “I'm Feeling Lucky” ✘ How do we know the visible choices or values? Read the docs!
  • 137. submit Country: España Deutschland España Addresses: Some St.- +state.usuario 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!!!!! Forms in APIs $form; $form->submit($request); POST “I'm Feeling Lucky” ✘ How do we know the visible choices or values? Read the docs!
  • 138.
  • 139.
  • 140. Define, maintain and keep in sync in triplicate Form Server API docs Form Client :_( How many devs does it take to write a form?
  • 141. Wizard type form? “While you code this we will be preparing different versions for other use cases”
  • 146. What we need $form->createView(); HTML Serialize! Ok, into which format? API $mySerializer->serialize($form);
  • 149. How does it look like { "$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"] } Definitions Types, Validation rules :_) New resource: my-api/products/form
  • 150. Liform & LiformBundle https://github.com/Limenius/Liform use LimeniusLiformResolver; use LimeniusLiformLiform; $resolver = new Resolver(); $resolver->setDefaultTransformers(); $liform = new Liform($resolver); $form = $this->createForm(CarType::class, $car, ['csrf_protection' => false]); $schema = json_encode($liform->transform($form));
  • 151. Transform piece by piece {"title":"task", "type":"object", "properties":{ "name":{ "type":"string", "title":"Name", "default":"I'm a placeholder", "propertyOrder":1 }, "description":{ "type":"string", "widget":"textarea", "title":"Description", "description":"An explanation of the task", "propertyOrder":2 }, "dueTo":{ "type":"string", "title":"Due to", "widget":"datetime", "format":"date-time", "propertyOrder":3 } }, “required":[ “name", "description","dueTo"]} public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('name',TypeTextType::class, [ 'label' => 'Name', 'required' => true, 'attr' => ['placeholder' => 'I'm a placeholder’]]) ->add('description',TypeTextType::class, [ 'label' => 'Description', 'liform' => [ 'widget' => 'textarea', 'description' => 'An explanation of the task', ]]) ->add('dueTo',TypeDateTimeType::class, [ 'label' => 'Due to', 'widget' => 'single_text'] ) ; }
  • 152. Transform piece by piece {"title":"task", "type":"object", "properties":{ "name":{ "type":"string", "title":"Name", "default":"I'm a placeholder", "propertyOrder":1 }, "description":{ "type":"string", "widget":"textarea", "title":"Description", "description":"An explanation of the task", "propertyOrder":2 }, "dueTo":{ "type":"string", "title":"Due to", "widget":"datetime", "format":"date-time", "propertyOrder":3 } }, “required":[ “name", "description","dueTo"]} public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('name',TypeTextType::class, [ 'label' => 'Name', 'required' => true, 'attr' => ['placeholder' => 'I'm a placeholder’]]) ->add('description',TypeTextType::class, [ 'label' => 'Description', 'liform' => [ 'widget' => 'textarea', 'description' => 'An explanation of the task', ]]) ->add('dueTo',TypeDateTimeType::class, [ 'label' => 'Due to', 'widget' => 'single_text'] ) ; }
  • 153. Transform piece by piece {"title":"task", "type":"object", "properties":{ "name":{ "type":"string", "title":"Name", "default":"I'm a placeholder", "propertyOrder":1 }, "description":{ "type":"string", "widget":"textarea", "title":"Description", "description":"An explanation of the task", "propertyOrder":2 }, "dueTo":{ "type":"string", "title":"Due to", "widget":"datetime", "format":"date-time", "propertyOrder":3 } }, “required":[ “name", "description","dueTo"]} public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('name',TypeTextType::class, [ 'label' => 'Name', 'required' => true, 'attr' => ['placeholder' => 'I'm a placeholder’]]) ->add('description',TypeTextType::class, [ 'label' => 'Description', 'liform' => [ 'widget' => 'textarea', 'description' => 'An explanation of the task', ]]) ->add('dueTo',TypeDateTimeType::class, [ 'label' => 'Due to', 'widget' => 'single_text'] ) ; }
  • 154. Transform piece by piece {"title":"task", "type":"object", "properties":{ "name":{ "type":"string", "title":"Name", "default":"I'm a placeholder", "propertyOrder":1 }, "description":{ "type":"string", "widget":"textarea", "title":"Description", "description":"An explanation of the task", "propertyOrder":2 }, "dueTo":{ "type":"string", "title":"Due to", "widget":"datetime", "format":"date-time", "propertyOrder":3 } }, “required":[ “name", "description","dueTo"]} public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('name',TypeTextType::class, [ 'label' => 'Name', 'required' => true, 'attr' => ['placeholder' => 'I'm a placeholder’]]) ->add('description',TypeTextType::class, [ 'label' => 'Description', 'liform' => [ 'widget' => 'textarea', 'description' => 'An explanation of the task', ]]) ->add('dueTo',TypeDateTimeType::class, [ 'label' => 'Due to', 'widget' => 'single_text'] ) ; }
  • 155. Transformers Transformers extract information from each Form Field. We can extract a lot of information: •Default values and placeholders. •Field attributes. •Validation rules.
  • 156. Also important •FormView serializer for initial values. •Form serializer that extracts errors. { "code":null, "message":"Validation Failed", "errors":{ "children":{ "name":{ "errors":[ "This value should not be equal to Beetlejuice." ] }, "description":[], "dueTo":[] } } }
  • 157. So far we have: $form $form->createView() (helpers in other Fws not Sf) Initial values UI hints (widgets, attributes) Bind incoming data Deserialize Validate Return errors Render view On blur sync validation On Submit validation On blur async validation Some client-side validation (HTML5) All the dynamic goodies
  • 158. Form Server API docs Form Client $form Json-schema Liform
  • 159. Leverage json-schema: Validators let valid = ajv.validate(schema, data) if (!valid) console.log(ajv.errors) https://github.com/epoberezkin/ajv
  • 160. Leverage json-schema: Form generators • mozilla/react-jsonschema-form: React. • limenius/liform-react: React + Redux; integrated with redux-form (we ♥ redux-form). • … • Creating our own generator is not so difficult (you typically only need all the widgets, only a subset)
  • 161. liform-react By using redux-form we can: • Have sane and powerful representation of state in Redux. • Integrate on-blur validation, async validation & on Submit validation. • Define our own widgets/themes. • Know from the beginning that it is flexible enough.
  • 162. liform-react import React from 'react' import { createStore, combineReducers } from 'redux' import { reducer as formReducer } from 'redux-form' import { Provider } from 'react-redux' import Liform from 'liform-react' const MyForm = () => { const reducer = combineReducers({ form: formReducer }) const store = createStore(reducer) const schema = { //… } } return ( <Provider store={store}> <Liform schema={schema} onSubmit={(v) => {console.log(v)}}/> </Provider> ) }
  • 163. liform-react { "properties": { "name": { "title":"Task name", "type":"string", "minLength": 2 }, "description": { "title":"Description", "type":"string", "widget":"textarea" }, "dueTo": { "title":"Due to", "type":"string", "widget":"datetime", "format":"date-time" } }, "required":["name"] }
  • 164. Form Server API docs Form Client $form Json-schema React form Liform liform-react
  • 165. =:D $form $form->createView() Initial values UI hints (widgets, attributes) Bind incoming data Deserialize Validate Return errors Render view On blur sync validation On Submit validation On blur async validation Some client-side validation (HTML5) All the dynamic goodies
  • 166. https://github.com/Limenius/symfony-react-sandbox Example with • Webpack • ReactRenderer/ReactBundle • Liform/LiformBudle & liform-react
  • 167. MADRID · NOV 27-28 · 2015 Thanks! @nacmartin nacho@limenius.com http://limenius.com Formation, Consulting and Development.