Maintaining State in
Single-Page JavaScript Apps



NET/WORK — Feb 23, 2016

Alexandra Hoefinger

@ahoefinger

What’s a SPA?


  • A web app that loads a single HTML page and dynamically updates that page as the user interacts with the app
  • Users see different states of the page, and never experience a hard reload
  • Ex: Gmail and Twitter

What's a SPA


  • Pros
    • No page refresh, seamless UX
    • Better performance, especially mobile
  • Cons
    • Potentially huge file size
    • SEO issues
    • Maintaining state is really hard

What is state?


  • State is what a user sees at any given point, paired with the structure of the application's data
  • As a user interact with a page and DOM changes occur, more state is created
  • Ex: A site header

More State = More Problems


  • MVC patterns help store data and abstract state, but "It’s still very easy to make a mess of your app." — Henrik Joreteg
  • Managing this ever-changing state is hard. If a model can update another model, then a view can update a model, which updates another model, and this, in turn, might cause another view to update. At some point, you no longer understand what happens in your app as you have lost control over the when, why, and how of its state. When a system is opaque and non-deterministic, it’s hard to reproduce bugs or add new features. — Dan Abramov

Enter Redux!


Three Principles of Redux:


  • There is only one single source of truth
  • State is read-only
  • Changes are made with pure functions

Single Source of Truth


  • The state of your whole application is stored in an object tree within a single store
  • Top level properties are main components, populated with subcomponents
  • The app state can only be a permutation of values in the store, so Redux acts as a gatekeeper

Single Source of Truth


Example store:


{
    header: {
        isLoggedIn: true,
        selectedTab: 'About',
        displaySubnav: false,
        loginExpires: 1456102366207
    },
    gallery: {
        showFullText: false,
        showThumbnails: true,
        currentPage: 1,
        totalPages: 5
    }
}

State is Read-Only


  • The only way to mutate the state is to emit an action, an object describing what happened
  • Everything that happens in your app is an 'action'
  • "These can be caused by users, browser events, or server events. Doesn’t matter. Everything that changes something in your app does it via an action"

State is Read-Only


Example actions:

{
    type: 'INCREMENT_COUNTER',
    text: 'Hey we are incrementing our counter by one!',
    date: new Date().getTime()
}

{
    type: 'DECREMENT_COUNTER',
    text: 'Hey we are decrementing our counter by one!',
    date: new Date().getTime()
}

Changes are Made with Pure Functions


  • To specify how the state tree is transformed by actions, you write pure reducers.
  • Reducers are just pure functions that take the previous state and an action, and return the next state.
  • (A pure function is a function that, given the same input, will always return the same output and does not have any observable side effect.) — Professor Frisby's

Changes are Made with Pure Functions


Example reducer:


function counter(state, action) {
    if (typeof state === 'undefined') {
        return 0; 
    }

    switch (action.type) {
        case 'INCREMENT_COUNTER':
            return state + 1;
        case 'DECREMENT_COUNTER':
            return state - 1;
        default: 
            return state;
    }
}

Store Methods


  • store.getState();
  • store.subscribe();
  • store.dispatch();

To put it all together...




Thank You!


@ahoefinger

alexandra.hoefinger@gmail.com