Why the Flux?!

Resolving to really understand state management in 2018

Rob Ocel - Developer, PointSource - @robocell

In 20 Minutes Or Less

Goals

Never heard of or used state management patterns?

Using a state management library but feeling confused?

Are you a state management expert?

State Libraries Are Ubiquitous

  • Angular - ngrx/store, ng-redux
  • React - Redux, MobX, Unstated, context, setState
  • Vue - Vuex
  • Preact - Unistore
But everything changed when the Fire Nation attacked...
So what's the deal?
People get lost in Redux syntax without understanding the Redux pattern
So what's the answer? What's the pattern we're supposed to understand?

Unidirectional Data Flow

Data down; events up

State

  • Globally - Everything that differentiates one instance of an application from another
  • Locally - What a component knows better than everyone else
  • State Ownership
    • Only one component owns a piece of state across the application
    • That component is responsible for fetching, changing, passing it

When state ownership goes wrong?

  • Components hold duplicate state (tough to synchronize)
  • Hard to reason about where state is mutated
  • Facebook's phantom notification

Props

  • Passing Data - Parent-To-Child
  • Immutable
  • Loss of Fidelity
                    
                        function BoilingVerdict(props) {
                            if (props.celsius >= 100) {
                                return 

Water would boil.

; } return

Water would not boil.

; }

Events

  • Passing Data - Child-To-Parent
  • Reflect External Input (Not Just User Input)
  • Can add context to the input
                    
                        

                        Vue.component("button-counter", {
                            template: ``,
                            props: ["amount"],
                            data: () => ({
                                counter: 0
                            }),
                            methods: {
                                incrementCounter: function() {
                                    this.counter += this.amount;
                                }
                            }
                        });
                    
                

Data Hoisting/Prop Drilling

  • When a component needs data that none of it's parents have (but someone else owns)
  • Move the data to the first shared ancestor of all components that need it
  • Pipe the data to the components through props (prop drilling)
  • To edit the data, pipe events back to the new data owner
                    
                        Vue.component("button-counter", {
                            template: ``,
                            props: ["counter", "amount"],
                            methods: {
                                incrementCounter: function() {
                                    this.$emit("increment", this.amount);
                                }
                            }
                        });
                    
                

What happens when our tree gets larger?

What happens if we had more state to share?

Flux/Redux

  • Developed by Facebook
  • Pattern; not library
  • Method for managing unidirectional data flow
  • Flux !== Redux (but close enough for this talk)

Store

  • All of the state for the application
  • Allows actions to be dispatched
  • Signals when state is updated
                    
                            {
                                selectedSubreddit: 'frontend',
                                postsBySubreddit: {
                                    frontend: {
                                        isFetching: true,
                                        didInvalidate: false,
                                        items: []
                                    },
                                    reactjs: {
                                        isFetching: false,
                                        didInvalidate: false,
                                        lastUpdated: 1439478405547,
                                        items: [{
                                                id: 42,
                                                title: 'Confusion about Flux and Relay'
                                        },
                                        {
                                            id: 500,
                                            title: 'Creating a Simple Application'
                                        }]
                                    }
                                }
                            }
                    
                

Actions

  • Parameterized events
  • Signal intent to change state
  • Every input is an opportunity for a new action
  • Comprised of:
    • Type
    • Data
                    
                        { type: 'LIKE_ARTICLE', articleId: 42 }
                        { type: 'ADD_TODO', text: 'Read the Redux docs.' }
                        { type: 'FETCH_POSTS_REQUEST' }
                        { type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
                        { type: 'FETCH_POSTS_SUCCESS', response: { ... } }
                    
                

Reducers

  • (Action, State) => State
  • Synchronous
  • Pure (in some libraries)
                    
                        function reducer(state = initialState, action) {
                            switch (action.type) {
                                case 'ADD_TODO':
                                    return Object.assign({}, state, {
                                        todos: [...state.todos, {
                                            text: action.text
                                        }]
                                    });
                                case 'SET_VISIBILITY_FILTER':
                                    return Object.assign({}, state, {
                                        visibilityFilter: action.filter
                                    });
                                default:
                                    return state;
                            }
                        }
                    
                

Takeaways:

  • Layout your problem in a library-agnostic way using state-management principles
    • State Machines (Develop your state tree)
    • User Flows (Figure out your actions)
    • Business Logic (Design your reducers and side-effects)
    • Assign Data Ownership
  • Map your approach to the syntax of your library
  • Don’t be afraid to just take a stab at it and iterate later

Links