167
PHP UK Conference February 2017 Integrating React.js with PHP Projects Nacho Martín @nacmartin

Integrating React.js with PHP projects

Embed Size (px)

Citation preview

Page 1: Integrating React.js with PHP projects

PHP UK Conference February 2017

Integrating React.js with PHP Projects

Nacho Martín @nacmartin

Page 2: Integrating React.js with PHP projects

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

Page 3: Integrating React.js with PHP projects

Why (as a PHP developer) should I care about the frontend?

Page 4: Integrating React.js with PHP projects
Page 5: Integrating React.js with PHP projects

What is React.js?

Page 6: Integrating React.js with PHP projects

Pour eggs in the pan

The fundamental premise

How to cook an omeletteBuy eggs

Break eggs

Page 7: Integrating React.js with PHP projects

Pour eggs in the pan

Beat eggs

The fundamental premise

How to cook an omeletteBuy eggs

Break eggs

Page 8: Integrating React.js with PHP projects

Options:

The fundamental premise

Page 9: Integrating React.js with PHP projects

Options:

The fundamental premise

1: Re-render everything.

Page 10: Integrating React.js with PHP projects

Options:

The fundamental premise

1: Re-render everything. Simple

Page 11: Integrating React.js with PHP projects

Options:

The fundamental premise

1: Re-render everything. Simple Not efficient

Page 12: Integrating React.js with PHP projects

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

Page 13: Integrating React.js with PHP projects

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

Page 14: Integrating React.js with PHP projects

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

Page 15: Integrating React.js with PHP projects

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

Page 16: Integrating React.js with PHP projects

Give me a state and a render() method that depends on it and forget about how and when to render.*

The fundamental premise

Page 17: Integrating React.js with PHP projects

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.

Page 18: Integrating React.js with PHP projects

Click me! Clicks: 0

Our first component

Page 19: Integrating React.js with PHP projects

Click me! Clicks: 1Click me!

Our first component

Page 20: Integrating React.js with PHP projects

Our first componentimport 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;

Page 21: Integrating React.js with PHP projects

Our first componentimport 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)

Page 22: Integrating React.js with PHP projects

Our first componentimport 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

Page 23: Integrating React.js with PHP projects

Our first componentimport 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

Page 24: Integrating React.js with PHP projects

Our first componentimport 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

Page 25: Integrating React.js with PHP projects

Working with state

Page 26: Integrating React.js with PHP projects

Working with state

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

Initial state

Page 27: Integrating React.js with PHP projects

Working with state

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

Initial state

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

Assign state

Page 28: Integrating React.js with PHP projects

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

Page 29: Integrating React.js with PHP projects

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.

Page 30: Integrating React.js with PHP projects

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.

Page 31: Integrating React.js with PHP projects

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.

Page 32: Integrating React.js with PHP projects

Thinking in React

render() {

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

Page 33: Integrating React.js with PHP projects

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

Page 34: Integrating React.js with PHP projects

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

Page 35: Integrating React.js with PHP projects

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

Page 36: Integrating React.js with PHP projects

Important: think our hierarchy

Page 37: Integrating React.js with PHP projects

Important: think our hierarchy

Page 38: Integrating React.js with PHP projects

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

Page 39: Integrating React.js with PHP projects

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: propsclass CounterGroup extends Component { render() { return ( <div> <Counter name="amigo"/> <Counter name="señor"/> </div> ); }}

Page 40: Integrating React.js with PHP projects

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: propsclass CounterGroup extends Component { render() { return ( <div> <Counter name="amigo"/> <Counter name="señor"/> </div> ); }}

Page 41: Integrating React.js with PHP projects

Pro tip: Stateless components

const Greeter = (props) => { <div> <div>Hi {props.name}!</div> </div>}

Page 42: Integrating React.js with PHP projects

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}/> }}

Page 43: Integrating React.js with PHP projects

Everything depends on the state, therefore we can:

Page 44: Integrating React.js with PHP projects

Everything depends on the state, therefore we can:

•Reproduce states,

Page 45: Integrating React.js with PHP projects

Everything depends on the state, therefore we can:

•Reproduce states,•Rewind,

Page 46: Integrating React.js with PHP projects

Everything depends on the state, therefore we can:

•Reproduce states,•Rewind,•Log state changes,

Page 47: Integrating React.js with PHP projects

Everything depends on the state, therefore we can:

•Reproduce states,•Rewind,•Log state changes,•Make storybooks,

Page 48: Integrating React.js with PHP projects

Everything depends on the state, therefore we can:

•Reproduce states,•Rewind,•Log state changes,•Make storybooks,•…

