mail: vnovick@gmail.com
twitter: @VladimirNovick
github: vnovick
facebook: vnovickdev
Redux
Middleware is the suggested way to extend Redux with custom functionality. Middleware lets you wrap the store’s dispatch method for fun and profit
import createLogger from 'redux-logger'
import { createStore } from 'redux'
let store = createStore(rootReducer,
    applyMiddleware(thunk, createLogger())
)const applyStateTransform = (action) => {
  let { transform, state } = action;
  return transform ? transform(state) || state : state
}
const fetcher = store => next => action => {
  if (action.type === '@@fetcher/FETCH'){
    let { url, method, resolve, reject, error } = action.state;
    fetch(url, {
      method
    }).then((result)=> {
      let resolveAction = resolve(result);
      let errorAction = error(result);
      return result.ok ?
        next({
          type: `@@fetcher/${resolveAction.type}`,
          state: applyStateTransform(resolveAction)
        }) :
        next({
          type: `@@fetcher/${errorAction.type}`,
          state: applyStateTransform(errorAction)
        })
    }).catch((err) => {
      let rejectAction = reject(err);
      return next({
        type: `@@fetcher/${rejectAction.type}`,
        state: applyStateTransform(rejectAction)
      })
    });
  }
  return next(action)
}
////Fetch Action Creator
export const fetchFromServer = (url, method) => dispatch => {
  dispatch({
    type: '@@fetcher/FETCH',
    state: {
      url,
      method,
      resolve: result => {
        return {
          type: 'FETCH_SUCCESS',
          state: result
        }
      },
      reject: err => {
        return {
          type: 'FETCH_ERROR',
          state: err
        }
      },
      error: err => {
        return {
          type: 'RESPONSE_ERROR',
          state: err,
          transform: ()=>{
            dispatch({
              type: 'Lalala'
            })
          }
        }
      }
    }
  })
}
// Usage
componentDidMount(){
    let { dispatch } = this.props;
    dispatch(inject('player', this))
    console.log(appStore.getState(this))
}
//Result will be player stateKey value
//Inject Action Creator
export const inject = (state, component) => {
  return {
    type: '@@redux/INJECT',
    state
    component
  }
}
function diStoreEnhancer(){
  return createStore => (...args) => {
    const store = createStore(...args);
    const diContainer = []
    const dispatch = function decoratedDispatch(action){
      if (action.type === '@@redux/INJECT') {
        const { component, state } = action;
        diContainer.push({ component, stateKey: state })
      }
      return store.dispatch(action)
    }
    const getState = function decoratedState() {
      if (component) {
        return store.getState()[diContainer
            .filter(di => di.component === component)
            .reduce( (prev,next) => next, {})
            .stateKey
        ];
      }
      return store.getState()
    }
    return {
      ...store,
      ...{
        dispatch,
        getState
      }
    };
  };
}
import { compose } from 'redux'
const createComposedStore = compose(
  storeEnhancer(),
  applyMiddleware(fetcher, createLogger()
)(createStore);
export default createComposedStore(rootReducer);Container(Smart) Component:
Specifies data and behavior for component but does not know anything about appearance
Presentational(Dumb) Component:
Specify appearance of component but knows nothing about behavior
The problem:
If you have a component tree, store must be passed through props for all components in the tree.
The solution:
Only container components get store from context when defining contextTypes.
import { Provider } from 'react-redux'
import { appStore} from 'appStore'
<Provider store={ appStore }>
  <PlayerContainer/> 
</Provider>
import * from 'actionCreators';
import { connect } from 'react-redux';
// ...
// ...Player React Component definition
function mapStateToProps(state){
  const { src, poster, showControls } = state.player;
  return { 
    src, poster, showControls
  }
}
export const PlayerContainer = connect(
    mapStateToProps
)(Player);
import React from 'react';
import * from 'actionCreators';
import { connect } from 'react-redux';
class Player extends React.Component { 
  play(){
    this.props.dispatch(actionCreators.play())
  }
  render() {
    return (
        <PlayerWrapper>
            <PlayerSurface/>
            <PlayerControls play={this.play.bind(this}/>
        </PlayerWrapper>
    )    
  }
}
//export PlayerContainer with connect as in previous slideimport React from 'react';
import * from 'actionCreators';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
class Player extends React.Component { 
  render() {
    return (
        <PlayerWrapper>
            <PlayerSurface/>
            <PlayerControls play={this.props.actions.play} />
        </PlayerWrapper>
    )    
  }
}
function mapStateToProps(state){
//...same as previous slide
}
function mapDispatchToProps(dispatch){
  return {
    actions: bindActionCreators(actionCreators, dispatch)
  }
}
export const PlayerContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(Player);
Provider: wraps root component and makes possible to use connect()
connect: executed on every state change if specified. returned object is merged into component props
react@90min.com