Top Banner
Borrowing the best of the web to make native better Brandon Kase & Christina Lee
76

Reduxing UI: Borrowing the Best of Web to Make Android Better

Apr 13, 2017

Download

Technology

CHRISTINA LEE
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Reduxing UI: Borrowing the Best of Web to Make Android Better

Borrowing the best of the webto make native better

Brandon Kase & Christina Lee

Page 2: Reduxing UI: Borrowing the Best of Web to Make Android Better

Why are we here?Let me tell you a story!

about a buttona complicated button

Page 3: Reduxing UI: Borrowing the Best of Web to Make Android Better

Take 1:boss asks for friend buttonwe say, 'OK! We'll do it this week!'we do not finish it in a weekwoah...this is very complicated!boss is less than thrilled

Page 4: Reduxing UI: Borrowing the Best of Web to Make Android Better

Take 2:boss asks for friend buttonwe say, 'OK! We'll do it this week!'we modify our approachwe do not finish it in a weekwe finish it in a day!boss is thrilledBrandon and Christina still have jobs, hurray!

Page 5: Reduxing UI: Borrowing the Best of Web to Make Android Better

What went wrongFetch data before view transitionsOptimistically update componentsSend server requests and react to responses

Page 6: Reduxing UI: Borrowing the Best of Web to Make Android Better

Side EffectsTouch global state, make network requestsSide-effects are bad, but necessary

Page 7: Reduxing UI: Borrowing the Best of Web to Make Android Better

Most Common PitfallsMutabilityAsynchronicity

Page 8: Reduxing UI: Borrowing the Best of Web to Make Android Better

As developers, we are expected to handleoptimistic updates, server- side rendering,

fetching data before performing routetransitions, and so on... This complexity is

difficult to handle as we’re mixing twoconcepts that are very hard for the human

mind to reason about: mutation andasynchronicity. I call them Mentos and Coke”

Motivation | Redux

Page 9: Reduxing UI: Borrowing the Best of Web to Make Android Better
Page 10: Reduxing UI: Borrowing the Best of Web to Make Android Better

Luckily, we are not alone

Page 11: Reduxing UI: Borrowing the Best of Web to Make Android Better

Web AlliesThe web faces all of these challenges and moreUnlike native; however, it's iteration rate is fastNot "one" UI framework

Page 12: Reduxing UI: Borrowing the Best of Web to Make Android Better

So what did the web come up with?

Page 13: Reduxing UI: Borrowing the Best of Web to Make Android Better

Ways to manage side-effectsView

State

Page 14: Reduxing UI: Borrowing the Best of Web to Make Android Better

Ways to manage side-effectsSeparation of concernsMutability and asynchronicity are decoupled

Page 15: Reduxing UI: Borrowing the Best of Web to Make Android Better
Page 16: Reduxing UI: Borrowing the Best of Web to Make Android Better

Flux

Page 17: Reduxing UI: Borrowing the Best of Web to Make Android Better

Redux// The current application state (list of todos and chosen filter) let previousState = { visibleTodoFilter: 'SHOW_ALL', todos: [ { text: 'Read the docs.', complete: false } ] }

// The action being performed (adding a todo) let action = { type: 'ADD_TODO', text: 'Understand the flow.' }

// Your reducer returns the next application state let nextState = todoApp(previousState, action)

-- Dan Abaramov's Data Flow example in Redux

Page 18: Reduxing UI: Borrowing the Best of Web to Make Android Better

Cycle.js

Page 19: Reduxing UI: Borrowing the Best of Web to Make Android Better

Cycle.jsinputs = read effects you care aboutoutputs = write effects you want performedall application logic is pure!

Page 20: Reduxing UI: Borrowing the Best of Web to Make Android Better

Cycle.js

Page 21: Reduxing UI: Borrowing the Best of Web to Make Android Better

from cycle.js.org

Page 22: Reduxing UI: Borrowing the Best of Web to Make Android Better

Common TraitsUnidirectional and circular data flowsSeparation of concerns

Page 23: Reduxing UI: Borrowing the Best of Web to Make Android Better

How do we benefit on Android?

Page 24: Reduxing UI: Borrowing the Best of Web to Make Android Better

Logic is made easyImplicit data flow of your app becomes explicit.Immutable views of a mutable world

Debugging is made easyAll edge cases caught at compile-time.Single source of truth.Time Travel

Page 25: Reduxing UI: Borrowing the Best of Web to Make Android Better

How can you adopt thisView:

React nativeAnvil

State:

Direct port of Redux/Cycle/etc.

Page 26: Reduxing UI: Borrowing the Best of Web to Make Android Better