Page 49: Integrating React.js with PHP projects

Learn once, write everywhere

Page 50: Integrating React.js with PHP projects

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> ); }

Page 51: Integrating React.js with PHP projects

…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> );}

Page 52: Integrating React.js with PHP projects

…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

Page 53: Integrating React.js with PHP projects

React Targets

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

Page 54: Integrating React.js with PHP projects

react-blessed (terminal)

Page 55: Integrating React.js with PHP projects

Setup

Page 56: Integrating React.js with PHP projects

Recommended setup

Page 57: Integrating React.js with PHP projects

WebpackPros

Page 58: Integrating React.js with PHP projects

Webpack

• Manages dependenciesPros

Page 59: Integrating React.js with PHP projects

Webpack

• Manages dependencies• Allows several environments: production, development, ….

Pros

Page 60: Integrating React.js with PHP projects

Webpack

• Manages dependencies• Allows several environments: production, development, ….• Automatic page reload (even hot reload).

Pros

Page 61: Integrating React.js with PHP projects

Webpack

• Manages dependencies• Allows several environments: production, development, ….• Automatic page reload (even hot reload).• Can use preprocessors/“transpilers”, like Babel.

Pros

Page 62: Integrating React.js with PHP projects

Webpack

• Manages dependencies• Allows several environments: production, development, ….• Automatic page reload (even hot reload).• Can use preprocessors/“transpilers”, like Babel.

Pros

Cons

Page 63: Integrating React.js with PHP projects

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.

Page 64: Integrating React.js with PHP projects

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

Page 65: Integrating React.js with PHP projects

Insertion

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

import ReactDOM from 'react-dom';

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

HTML

JavaScript

Page 66: Integrating React.js with PHP projects

Integration with PHP https://github.com/Limenius/ReactRenderer

Page 67: Integrating React.js with PHP projects

https://github.com/shakacode/react_on_rails

Based on

Page 68: Integrating React.js with PHP projects

ReactRenderer

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

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

ReactOnRails.register({ RecipesApp });

Twig:

JavaScript:

Page 69: Integrating React.js with PHP projects

ReactRenderer

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

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

ReactOnRails.register({ RecipesApp });

Twig:

JavaScript:

Page 70: Integrating React.js with PHP projects

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:

Page 71: Integrating React.js with PHP projects

Server-side rendering

Page 72: Integrating React.js with PHP projects
Page 73: Integrating React.js with PHP projects

Give me a state and a render() method that depends on it and forget about how and when to render.

The fundamental premise

Page 74: Integrating React.js with PHP projects

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

Page 75: Integrating React.js with PHP projects

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.

Page 76: Integrating React.js with PHP projects

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.

Page 77: Integrating React.js with PHP projects

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.

Page 78: Integrating React.js with PHP projects

Client-side + Server-side

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

TWIG

Page 79: Integrating React.js with PHP projects

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>

Page 80: Integrating React.js with PHP projects

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

Page 81: Integrating React.js with PHP projects

Universal applications: Options

Page 82: Integrating React.js with PHP projects

Option 1: Call a node.js subprocessMake a call to node.js using Symfony Process component

* Easy (if we have node.js installed).

* Slow.

Library: https://github.com/nacmartin/phpexecjs

Page 83: Integrating React.js with PHP projects

Option 2: v8jsUse 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

Page 84: Integrating React.js with PHP projects

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

Page 85: Integrating React.js with PHP projects

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

Page 86: Integrating React.js with PHP projects

Option 3

$renderer = new ExternalServerReactRenderer(‘../some_path/node.sock’);$ext = new ReactRenderExtension($renderer, 'both');

$twig->addExtension($ext);

Page 87: Integrating React.js with PHP projects

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.

Page 88: Integrating React.js with PHP projects

Server side rendering, is it worth it?

Page 89: Integrating React.js with PHP projects

Server side rendering, is it worth it?

Sometimes yes, but it introduces complexity

Page 90: Integrating React.js with PHP projects

Redux support (+very brief introduction to Redux)

Page 91: Integrating React.js with PHP projects

Redux: a matter of state

save

Your name: John

Hi, John

John’s stuff

Page 92: Integrating React.js with PHP projects

Redux: a matter of state

save

Your name: John

Hi, John

John’s stuff

Page 93: Integrating React.js with PHP projects

Redux: a matter of state

save

Your name: John

Hi, John

John’s stuff

state.name

callback to change it

Page 94: Integrating React.js with PHP projects

