nate abele "un-dux your front-end"

Post on 22-Jan-2018

85 Views

Category:

Technology

5 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Un-Dux Your Front-EndNate AbeleAdvisor Innovation Labs

UN-DUX YOUR FRONT-ENDNATE ABELE

THE UNREASONABLE EFFECTIVENESS OF DATA

OR…

UN-DUX YOUR FRONT-END

YOUR HOST: NATE ABELE

▸ Some PHP frameworks (Li3, CakePHP)

▸ AngularUI Router

▸ Architect @ AI Labs

▸ @nateabele

▸ natea@advisorinnovationlabs.com

UN-DUX YOUR FRONT-END

ADVISOR INNOVATION LABS

CONCEPTS

CONCEPTS

STATE

< Component />

< Component />

< Component /> < Component />

< Component />

< Component /> < Component />

< Component />

< Component />

< Component /> < Component />

< Component /> < Component /> < Component />

< Component />

< Component /> < Component />

< Component /> < Component /> < Component />

< Component />

< Component /> < Component />

< Component /> < Component /> < Component />

< Component />

< Component /> < Component />

< Component /> < Component /> < Component />

UN-DUX YOUR FRONT-END

REDUX

▸ Single-value store

▸ Changes (actions) are simple values

▸ Few opinions

SETTING UP

$ yarn add

SETTING UP

$ yarn add reduxreact-reduxreact-router-reduxredux-loggerredux-sagaredux-mock-storeredux-immutableredux-thunkredux-promise-middlewarereact-redux-formredux-actredux-saga-test-planredux-queryredux-saga-async...

function todoApp(state = initialState, action) {

switch (action.type) {

case SET_VISIBILITY_FILTER:

return Object.assign({}, state, {

visibilityFilter: action.filter

})

default:

return state

}

}

ELMELM

CASIUM

import React from 'react';

import { container } from 'casium';import Message from 'casium/message';

class Increment extends Message {}class Decrement extends Message {}

export default container({

init: () => ({ count: 0 }),

update: [ [Increment, ({ count }) => ({ count: count + 1 })], [Decrement, ({ count }) => ({ count: count - 1 })] ],

view: ({ emit, count }) => ( <div> <button onClick={emit(Decrement)}> - </button> { count } <button onClick={emit(Increment)}> + </button> </div> )});

import React from 'react';

import { container } from 'casium';import Message from 'casium/message';

class Increment extends Message {}class Decrement extends Message {}

export default container({

init: () => ({ count: 0 }),

update: [ [Increment, ({ count }) => ({ count: count + 1 })], [Decrement, ({ count }) => ({ count: count - 1 })] ],

view: ({ emit, count }) => ( <div> <button onClick={emit(Decrement)}> - </button> { count } <button onClick={emit(Increment)}> + </button> </div> )});

import React from 'react';

import { container } from 'casium';import Message from 'casium/message';

class Increment extends Message {}class Decrement extends Message {}

export default container({

init: () => ({ count: 0 }),

update: [ [Increment, ({ count }) => ({ count: count + 1 })], [Decrement, ({ count }) => ({ count: count - 1 })] ],

view: ({ emit, count }) => ( <div> <button onClick={emit(Decrement)}> - </button> { count } <button onClick={emit(Increment)}> + </button> </div> )});

import React from 'react';

import { container } from 'casium';import Message from 'casium/message';

class Increment extends Message {}class Decrement extends Message {}

export default container({

init: () => ({ count: 0 }),

update: [ [Increment, ({ count }) => ({ count: count + 1 })], [Decrement, ({ count }) => ({ count: count - 1 })] ],

view: ({ emit, count }) => ( <div> <button onClick={emit(Decrement)}> - </button> { count } <button onClick={emit(Increment)}> + </button> </div> )});

RAMDAJS

import React from 'react';import { evolve } from 'ramda';

import { container } from 'casium';import Message from 'casium/message';

class Increment extends Message {}class Decrement extends Message {}

