From ed9b0e94df19eea2fb6cab0f6d4c4c35287161d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=C3=A9=20Jr?= Date: Sun, 8 Jan 2017 22:15:51 -0300 Subject: [PATCH 1/2] Starting with redux-loop --- src/actions/index.js | 20 +++++++++-- src/components/Drawer/index.js | 1 + src/modules/AppViewContainer.js | 11 ++---- src/scenes/Topic/TopicContainer.js | 10 +----- src/store/reducers/topics.js | 55 +++++++++++++++++++++++++++--- 5 files changed, 73 insertions(+), 24 deletions(-) diff --git a/src/actions/index.js b/src/actions/index.js index be49e65..f1bd4c9 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -1,22 +1,38 @@ export const FETCH_TOPICS = 'FETCH_TOPICS' export const FETCH_DRIPS = 'FETCH_DRIPS' +export const SET_TOPICS = 'SET_TOPICS' +export const SET_DRIPS = 'SET_DRIPS' export const SELECT_TOPIC = 'SELECT_TOPIC' const setTopics = (topics) => { return { - type: FETCH_TOPICS, + type: SET_TOPICS, topics, } } const setDrips = (topicId, drips) => { return { - type: FETCH_DRIPS, + type: SET_DRIPS, topicId, drips, } } +const fetchTopics = () => { + return { + type: FETCH_TOPICS + } +} + +const fetchDrips = (topicId) => { + return { + type: FETCH_DRIPS, + topicId + } +} + + const selectTopic = (topicId) => { return { type: SELECT_TOPIC, diff --git a/src/components/Drawer/index.js b/src/components/Drawer/index.js index 53df5c2..1021555 100644 --- a/src/components/Drawer/index.js +++ b/src/components/Drawer/index.js @@ -26,6 +26,7 @@ class Drawer extends Component { onPress: () => { const { selectTopic, navigate } = this.props + debugger selectTopic(topic) navigate.to('topics.topic', topic.title, { topic: Immutable.fromJS(topic) }) // TODO: We will want to switch to NavigationExperimental sigh :) This should be declarative "data-down" style or I'll cry forever drawerWrapper.closeDrawer() diff --git a/src/modules/AppViewContainer.js b/src/modules/AppViewContainer.js index 9f12bff..2a16289 100644 --- a/src/modules/AppViewContainer.js +++ b/src/modules/AppViewContainer.js @@ -9,15 +9,8 @@ export default connect( dispatch => { return { fetchTopics: () => { - API.getTopics().then((response) => { - const topicsMap = response.data.topics.reduce((acc, topic) => { - return acc.set(topic.id, Immutable.fromJS(topic)) - }, Immutable.Map()) - dispatch(Actions.setTopics(topicsMap)) - }).catch((error) => { - console.log(error) - }) - }, + dispatch(Actions.fetchTopics()) + } } } )(AppView) diff --git a/src/scenes/Topic/TopicContainer.js b/src/scenes/Topic/TopicContainer.js index 66a00d5..cfeb068 100644 --- a/src/scenes/Topic/TopicContainer.js +++ b/src/scenes/Topic/TopicContainer.js @@ -1,6 +1,5 @@ import Topic from './index' import { connect } from 'react-redux' -import API from '../../api' import Actions from '../../actions' import Immutable from 'immutable' @@ -17,14 +16,7 @@ const mapStateToProps = (state) => { const mapDispatchToProps = (dispatch) => { return { fetchDrips: (topicId) => { - API.getDrips(topicId).then((response) => { - const dripsMap = response.data.drips.reduce((acc, drip) => { - return acc.set(drip.id, Immutable.fromJS(drip)) - }, Immutable.Map()) - dispatch(Actions.setDrips(topicId, dripsMap)) - }).catch((error) => { - console.log(error) - }) + dispatch(Actions.fetchDrips(topicId)) }, } } diff --git a/src/store/reducers/topics.js b/src/store/reducers/topics.js index a573bcd..04b548f 100644 --- a/src/store/reducers/topics.js +++ b/src/store/reducers/topics.js @@ -1,10 +1,41 @@ import { createReducer } from 'redux-immutablejs' import Immutable from 'immutable' -import { FETCH_TOPICS, FETCH_DRIPS } from '../../actions' +import { FETCH_TOPICS, FETCH_DRIPS, SET_TOPICS, SET_DRIPS } from '../../actions' +import { loop, Effects } from 'redux-loop'; +import API from '../../api' + +function fetchTopics(){ + API.getTopics().then((response) => { + const topicsMap = response.data.topics.reduce((acc, topic) => { + return acc.set(topic.id, Immutable.fromJS(topic)) + }, Immutable.Map()) + return Actions.setTopics(topicsMap) + }).catch((error) => { + console.log(error) + }) +} + +function fetchDrips(topicId){ + API.getDrips(topicId).then((response) => { + const dripsMap = response.data.drips.reduce((acc, drip) => { + return acc.set(drip.id, Immutable.fromJS(drip)) + }, Immutable.Map()) + return Actions.setDrips(topicId, dripsMap) + }).catch((error) => { + console.log(error) + }) +} export default createReducer(Immutable.Map(), { [FETCH_TOPICS]: (state, action) => { - return action.topics.map((topic) => { + return loop( + state.set('loading', true), + Effects.promise(fetchTopics) + ) + }, + + [SET_TOPICS]: (state, action) => { + const topics = action.topics.map((topic) => { // Look in the store and pull in any drips we already know about, since // we don't have them here but we'd like to keep them for faster startup // from storage @@ -17,8 +48,24 @@ export default createReducer(Immutable.Map(), { } return newTopic }) + return loop( + state.set('topics', topics).set('loading', false), + Effects.none() + ) }, - [FETCH_DRIPS]: (state, { topicId, drips }) => { - return state.setIn([topicId, 'drips'], drips) + + [FETCH_DRIPS]: (state, { topicId }) => { + return loop( + state.set('loading', true), + Effects.promise(fetchDrips(topicId)) + ) + }, + + [SET_DRIPS]: (state, { topicId, drips }) => { + let newState = state.setIn([topicId, 'drips'], drips) + return loop( + newState.set('loading', false), + Effects.none() + ) }, }) From 3528d9ddea7ef2a223549a88b68af12c6bb045a8 Mon Sep 17 00:00:00 2001 From: Josh Adams Date: Sun, 8 Jan 2017 21:28:00 -0600 Subject: [PATCH 2/2] Got redux-loop working --- index.android.js | 8 +- src/actions/index.js | 13 ++- src/components/Drawer/DrawerContainer.js | 2 +- src/components/Drawer/index.js | 1 - src/modules/AppView.android.js | 3 +- src/modules/AppViewContainer.js | 2 +- src/scenes/Login/LoginContainer.js | 2 +- src/scenes/Topic/TopicContainer.js | 2 +- src/scenes/Topics/TopicsContainer.js | 2 +- src/store/index.js | 38 +++++--- src/store/reducers/topics.js | 118 +++++++++++++---------- 11 files changed, 113 insertions(+), 78 deletions(-) diff --git a/index.android.js b/index.android.js index ad75f2c..cb5d3ae 100755 --- a/index.android.js +++ b/index.android.js @@ -14,10 +14,10 @@ import AppViewContainer from './src/modules/AppViewContainer' class DailyDrip extends Component { componentWillMount() { // Load existing store state from async storage - console.log('loading storage') - storageLoader(store) - .then((newState) => console.log('Loaded state:', newState)) - .catch((e) => console.log('Failed to load previous state', e)) + // console.log('loading storage') + // storageLoader(store) + // .then((newState) => console.log('Loaded state:', newState)) + // .catch((e) => console.log('Failed to load previous state', e)) } render() { diff --git a/src/actions/index.js b/src/actions/index.js index f1bd4c9..3413cf9 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -3,6 +3,7 @@ export const FETCH_DRIPS = 'FETCH_DRIPS' export const SET_TOPICS = 'SET_TOPICS' export const SET_DRIPS = 'SET_DRIPS' export const SELECT_TOPIC = 'SELECT_TOPIC' +export const NO_OP = 'NO_OP' const setTopics = (topics) => { return { @@ -40,10 +41,16 @@ const selectTopic = (topicId) => { } } -const Actions = { +const noOp = () => { + return { + type: NO_OP + } +} + +export const Actions = { setTopics, setDrips, selectTopic, + fetchTopics, + fetchDrips } - -export default Actions diff --git a/src/components/Drawer/DrawerContainer.js b/src/components/Drawer/DrawerContainer.js index cafd16f..786069b 100644 --- a/src/components/Drawer/DrawerContainer.js +++ b/src/components/Drawer/DrawerContainer.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux' -import Actions from '../../actions' +import { Actions } from '../../actions' import Drawer from '../Drawer' const mapStateToProps = (state) => { diff --git a/src/components/Drawer/index.js b/src/components/Drawer/index.js index 1021555..53df5c2 100644 --- a/src/components/Drawer/index.js +++ b/src/components/Drawer/index.js @@ -26,7 +26,6 @@ class Drawer extends Component { onPress: () => { const { selectTopic, navigate } = this.props - debugger selectTopic(topic) navigate.to('topics.topic', topic.title, { topic: Immutable.fromJS(topic) }) // TODO: We will want to switch to NavigationExperimental sigh :) This should be declarative "data-down" style or I'll cry forever drawerWrapper.closeDrawer() diff --git a/src/modules/AppView.android.js b/src/modules/AppView.android.js index f2cdf80..b867425 100644 --- a/src/modules/AppView.android.js +++ b/src/modules/AppView.android.js @@ -58,9 +58,10 @@ class App extends Component { } checkAuth(navigate) { + let { fetchTopics } = this.props AsyncStorage.getItem('auth_token').then((value) => { if (value) { - this.props.fetchTopics() + fetchTopics() } else { if (navigate) { navigate.to('login') diff --git a/src/modules/AppViewContainer.js b/src/modules/AppViewContainer.js index 2a16289..489d27c 100644 --- a/src/modules/AppViewContainer.js +++ b/src/modules/AppViewContainer.js @@ -1,7 +1,7 @@ import { connect } from 'react-redux' import AppView from './AppView' import Immutable from 'immutable' -import Actions from '../actions' +import { Actions } from '../actions' import API from '../api' export default connect( diff --git a/src/scenes/Login/LoginContainer.js b/src/scenes/Login/LoginContainer.js index 9974cb4..575ca52 100644 --- a/src/scenes/Login/LoginContainer.js +++ b/src/scenes/Login/LoginContainer.js @@ -1,7 +1,7 @@ import Login from './index' import { connect } from 'react-redux' import API from '../../api' -import Actions from '../../actions' +import { Actions } from '../../actions' const mapStateToProps = () => { return {} diff --git a/src/scenes/Topic/TopicContainer.js b/src/scenes/Topic/TopicContainer.js index cfeb068..b2ea69e 100644 --- a/src/scenes/Topic/TopicContainer.js +++ b/src/scenes/Topic/TopicContainer.js @@ -1,6 +1,6 @@ import Topic from './index' import { connect } from 'react-redux' -import Actions from '../../actions' +import { Actions } from '../../actions' import Immutable from 'immutable' const mapStateToProps = (state) => { diff --git a/src/scenes/Topics/TopicsContainer.js b/src/scenes/Topics/TopicsContainer.js index 7b95b83..f422a9a 100644 --- a/src/scenes/Topics/TopicsContainer.js +++ b/src/scenes/Topics/TopicsContainer.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux' import Topics from './index' -import Actions from '../../actions' +import { Actions } from '../../actions' const mapStateToProps = (state) => { const topics = state.get('topics') diff --git a/src/store/index.js b/src/store/index.js index 11fd760..7848f42 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,5 +1,5 @@ import { createStore, applyMiddleware, compose } from 'redux' -import { combineReducers } from 'redux-immutablejs' +//import { combineReducers } from 'redux-immutablejs' import * as storage from 'redux-storage' import createEngine from 'redux-storage-engine-reactnativeasyncstorage' import merger from 'redux-storage-merger-immutablejs' @@ -19,17 +19,17 @@ import Immutable from 'immutable' import * as reducers from './reducers' // LOG OUT STATE CHANGES -// import createLogger from 'redux-logger' -// const loggerMiddleware = createLogger({ -// stateTransformer: state => state && state.toJS(), -// }) +import createLogger from 'redux-logger' +const loggerMiddleware = createLogger({ + stateTransformer: state => state && state.toJS(), +}) // END LOG OUT STATE CHANGES // STORE REDUX STATE -const reducer = storage.reducer(combineReducers(reducers), merger) -const storageEngine = createEngine('@DAILYDRIP.reduxStore') -const storageMiddleware = storage.createMiddleware(storageEngine) -export const storageLoader = storage.createLoader(storageEngine) +// const reducer = storage.reducer(combineReducers(reducers), merger) +// const storageEngine = createEngine('@DAILYDRIP.reduxStore') +// const storageMiddleware = storage.createMiddleware(storageEngine) +// export const storageLoader = storage.createLoader(storageEngine) // END STORE REDUX STATE const initialState = Immutable.fromJS({ @@ -37,14 +37,26 @@ const initialState = Immutable.fromJS({ selectedTopic: {}, // NOTE: This is presently intended to end up as just a map with a basic `id` key - this is because you can't do immutable ints and I wanted to use createReducer throughout, but it's probably dumb... }) +console.log('initialState', initialState) + const enhancer = compose( - // applyMiddleware(loggerMiddleware, storageMiddleware), - applyMiddleware(storageMiddleware), + //applyMiddleware(loggerMiddleware, storageMiddleware), + applyMiddleware(loggerMiddleware), Loop.install(), - devTools() + devTools(), +) + +const store = createStore( + Loop.combineReducers( + reducers, + Immutable.Map(), + (state, key) => state.get(key), + (state, key, value) => state.set(key, value) + ), + initialState, + enhancer ) -const store = createStore(reducer, initialState, enhancer) devTools.updateStore(store) export default store diff --git a/src/store/reducers/topics.js b/src/store/reducers/topics.js index 04b548f..b42e247 100644 --- a/src/store/reducers/topics.js +++ b/src/store/reducers/topics.js @@ -1,71 +1,87 @@ -import { createReducer } from 'redux-immutablejs' import Immutable from 'immutable' -import { FETCH_TOPICS, FETCH_DRIPS, SET_TOPICS, SET_DRIPS } from '../../actions' +import { Actions, SET_TOPICS, SET_DRIPS, FETCH_TOPICS, FETCH_DRIPS } from '../../actions' import { loop, Effects } from 'redux-loop'; import API from '../../api' -function fetchTopics(){ - API.getTopics().then((response) => { - const topicsMap = response.data.topics.reduce((acc, topic) => { - return acc.set(topic.id, Immutable.fromJS(topic)) - }, Immutable.Map()) - return Actions.setTopics(topicsMap) - }).catch((error) => { - console.log(error) - }) -} -function fetchDrips(topicId){ - API.getDrips(topicId).then((response) => { - const dripsMap = response.data.drips.reduce((acc, drip) => { - return acc.set(drip.id, Immutable.fromJS(drip)) - }, Immutable.Map()) - return Actions.setDrips(topicId, dripsMap) - }).catch((error) => { - console.log(error) - }) +let fetchTopics = () => ( + API.getTopics() + .then((response) => { + return Actions.setTopics( + response.data.topics + .reduce((acc, topic) => { + return acc + .set(topic.id, Immutable.fromJS(topic)) + }, Immutable.Map()) + ) + }) + .catch((err) => { + console.error(err) + return Actions.noOp() + }) +) + +let fetchDrips = (topicId) => { + return API.getDrips(topicId) + .then((response) => { + return Actions.setDrips( + topicId, + response.data.drips + .reduce((acc, drip) => ( + acc.set(drip.id, Immutable.fromJS(drip)) + ), Immutable.Map()) + )}) + .catch((err) => { + console.error(err) + return Actions.noOp() + }) } -export default createReducer(Immutable.Map(), { - [FETCH_TOPICS]: (state, action) => { - return loop( - state.set('loading', true), +export default function(state, action){ + switch (action.type) { + case FETCH_TOPICS: + return loop( + state, Effects.promise(fetchTopics) ) - }, - [SET_TOPICS]: (state, action) => { - const topics = action.topics.map((topic) => { - // Look in the store and pull in any drips we already know about, since - // we don't have them here but we'd like to keep them for faster startup - // from storage - const existingTopic = state.get(topic.get('id').toString()) - let newTopic - if (existingTopic) { - newTopic = topic.set('drips', existingTopic.get('drips')) - } else { - newTopic = topic - } - return newTopic - }) + case SET_TOPICS: + const topics = action.topics.map((topic) => { + // Look in the store and pull in any drips we already know about, since + // we don't have them here but we'd like to keep them for faster startup + // from storage + const existingTopic = state.get(topic.get('id').toString()) + let newTopic + if (existingTopic) { + newTopic = topic.set('drips', existingTopic.get('drips')) + } else { + newTopic = topic + } + return newTopic + }) + return loop( - state.set('topics', topics).set('loading', false), + topics, Effects.none() ) - }, - [FETCH_DRIPS]: (state, { topicId }) => { + case FETCH_DRIPS: + return loop( + state, + Effects.promise(fetchDrips, action.topicId) + ) + + case SET_DRIPS: return loop( - state.set('loading', true), - Effects.promise(fetchDrips(topicId)) + state + .setIn([action.topicId, 'drips'], action.drips), + Effects.none() ) - }, - [SET_DRIPS]: (state, { topicId, drips }) => { - let newState = state.setIn([topicId, 'drips'], drips) + default: return loop( - newState.set('loading', false), + state, Effects.none() ) - }, -}) + } +}