Skip to content

Instantly share code, notes, and snippets.

@mLuby
Last active July 26, 2016 21:05
Show Gist options
  • Save mLuby/9ee6b97ab099ec4c268ced1bcbebd032 to your computer and use it in GitHub Desktop.
Save mLuby/9ee6b97ab099ec4c268ced1bcbebd032 to your computer and use it in GitHub Desktop.
Implementation of redux pattern and simulated use within a react-like app.
"use strict"
// Redux
const bindActionCreators = (actionCreator, dispatch) => args => dispatch(actionCreator(args))
const createStore = reducer => { // (state -> action -> state) -> Store
let state = null
const subscribers = []
const dispatch = action => {
state = reducer(state, action)
subscribers.forEach(cb => cb(state))
}
const subscribe = callback => subscribers.push(callback)
const connect = (mapStateToProps, mapDispatchToProps, component) => {
subscribe(state => component.onNewState(mapStateToProps(state)))
return mapDispatchToProps(dispatch)
}
return {
dispatch, // action -> ()
subscribe, // callback -> ()
connect // (state -> props) -> (dispatch -> props) -> Component -> Component
}
}
// App
// action types
const INCREMENT_COUNTER = "INCREMENT_COUNTER"
const SET_COUNTER = "SET_COUNTER"
// action creators
const incrementCounter = () => ({type: INCREMENT_COUNTER})
const setCounter = value => ({type: SET_COUNTER, payload: value})
// reducers
const initialState = {counter: 0, counterIsOdd: false}
const isOdd = value => Boolean(value % 2) // helper function
const rootReducer = (state, action) => {
state = state || initialState
switch (action.type) {
case INCREMENT_COUNTER: return Object.assign({}, state, {
counter: state.counter + 1,
counterIsOdd: isOdd(state.counter + 1)
})
case SET_COUNTER: return Object.assign({}, state, {
counter: action.payload,
counterIsOdd: isOdd(action.payload)
})
default: return state
}
}
// Components
const store = createStore(rootReducer)
// Component A: click to increment counter; displays whether counter is odd.
const ComponentA = {
onNewState: newState => console.log('Component A received new state', newState),
onClick: () => store.dispatch(incrementCounter())
}
store.subscribe(ComponentA.onNewState)
// Component B: input that displays counter value; can change counter.
const ComponentB = {
onNewState: newState => console.log('Component B received new state', newState),
onChange: value => setCounter(value)
}
const mapStateToPropsB = props => ({
counter: props.counter
})
const mapDispatchToPropsB = dispatch => ({
onChange: bindActionCreators(ComponentB.onChange, dispatch)
})
const HigherOrderComponentB = store.connect(mapStateToPropsB, mapDispatchToPropsB, ComponentB)
// Runtime/user interaction
ComponentA.onClick()
// Component A received new state { counter: 1, counterIsOdd: true }
// Component B received new state { counter: 1 }
HigherOrderComponentB.onChange(7)
// Component A received new state { counter: 7, counterIsOdd: true }
// Component B received new state { counter: 7 }
ComponentA.onClick()
// Component A received new state { counter: 8, counterIsOdd: false }
// Component B received new state { counter: 8 }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment