Application state management

Flux is a pattern that Facebook uses for
building front-end web applications.


It complements React's components with
a unidirectional data flow.

Flux

Flux

Motivation...

Flux

Flux

Flux

Flux

Flux is not a library

It is just a pattern

a guideline...

The race had begun

Alt

DeLorean.js

Flummox

McFly

marty.js

RefluxJS

Fluxible

material-flux

Critical Mass votes for Redux

The race was won

🎉

Redux became the industry standard

🥇

Redux Toolkit is an improvement

🥇

implementation

Single source of truth

all application state is stored as
a single
 JS object

store

1st Principle 

The state tree is immutable 
(Read-only)

You cannot write to or modify the app state directly

store

2nd Principle 

To modify application state You need to dispach an   Action

Actions are payloads of information
They are the
only source of data of the store.

const ADD_TODO = 'ADD_TODO'

{
  type: ADD_TODO,
  text: 'Build my first Redux app'
}

Action Creators are functions
that create and return actions

const ADD_TODO = 'ADD_TODO'

function addTodo(text) {
  return {
    type: ADD_TODO,
    text: text
  }
}

3rd Principle                            Changes are made with pure functions

A reducer is a pure function that takes the previous state and an action, and returns the next state.

( previousState ,  action ) =>  newState

 Reducers   Handle Actions

Handling Actions in Reducers

let initialState = {
      todos: [],
      visibilityFilter: 'ALL'
}

function todosReducer(reducerState = initialState, action){

  switch (action.type) {

    case SET_VISIBILITY_FILTER:
      return {
        ...reducerState,
        visibilityFilter: action.filter
      }

    case ADD_TODO:
      return {
        ...reducerState, 
        todos: [...reducerState.todos,{ text: action.text,
                                 completed: false }]
      }

    default:
      return reducerState

  }
}

Combine Reducers

reducers/index.js


import { combineReducers } from 'redux'
import todosReducer from './todos.reducer.js'
import countReducer from './count.reducer.js'

const  rootReducer = combineReducers({
  todos : todosReducer,
  count : countReducer
})


export default rootReducer

To create the    store   instance
use the redux method createStore()

createStore() requires a root reducer as 1st param
and optionally - an initialState and an enhancer  

import { createStore, applyMiddleware } from 'redux'
import   rootReducer   from './reducers'
 
let store = createStore(rootReducer , 
                        {count:8} , 
                        applyMiddleware(...))

applyMiddleware(...middleware)

Middleware are functions used to extend Redux
They are composable by design
Each function requires no knowledge of what comes before or after it in the chain.

Home made Middleware

Middleware functions have the following signature:

import { createStore, applyMiddleware } from 'redux'
import   rootReducer   from './reducers'



const logger = store => next => action => {
    console.log('dispatching', action)
    next(action)
    console.log('next state', store.getState()
}


let store = createStore(rootReducer , 
                        {count:8} , 
                        applyMiddleware(logger) )

Home made Middleware

const logger = store => next => action => {
  console.log('dispatching', action)
  next(action)
  console.log('next state', store.getState())
}
function logger(store) {
  return function(next) {
    return function(action) {
      console.log('dispatching', action)
      next(action)
      console.log('next state', store.getState())
    }
  }
}

Same as writing:

3rd party Middleware

Same function signature as home-made middleware
serve as Redux 'plugins' and available as npm packages

import { createStore, applyMiddleware } from 'redux'
import   thunk   from 'redux-thunk'
import   rootReducer   from './reducers'
import   logger   from './my-middleware/logger'





let store = createStore(rootReducer , 
                        {count:8} , 
                        applyMiddleware(thunk,logger) )

regular Action Creator functions
create and return action objects

const ADD_TODO = 'ADD_TODO'

function addTodo(text) {
  return {
    type: ADD_TODO,
    payload: text
  }
}

dispatching async actions with
redux-thunk middleware

Async action creators return a callback with a promise
The callback will receive the store
dispatch
 method as a parameter
* The following code will work only if redux-thunk middleware was used at the store creation

 
function fetchPosts(topic) {
   return async (dispatch)=> {
        try{
             dispatch(fetch_posts_start(topic))   // START     
             const url = `https://myblog.com/api/${topic}`
             const response = await fetch(url)
             const data = await response.json()
             dispatch(fetch_posts_success(topic, data)) // SUCCESS 
        }catch(error){
             dispatch(fetch_posts_failed(error)) // FAILED 
        }
   }
}

applyMiddleware(...middleware)

Middleware are functions used to extend Redux
They are composable by design
Each function requires no knowledge of what comes before or after it in the chain.

tightly coupled with React

You can write Redux apps with React, Angular, Ember, jQuery, or vanilla JavaScript.

is not 

Official React bindings for Redux

<Provider store> 

Injects the store to your app

hooks API

The hooks API is an elegant way to interact with the store from react
Recommended!

Connect(...)

HOC connect to store from within react component outdated,
not recommended...

<Provider store> 

Wraps the root component,
Providing access to the Redux store

ReactDOM.render(
  <Provider store={store}>
    <MyRootComponent />
  </Provider>,
  document.getElementById('root')
)

hooks API

Rerender on store state changes 

Affect the store state

import React from 'react'
import { useSelector, useDispatch } from 'react-redux'

export const CounterComponent = () => {

  const counter = useSelector(state => state.counter)
  const dispatch = useDispatch()

  return (
    <div>
      <span>{counter}</span>
      <button onClick={() => dispatch({ type: 'increment' })}>
        Increment counter
      </button>
    </div>
  )
}

Connect(...)

  • Connects a React component to a Redux store.
  • Components connected to a Redux store
    are typically called Containers
  • Whenever the store will update, so will the container
import { connect } from 'react-redux'

let ProfilePage = () => {
  return (
    <div>
      ...
    </div>
  )
}
export default connect(...)(ProfilePage)

update the view with
mapStateToProps( state )

To use connect(), define a mapStateToProps function

It maps the parts of the store state your container cares about
into props it will pass down to its child components

import { connect } from 'react-redux'

let ListView = ({ active }) => {
  return <div>...</div>
}

const mapStateToProps = (state, ownProps) => {
  return {
    active: ownProps.filter === state.visibilityFilter
  }
}
export default connect(mapStateToProps)(ListView)
import { someActionCreator,increment,decrement,toggleTodo} from './actions'

let ListView = ({ doSomething, doIncrement, doDecrement, onTodoClick}) => {
  return <div>...</div>
}
const mapDispatchToProps = (dispatch, ownProps) => {
  return {
     doSomething: ()=>     dispatch( someActionCreator() )
     doIncrement: ()=>     dispatch( increment() ),
     doDecrement: ()=>     dispatch( decrement() ),
     onTodoClick: (id)=>   dispatch( toggleTodo(id) )
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(ListView)

dispatching actions with
mapDispatchToProps( dispatch)

Optional  second parameter of connect(),
It recieves dispatch() as a param and returns callback props
your container will typically inject down to its child components
* if not implemented, containers will have
dispatch() available via props

Redux

By Yariv Gilad

Redux

A better way to manage application state

  • 1,982