Example:
connect(() => {...}, {
noteActions: {
create: createNote,
update: updateNote,
...
},
laneActions: {
create: createLane
}
})(Demo);
// at component
this.props.noteActions.create();
This would allow you to namespace actions better. You could even do
connect(() => {...}, {
noteActions,
laneActions
})(Demo);
if you want to be terse and don't mind connecting each action. This isn't as maintainable, though.
At the moment I get round this by manually wrapping nested Actions with dispatch
function mapDispatchToProps(dispatch) {
return {
noteActions: {
create: () => dispatch(createNote())
}
};
}
Obviously this is more verbose but it would get you what you needed.
Using traverse I put together this little util, it might be of use to someone.
// createNestedMapDispatchToProps.js
import traverse from 'traverse';
module export function(obj) {
return function (dispatch) {
return traverse(obj).map(function(action) {
if (this.isLeaf) {
this.update(function (...args) {
return dispatch(action(...args));
});
}
});
};
}
// MyComponent.jsx
import * as bookActions from './actions/books.js'
import * as authorActions from './actions/authors.js'
import createNestedMapDispatchToProps from 'createNestedMapDispatchToProps.js'
var mapDispatchToProps = createNestedMapDispatchToProps({
bookActions,
authorActions
});
@connect(
mapStateToProps,
mapDispatchToProps
)
class MyComponent extends React.Component {
...
}
_Edit_: Given traverse's dislike for IE you might want to iterate over the object in a different way but the principle still stands.
@rockingskier Nice work. Depending on what the maintainers of react-redux think, maybe this should become a little package of its own. That wouldn't be as discoverable, though.
This seems pretty obvious enhancement to me although I wonder whether it encourages writing huge components instead of small composable ones. React's tool for nesting is a component after all.
maybe this should become a little package of its own
It would be good for test driving at least.
I think the way it is right now is perfect.
This of mapDispatchToProps as factory.
You can do something like this:
function attachBookActions (dispatch, getState, actions) {
return assign({}, actions, {bookActions: {getBook: function () {//...}}}
}
function attachAuthorActions (dispatch, getState, actions) {
return assign({}, actions, {authorActions: {getAuthor: function () {//...}}}
}
function combinePropMappers (factories, dispatch, getState) {
return factories
.map(function (factory) {
return factory.bind(null, dispatch, getState)
})
.reduce(function (actions, factory) {
return factory(actions);
}, {});
}
var mapDispatchToProps = combinePropMappers.bind(null, [
attachAuthorActions,
attachBookActions
]);
You can achieve a cleaner code with currying. That's the way I try to write all my apps. I try not to create objects out of nowhere. only functions that reduce other objects.
This gives you the advantage of separating reducers.
For example you'd be able to have book reducers and author reducers.
All contained in the same module. Each module would have it's factories. Or whatever functions that are related to the logic of the reducer.
I'd be wary of including too many convenient opinionated defaults. I generally agree namespacing is nice, but there are potential unwanted side effects too (e.g. an export of some unrelated object from actions.js that we begin to iterate over because of import * as actions鈥攐r a recursive object we get stuck on).
The example in API docs actually shows namespacing:
import * as todoActionCreators from './todoActionCreators';
import * as counterActionCreators from './counterActionCreators';
import { bindActionCreators } from 'redux';
function mapStateToProps(state) {
return { todos: state.todos };
}
function mapDispatchToProps(dispatch) {
return {
todoActions: bindActionCreators(todoActionCreators, dispatch),
counterActions: bindActionCreators(counterActionCreators, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp);
I don't think it's too much typing, and the upside is it's explicit and non-magic.
I'd prefer to keep it that way.
Most helpful comment
I'd be wary of including too many convenient opinionated defaults. I generally agree namespacing is nice, but there are potential unwanted side effects too (e.g. an export of some unrelated object from
actions.jsthat we begin to iterate over because ofimport * as actions鈥攐r a recursive object we get stuck on).The example in API docs actually shows namespacing:
I don't think it's too much typing, and the upside is it's explicit and non-magic.
I'd prefer to keep it that way.