export default container({

init: () => ({ count: 0 }),

update: [ [Increment, evolve({ count: ct => ct + 1 })], [Decrement, evolve({ count: ct => ct - 1 })] ],

view: ({ emit, count }) => ( <div> <button onClick={emit(Decrement)}> - </button> { count } <button onClick={emit(Increment)}> + </button> </div> )});

import React from 'react';import { evolve } from 'ramda';

import { container } from 'casium';import Message from 'casium/message';

class Increment extends Message {}class Decrement extends Message {}

export default container({

init: () => ({ count: 0 }),

update: [ [Increment, evolve({ count: ct => ct + 1 })], [Decrement, evolve({ count: ct => ct - 1 })] ],

view: ({ emit, count }) => ( <div> <button onClick={emit(Decrement)}> - </button> { count } <button onClick={emit(Increment)}> + </button> </div> )});

import React from 'react';import { evolve, inc, dec } from 'ramda';

import { container } from 'casium';import Message from 'casium/message';

class Increment extends Message {}class Decrement extends Message {}

export default container({

init: () => ({ count: 0 }),

update: [ [Increment, evolve({ count: inc })], [Decrement, evolve({ count: dec })] ],

view: ({ emit, count }) => ( <div> <button onClick={emit(Decrement)}> - </button> { count } <button onClick={emit(Increment)}> + </button> </div> )});

import { merge } from 'ramda';/* … */

class UpdateForm extends Message {}class SignIn extends Message {}

export default container({ init: () => ({ email: '', password: '' }),

update: [[UpdateForm,(model, { key, value }) => merge(model, { [key]: value })

]],

view: ({ emit, email, password }) => ( <form> <input type='email' value={email}

onChange={emit([UpdateForm, { key: 'email' }])} /> <input type='password' value={password}

onChange={emit([UpdateForm, { key: 'password' }])} /> </form> )});

import { Http } from 'casium/commands';/* … */

class SignIn extends Message {}class SignInSuccess extends Message {}class SignInError extends Message {}

export default container({ init: () => ({ email: '', password: '', loading: false }),

update: [ [UpdateForm, (model, { key, value }) => /* … */],

[SignIn, model => [merge(model, { loading: true }),new Http.Post({url: '/login',data: { email: model.email, password: model.password },result: SignInSuccess,error: SignInError

})]]

],

view: ({ emit, email, password }) => ( <form onSubmit={emit(SignIn)}> /* … */ </form> )});

import { Http } from 'casium/commands';/* … */

class SignInSuccess extends Message {}class SignInError extends Message {}

export default container({ init: () => ({ email: '', password: '', loading: false }),

update: [ /* … */

[SignInSuccess, (model, { data }) => /* Handle the response… */][SignInError, (model, { status }) => /* Handle error… */]

],

view: ({ emit, email, password }) => ( <form onSubmit={emit(SignIn)}> /* … */ </form> )});

import { Post, formData } from 'casium/commands/http';