dispatch(changeName(‘John'));

Component

Page 95: Integrating React.js with PHP projects

dispatch(changeName(‘John'));

Component

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

Action

Page 96: Integrating React.js with PHP projects

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

Page 97: Integrating React.js with PHP projects

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

Page 98: Integrating React.js with PHP projects

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

Page 99: Integrating React.js with PHP projects
Page 100: Integrating React.js with PHP projects
Page 101: Integrating React.js with PHP projects

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') }}

Page 102: Integrating React.js with PHP projects

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') }}

Page 103: Integrating React.js with PHP projects

Share store between components

Page 104: Integrating React.js with PHP projects

React

ReactReact

Twig

Twig

React

By sharing store they can share state

Twig

Share store between components

Page 105: Integrating React.js with PHP projects

Forms, a special case

Page 106: Integrating React.js with PHP projects

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).

Page 107: Integrating React.js with PHP projects
Page 108: Integrating React.js with PHP projects

Typically PHP frameworks have a Form Component

Page 109: Integrating React.js with PHP projects

Typically PHP frameworks have a Form Component$form (e.g. Form Symfony Component)

Page 110: Integrating React.js with PHP projects

Typically PHP frameworks have a Form Component$form (e.g. Form Symfony Component)

Initial values

Page 111: Integrating React.js with PHP projects

Typically PHP frameworks have a Form Component$form (e.g. Form Symfony Component)

Initial values

UI hints (widgets, attributes)

Page 112: Integrating React.js with PHP projects

Typically PHP frameworks have a Form Component$form (e.g. Form Symfony Component)

Initial values

UI hints (widgets, attributes)

Bind incoming data

Page 113: Integrating React.js with PHP projects

Typically PHP frameworks have a Form Component$form (e.g. Form Symfony Component)

Initial values

UI hints (widgets, attributes)

Bind incoming data

Deserialize

Page 114: Integrating React.js with PHP projects

Typically PHP frameworks have a Form Component$form (e.g. Form Symfony Component)

Initial values

UI hints (widgets, attributes)

Bind incoming data

Deserialize

Validate

Page 115: Integrating React.js with PHP projects

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

Page 116: Integrating React.js with PHP projects

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

Page 117: Integrating React.js with PHP projects

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

Page 118: Integrating React.js with PHP projects

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

Page 119: Integrating React.js with PHP projects

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

Page 120: Integrating React.js with PHP projects

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

Page 121: Integrating React.js with PHP projects

…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)

Page 122: Integrating React.js with PHP projects

…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)

Page 123: Integrating React.js with PHP projects

…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)

Page 124: Integrating React.js with PHP projects

…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

Page 125: Integrating React.js with PHP projects

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, ...);};

Page 126: Integrating React.js with PHP projects

Forms rendered to HTML

$form->createView();

state.usuario

Page 127: Integrating React.js with PHP projects

Forms rendered to HTML

$form->createView();

state.usuario

Page 128: Integrating React.js with PHP projects

Forms rendered to HTML

$form->createView();

submit

Country: España

DeutschlandEspaña

Addresses:

Some St.-+state.usuario

Page 129: Integrating React.js with PHP projects

Forms rendered to HTML

$form->createView();

submit

Country: España

DeutschlandEspaña

Addresses:

Some St.-+state.usuario

POST well formed with country:’es’

and not ‘España’, ‘espana', ‘spain', ‘0’…

Page 130: Integrating React.js with PHP projects

Forms rendered to HTML

$form->createView();

$form->submit($request);

submit

Country: España

DeutschlandEspaña

Addresses:

Some St.-+state.usuario

POST well formed with country:’es’

and not ‘España’, ‘espana', ‘spain', ‘0’…

Page 131: Integrating React.js with PHP projects

state.usuario

Forms in APIs

$form;

Page 132: Integrating React.js with PHP projects

submit

Country: España

DeutschlandEspaña

Addresses:

Some St.-+state.usuario

Forms in APIs

$form; ✘How do we know the visible choices or values?

Read the docs!

Page 133: Integrating React.js with PHP projects

submit

Country: España

DeutschlandEspañ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!

Page 134: Integrating React.js with PHP projects

submit

Country: España

DeutschlandEspañ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!

Page 135: Integrating React.js with PHP projects

submit

Country: España

DeutschlandEspañ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!

Page 136: Integrating React.js with PHP projects

submit

Country: España

DeutschlandEspañ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!

Page 137: Integrating React.js with PHP projects

submit

Country: España

DeutschlandEspañ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!

Page 138: Integrating React.js with PHP projects
Page 139: Integrating React.js with PHP projects
Page 140: Integrating React.js with PHP projects

Define, maintain and keep in sync in triplicate

Form Server API docs Form Client

