Marionette and Backbone bindings for Redux.
It's like React Redux, but for Marionette and Backbone.
Marionette Redux allows you to connect
any Marionette or Backbone "component" to a Redux store.
Migrating to React
If you've decided to migrate, Marionette Redux allows you to leverage Redux as a central data store to share app state between your React and Marionette components.
Examples:
Predictability
Marionette Redux introduces to a Marionette application a lifecycle that allows for deterministic DOM updates – consistent at first render and for any subsequent store updates (or component state changes) after the first render.
componentWillUpdate
will execute when a display component (Marionette.View
or Marionette.Behavior
) first renders. This is where you put your DOM manipuation code.
A connected component's mapStateToProps
will execute whenever the Redux store state changes. If the return object of display component's mapStateToProps
differs from the last result, componentWillUpdate
will execute.
Thus, you can set up components to always rely on the same set of values to determine their DOM state. This means that your views can execute the same callstack on first render and for any subsequent changes to the Redux store.
npm install --save marionette-redux
Below is an example of a Marionette.View
that has been "connected" to a Redux store
. The following code could also be applied to a Marionette.Behavior
.
var ConnectedView = MarionetteRedux.connect()(Marionette.View.extend({
store: store,
mapStateToProps: function(state) {
return {
isActive: state.isActive
}
},
componentWillUpdate: function() {
this.$el.toggleClass('active', this.props.isActive);
}
}));
In this example, store
is a property on the component, but connect
will also look to window.store
as a last resort. window.store
can thus act similarly to React Redux's "Provider
".
While connect
is the recommended approach, Marionette Redux can also be used as a mixin.
Marionette.View.extend(MarionetteRedux.mixin);
Mappings work the same as in React Redux. A change to the Redux store will result in this callback being executed on any "connected" components.
mapStateToProps
can be a property on the component itself.
var ConnectedView = MarionetteRedux.connect()(Marionette.View.extend({
mapStateToProps: function(state) {
return {
isActive: state.isActive
}
}
}));
Or it can be provided to connect
as the first argument.
function mapStateToProps(state) {
return {
isActive: state.isActive
}
}
var ConnectedView = MarionetteRedux.connect(mapStateToProps)(Marionette.View.extend({…}));
mapDispatchToProps
can also be a property on the component.
var ConnectedView = MarionetteRedux.connect()(Marionette.View.extend({
mapDispatchToProps: function(dispatch) {
return {
dispatchMyEvent: function() {
dispatch({
type: 'MY_EVENT'
});
}
}
},
events: {
click: 'handleClick'
},
handleClick: function() {
this.props.dispatchMyEvent();
}
}));
Or it can provided to connect
as the second argument.
var ConnectedView = MarionetteRedux.connect(null, mapDispatchToProps)(Marionette.View.extend({…}));
This function is similar to React's componentWillReceiveProps
. It provides an opportunity to execute any side effect functions before execution of componentWillUpdate
.
Note: If the component is not a display component, meaining it is a Backbone.Model
or Backbone.Collection
, componentWillReceiveProps
will still execute, however componentWillUpdate
WILL NOT.
This library ecourages the use of componentWillUpdate
to ensure predictability of DOM state – one of the great things about React.
As demonstrated below, componentWillUpdate
can be used to execute code that you want to run when a component is first rendered and after any subsequent changes to a component's props
or state
.
var ConnectedView = MarionetteRedux.connect()(Marionette.View.extend({
store: store,
mapStateToProps: function(state) {
return {
isActive: state.isActive
}
},
componentWillUpdate: function() {
this.$el.toggleClass('active', this.props.isActive);
}
}));
If you prefer more granular control over store updates, we've provided state to components as well.
setState
, getState
, state
, and getInitialState
are all available for getting and setting state.
State works exactly the same as Marionette's modelEvents
listeners, using the stateEvents
object to define listeners:
var ConnectedView = MarionetteRedux.connect()(Marionette.View.extend({
store: store,
getInitialState: function() {
return: {
isActive: false
}
},
stateEvents: {
'change:isActive': 'onIsActiveChange'
},
onIsActiveChange: function(view, isActive) {
this.$el.toggleClass('active', isActive);
},
mapStateToProps: function(state) {
return {
isActive: state.active === this.model.id
}
},
componentWillReceiveProps: function(update) {
this.setState({
isActive: update.isActive
});
},
}));
As with changes to props
, changes to a display component's state
will execute componentWillUpdate
.
You also have the option to connect
a Backbone.Model
or Backbone.Collection
.
function mapStateToProps(state) {
return {
currency: state.currency
}
}
var Model = Backbone.Model.extend({
store: store,
initialize: function() {
// update the store on changes
this.on('update', function() {
store.dispatch({
type: 'MODEL_UPDATE',
data: this.toJSON()
});
})
},
componentWillReceiveProps: function(update) {
this.set({
currency: update.currency
})
}
});
var ConnectedModel = MarionetteRedux.connect(mapStateToProps)(Model);
var Collection = Backbone.Collection.extend({
store: store,
initialize: function() {
// update the store on changes
this.on('update', function() {
store.dispatch({
type: 'COLLECTION_UPDATE',
data: this.toJSON()
});
})
}
});
var ConnectedCollection = MarionetteRedux.connect()(Collection);
MIT