export default class SignIn extends Post {

constructor({ email, password, ...params }) { const id = 'my-app', secret = 'woo-sekrit';

super({ url: '/oauth/token', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Basic ' + btoa(id + ':' + secret) }, data: formData({ username: email, password, grant_type: 'password', scope: 'read write', client_secret: secret, client_id: id }), ...params }); }}

import { Post, formData } from 'casium/commands/http';import { SignInSuccess, SignInError } from './sign_in_container';

export default class SignIn extends Post {

constructor({ email, password, ...params }) { const id = 'my-app', secret = 'woo-sekrit';

super({ url: '/oauth/token', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Basic ' + btoa(id + ':' + secret) }, data: formData({ username: email, password, grant_type: 'password', scope: 'read write', client_secret: secret, client_id: id }),

result: SignInSuccess,error: SignInError

...params }); }}

import { Http } from 'casium/commands';/* … */

export default container({ init: () => ({ email: '', password: '', loading: false }),

update: [ /* … */

[SignInSubmit, model => [merge(model, { loading: true }),new Http.Post({url: '/login',data: { email: model.email, password: model.password },result: SignInSuccess,error: SignInError

})]]

],

view: ({ emit, email, password }) => ( <form onSubmit={emit(SignInSubmit)}> /* … */ </form> )});

import { SignIn } from ‘./messages/sign_in’;/* … */

export default container({ init: () => ({ email: '', password: '', loading: false }),

update: [ /* … */

[SignInSubmit, model => [merge(model, { loading: true }),new SignIn({ email: model.email, password: model.password })

]] ],

view: ({ emit, email, password }) => ( <form onSubmit={emit(SignInSubmit)}> /* … */ </form> )});

import { SignIn } from ‘./messages/sign_in’;import { container, seq } from 'casium';/* … */

export default container({ init: () => ({ email: '', password: '', loading: false }),

update: [ /* … */

[SignInSubmit, seq(model => merge(model, { loading: true }),model => [model, new SignIn({ email: model.email, password: model.password})]

)] ],

view: ({ emit, email, password }) => ( <form onSubmit={emit(SignInSubmit)}> /* … */ </form> )});

import { merge, pick } from 'ramda';import { SignIn } from ‘./messages/sign_in’;import { container, seq, replace, commands } from 'casium';/* … */

export default container({ init: () => ({ email: '', password: '', loading: false }),

update: [ /* … */

[SignInSubmit, seq(replace({ loading: true }),commands(SignIn, pick(['email', 'password']))

)] ],

view: ({ emit, email, password }) => ( <form onSubmit={emit(SignInSubmit)}> /* … */ </form> )});

WHY?

GUARANTEES

EXAMPLE: PROMISES

GUARANTEES

‘PROBABLY’

$ git checkout b69c907

$ yarn test

[DEMO]

UN-DUX YOUR FRONT-END

REVIEW

▸ Diff view of changes

▸ Generated unit tests

▸ Time travel

▸ Message log export

▸ Websockets magic

UN-DUX YOUR FRONT-END

IMPLICATIONS

▸ Crossing boundaries

▸ Device farm

▸ Reproduce production errors

▸ Watch / replay user sessions

▸ Isolation

▸ Browser testing

▸ API testing

UN-DUX YOUR FRONT-END

IMPLICATIONS

▸ Diff analysis

UN-DUX YOUR FRONT-END

Sign Up.json

Log In.json

Set Prefs.json

UN-DUX YOUR FRONT-END

Sign Up.json

Log In.json

Set Prefs.json

$ webpack my-app.js

$ webpack my-app.js> my-app-df3db62731a0a32ef0c6.js

UN-DUX YOUR FRONT-END

df3db6273+

UN-DUX YOUR FRONT-END

Sign Up.json

Log In.json

+

+

df3db6273

df3db6273

UN-DUX YOUR FRONT-END

TRY CASIUM

▸ /ai-labs-team/casium

▸ /ai-labs-team/casium-devtools

▸ casium.io

THANKS!

QUESTIONS?

UN-DUX YOUR FRONT-END

CONTACT

▸ /nateabele

▸ @nateabele

▸ natea@advisorinnovationlabs.com

PHOTO CREDITS

▸ ‘This is not a pipe’: https://www.threadless.com/product/543/this_is_not_a_pipe

▸ ‘Cow’: https://www.emaze.com/@AWCTOFZ

▸ ‘Steak’: http://www.chicagomeat.com/products/t-bone-steak/

▸ ‘McDonald’s Hamburger’: https://www.businessinsider.com.au/man-saves-mcdonalds-burger-for-5-years-2014-7

▸ ‘Texas’: http://yalsa.ala.org/blog/2014/02/25/virtual-road-trip-texas-part-2/

▸ ‘DeLorean’: https://www.pinterest.com/pin/114419646753355072/

▸ ‘Redux Architecture’: https://medium.com/mofed/react-redux-architecture-overview-7b3e52004b6e

top related