We focused on StateDon't have to fight Android's UI frameworkEasy to introduce

Page 27: Reduxing UI: Borrowing the Best of Web to Make Android Better
Page 28: Reduxing UI: Borrowing the Best of Web to Make Android Better

Native (kotlin)Single-Atom-State (like redux)Pure Functional Reactive (like cycle)Composable (like cycle)

Page 29: Reduxing UI: Borrowing the Best of Web to Make Android Better

Aside: RxJava

Page 30: Reduxing UI: Borrowing the Best of Web to Make Android Better

Aside: RxJavaReactive programming is programming with

asynchronous data streams

Andre Staltz

Global event emitter << Event buses << Data stream

Page 31: Reduxing UI: Borrowing the Best of Web to Make Android Better

Aside: RxJavaWhen we say streams, we mean push-based event streams,

not pull-based infinite list streams

Page 32: Reduxing UI: Borrowing the Best of Web to Make Android Better

Aside: RxJavaWhat can this look like in practice?

Streams of button tapsStreams of snapshots of changing dataStreams from network responses

Page 33: Reduxing UI: Borrowing the Best of Web to Make Android Better

Aside: RxJavaRxJava

Reactive programming with streamsTools to combine and transform those streams

Page 34: Reduxing UI: Borrowing the Best of Web to Make Android Better

Aside: RxJava

from RxMarbles

Page 35: Reduxing UI: Borrowing the Best of Web to Make Android Better

Aside: RxJava

from RxMarbles

Page 36: Reduxing UI: Borrowing the Best of Web to Make Android Better

Aside: RxJavaObservable.just(1,2,3)

Page 37: Reduxing UI: Borrowing the Best of Web to Make Android Better

Example

Page 38: Reduxing UI: Borrowing the Best of Web to Make Android Better

Example

0:09 0:14

Not just a counter

Page 39: Reduxing UI: Borrowing the Best of Web to Make Android Better

Not just a counter

Page 40: Reduxing UI: Borrowing the Best of Web to Make Android Better
Page 41: Reduxing UI: Borrowing the Best of Web to Make Android Better

1. View-Model Statedata class /*View-Model*/ State( val numLikes: Int, val numComments: Int, val showNewHighlight: Boolean, val imgUrl: String?, val showUndo: Boolean )

Page 42: Reduxing UI: Borrowing the Best of Web to Make Android Better
Page 43: Reduxing UI: Borrowing the Best of Web to Make Android Better

2. View Intentions// Mode is either tapped or untapped data class ViewIntentions( val photos: Observable<Photo>, val modes: Observable<Mode.Sum>, val globalReadTs: Observable<Long> )

Page 44: Reduxing UI: Borrowing the Best of Web to Make Android Better
Page 45: Reduxing UI: Borrowing the Best of Web to Make Android Better

3. Model State// Mode is either tapped or untapped data class /*Model*/ State( val photo: Photo?, val isNew: Boolean, val mode: Mode.Sum, ): RamState<...>

val initialState = State( photo = null, isNew = false, mode = Mode.untapped )

Page 46: Reduxing UI: Borrowing the Best of Web to Make Android Better

4. View Intentions => Model State ChangesState changes? We want functional code. We want

immutability.

Think of a state change as a function

func change(currentState: State) -> State /*nextState */

Page 47: Reduxing UI: Borrowing the Best of Web to Make Android Better

4. View Intentions => Model State Changesval model: (ViewIntentions) -> Observable<(State) -> State> = { intentions -> val modeChanges: Observable<(State) -> State> = intentions.modes.map{ /*...*/ }

val photoChanges: Observable<(State) -> State> = intentions.photos.map{ /*...*/ }

val tsChanges: Observable<(State) -> State> = intentions.globalReadTs.map{ /*...*/ }

Observable.merge( modeChanges, photoChanges, tsChanges) }

Page 48: Reduxing UI: Borrowing the Best of Web to Make Android Better

4. View Intentions => Model State Changesval modeChanges: Observable<(State) -> State> = intentions.modes.map{ mode -> { state: State -> State(state.photo, state.isNew, mode) } }

Page 49: Reduxing UI: Borrowing the Best of Web to Make Android Better
Page 50: Reduxing UI: Borrowing the Best of Web to Make Android Better

5. Model State => View-Model Stateval viewModel: (Observable<Model.State>)->Observable<ViewModel.State> = { stateStream -> stateStream .map{ state -> val undoable = state.mode == Mode.tapped val likes = state.photo?.like_details ?: emptyList() val comments = state.photo?.comments ?: emptyList() ViewModel.State( numLikes = likes.sumBy { it.multiplier }, numComments = comments.count, showNewHighlight = state.isNew, imgUrl = /* ... */, showUndo = /*...*/ ) } }

