Created
June 3, 2015 18:03
-
-
Save gaearon/d77ca812015c0356654f to your computer and use it in GitHub Desktop.
Combining Stateless Stores
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ------------ | |
// counterStore.js | |
// ------------ | |
import { | |
INCREMENT_COUNTER, | |
DECREMENT_COUNTER | |
} from '../constants/ActionTypes'; | |
const initialState = { counter: 0 }; | |
function increment({ counter }) { | |
return { counter: counter + 1 }; | |
} | |
function decrement({ counter }) { | |
return { counter: counter - 1 }; | |
} | |
export default function counterStore(state = initialState, action) { | |
switch (action.type) { | |
case INCREMENT_COUNTER: | |
return increment(state, action); | |
case DECREMENT_COUNTER: | |
return decrement(state, action); | |
default: | |
return state; | |
} | |
} | |
// ------------ | |
// todoStore.js | |
// ------------ | |
import { ADD_TODO } from '../constants/ActionTypes'; | |
const initialState = { | |
todos: [{ | |
text: 'do something', | |
id: 0 | |
}] | |
}; | |
export default function todoStore(state = initialState, action) { | |
switch (action.type) { | |
case ADD_TODO: | |
return { | |
todos: [{ | |
id: state.todos[0].id + 1, | |
text: action.text | |
}].concat(state.todos) | |
}; | |
} | |
return state; | |
} | |
// ------------ | |
// combinedStore.js | |
// ------------ | |
// Let's say at some point I know that these stores depend on each other in some way. | |
// If I *decide* I want to hide these stores as impl details of a single store | |
// I don't need to change their public APIs at all. I just register combinedStore instead. | |
import counterStore from './counterStore'; | |
import todoStore from '../todoStore'; | |
const initialState = { | |
counterData: undefined, | |
todoData: undefined | |
}; | |
export default function combinedStore(state = initialState, action) { | |
const counterData = counterStore(state.counterData, action); | |
const todoData = todoStore(state.todoData, action); | |
return { counterData, todoData }; | |
} | |
// So it's trivial to "merge" stores but keep the delegation. This is exactly how Elm models work too. | |
// Now, if I *want* to, I can make substores more custom (e.g. make a store factory that responds only to | |
// actions matching predicate, like createFollowersStore(userId) => FollowersStore that responds to specific | |
// userId in the action). Composition all the way! |
@fisherwebdev I just published a small middleware that i think can help you solve that problem: redux-next I hope it helps but it doesn't quite react to the store though... :P
Here are a cople of other middlewares i made redux-delay and redux-client-next (middleware creator function) lol @ my useless contributions
I can see the benefit of combined store that rules them all so stores don't have to talk/depend on each other, however i.e the combined store now owns the dependency as one abstraction level up, this is more likely to be more readable and easier to manage as an application grows.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@speedskater
I understand what you're suggesting but I'm sure it's going to be a pain in a large app to keep inventing keys so they don't clash, and it will look exactly the same namespaced way in the end.
I think that the problem of components tied to particular state keys is better solved by composing their
select
methods. This is exactly what NuclearJS does with "getters". It's already possible in Redux but not really documented yet. Here's some info:reduxjs/redux#160 (comment)
reduxjs/redux#47