:_(How many devs does it take to write a form?

Page 141: Integrating React.js with PHP projects

Wizard type form?

“While you code this we will be preparing different versions for

other use cases”

Page 142: Integrating React.js with PHP projects

Case: Complex Wizard Form

Page 143: Integrating React.js with PHP projects

Case: Complex Wizard Form

Page 144: Integrating React.js with PHP projects

Case: Complex Wizard Form

Page 145: Integrating React.js with PHP projects

What we need

$form->createView();

HTML

API$mySerializer->serialize($form);

Page 146: Integrating React.js with PHP projects

What we need

$form->createView();

HTML

Serialize! Ok, into which format?

API$mySerializer->serialize($form);

Page 147: Integrating React.js with PHP projects

JSON Schema

Page 148: Integrating React.js with PHP projects

json-schema.org

Page 149: Integrating React.js with PHP projects

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

Page 150: Integrating React.js with PHP projects

Liform & LiformBundle

https://github.com/Limenius/Liform

use Limenius\Liform\Resolver;use Limenius\Liform\Liform;

$resolver = new Resolver();$resolver->setDefaultTransformers();$liform = new Liform($resolver);

$form = $this->createForm(CarType::class, $car, ['csrf_protection' => false]);$schema = json_encode($liform->transform($form));

Page 151: Integrating React.js with PHP projects

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', Type\TextType::class, [ 'label' => 'Name', 'required' => true, 'attr' => ['placeholder' => 'I\'m a placeholder’]])

->add('description', Type\TextType::class, [ 'label' => 'Description', 'liform' => [ 'widget' => 'textarea', 'description' => 'An explanation of the task', ]])

->add('dueTo', Type\DateTimeType::class, [ 'label' => 'Due to', 'widget' => 'single_text'] ) ; }

Page 152: Integrating React.js with PHP projects

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', Type\TextType::class, [ 'label' => 'Name', 'required' => true, 'attr' => ['placeholder' => 'I\'m a placeholder’]])

->add('description', Type\TextType::class, [ 'label' => 'Description', 'liform' => [ 'widget' => 'textarea', 'description' => 'An explanation of the task', ]])

->add('dueTo', Type\DateTimeType::class, [ 'label' => 'Due to', 'widget' => 'single_text'] ) ; }

Page 153: Integrating React.js with PHP projects

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', Type\TextType::class, [ 'label' => 'Name', 'required' => true, 'attr' => ['placeholder' => 'I\'m a placeholder’]])

->add('description', Type\TextType::class, [ 'label' => 'Description', 'liform' => [ 'widget' => 'textarea', 'description' => 'An explanation of the task', ]])

->add('dueTo', Type\DateTimeType::class, [ 'label' => 'Due to', 'widget' => 'single_text'] ) ; }

Page 154: Integrating React.js with PHP projects

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', Type\TextType::class, [ 'label' => 'Name', 'required' => true, 'attr' => ['placeholder' => 'I\'m a placeholder’]])

->add('description', Type\TextType::class, [ 'label' => 'Description', 'liform' => [ 'widget' => 'textarea', 'description' => 'An explanation of the task', ]])

->add('dueTo', Type\DateTimeType::class, [ 'label' => 'Due to', 'widget' => 'single_text'] ) ; }

Page 155: Integrating React.js with PHP projects

Transformers

Transformers extract information from each Form Field.

We can extract a lot of information: •Default values and placeholders. •Field attributes. •Validation rules.

Page 156: Integrating React.js with PHP projects

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":[] } }}

Page 157: Integrating React.js with PHP projects

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

Page 158: Integrating React.js with PHP projects

Form Server API docs Form Client

$form Json-schemaLiform

Page 159: Integrating React.js with PHP projects

Leverage json-schema: Validators

let valid = ajv.validate(schema, data)if (!valid) console.log(ajv.errors)

https://github.com/epoberezkin/ajv

Page 160: Integrating React.js with PHP projects

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)

Page 161: Integrating React.js with PHP projects

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.

Page 162: Integrating React.js with PHP projects

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> )}

Page 163: Integrating React.js with PHP projects

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"]}

Page 164: Integrating React.js with PHP projects

Form Server API docs Form Client

$form Json-schema React formLiform liform-react

Page 165: Integrating React.js with PHP projects

=: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

Page 166: Integrating React.js with PHP projects

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

Example with • Webpack • ReactRenderer/ReactBundle • Liform/LiformBudle & liform-react

Page 167: Integrating React.js with PHP projects

MADRID · NOV 27-28 · 2015

Thanks!@nacmartin

[email protected]

http://limenius.com Formation, Consulting and

Development.