Page 51: Reduxing UI: Borrowing the Best of Web to Make Android Better
Page 52: Reduxing UI: Borrowing the Best of Web to Make Android Better

6. View-Model => Mutate the Viewclass PhotoComponent( viewIntentions: ViewIntentions, view: PhotoCellView ): StartStopComponent by Component( driver = /* ... */, model = /* ... */ )

Page 53: Reduxing UI: Borrowing the Best of Web to Make Android Better

6. View-Model => Mutate the Viewdriver = ViewDriver<ViewIntentions, ViewModel.State>( intention = viewIntentions, onViewState = { old, state -> if (old?.imgUrl != state.imgUrl) { view.setImg(state.imgUrl) } /* ... */ } ),

Page 54: Reduxing UI: Borrowing the Best of Web to Make Android Better

6. View-Model => Mutate the Viewmodel = ViewDriver.makeModel( initialState = Model.initialState, createState = Model.createState, model = Model.model, viewModel = ViewModel.viewModel )

Page 55: Reduxing UI: Borrowing the Best of Web to Make Android Better

Stick it in a recycler-view, hook up the side-effects into viewintentions and you're done

Page 56: Reduxing UI: Borrowing the Best of Web to Make Android Better
Page 57: Reduxing UI: Borrowing the Best of Web to Make Android Better

ViewIntentionsThe inputs to your componentThe photo, the mode, the tap timestamp

ModelTransform the inputs into state changesChange mode, change isNew, change photo

ViewModelTransform model state to view-model stateExtract photo url, like counts, etc

ComponentApply mutations to your view based on your view-modelUse the View-Model to change the underlying Androidview

Page 58: Reduxing UI: Borrowing the Best of Web to Make Android Better

Under the hoodEnforce viewintentions/model/view-model structureRxJava does heavy-liftingand a magic scan

Page 59: Reduxing UI: Borrowing the Best of Web to Make Android Better

Under the hood

Page 60: Reduxing UI: Borrowing the Best of Web to Make Android Better

Implementation of Redux in one-linemodelStream.scan(initialState, { currentState, transform -> transform(currentState) })

Page 61: Reduxing UI: Borrowing the Best of Web to Make Android Better

BonusCycle.js-like side-effect driversConfigurable model state persistance within stateAuto-start and stop components onPause/onResume

Page 62: Reduxing UI: Borrowing the Best of Web to Make Android Better

Just like cycle

read effects are inputswrite effects are outputs

Effects are decoupled from business logic

Page 63: Reduxing UI: Borrowing the Best of Web to Make Android Better

What does it look like in production?

Page 64: Reduxing UI: Borrowing the Best of Web to Make Android Better

Results: The GoodIt wouldn't compile

Page 65: Reduxing UI: Borrowing the Best of Web to Make Android Better

Results: The GoodWhen it did compile, it worked!

Page 66: Reduxing UI: Borrowing the Best of Web to Make Android Better

Results: The GoodIncredibly modular and composableLEGO-like plug-and-play

Page 67: Reduxing UI: Borrowing the Best of Web to Make Android Better

Results: The GoodEasy to test (by hand + by unit test) & debugREALLY EASYMocking inputs is trivialUI component is defined ONLY by it's state

Page 68: Reduxing UI: Borrowing the Best of Web to Make Android Better

Results: The GoodEasy to maintainSpec change?

(possibly) add an input streamadd another map in the model

Page 69: Reduxing UI: Borrowing the Best of Web to Make Android Better

Results: The BadRamp up necessary

Page 70: Reduxing UI: Borrowing the Best of Web to Make Android Better

Results: The BadAnimations are hard

chase or interpolate underlying state?probably additive animations (google it)

Page 71: Reduxing UI: Borrowing the Best of Web to Make Android Better

Results: The Bad?Boiler plate

(screenshot of files)

Always the 4 piecescyklic repo has counter example in one file

Page 72: Reduxing UI: Borrowing the Best of Web to Make Android Better

Results: The SurprisingIt's actually not slow

No noticeable perf hit

Page 73: Reduxing UI: Borrowing the Best of Web to Make Android Better

Results: The Conclusion

Page 74: Reduxing UI: Borrowing the Best of Web to Make Android Better

Results: The ConclusionWe have more powerful tools now

(i.e. Kotlin + Functional programming)

Let's use them

Question everything

Page 76: Reduxing UI: Borrowing the Best of Web to Make Android Better

ThanksBrandon Kase Christina Lee

[email protected] [email protected]

@bkase_ @runchristinarun

bkase.com

Github: bkase/cyklic