I am trying to set up RactiveJS with Redux for small example application - initialize dashboard (from AJAX), add/remove elements (widgets) from dashboard (and save serialized data on server). As there are tutorials almost exclusively for React, then I need advice. I followed some and got directory structure like:
views
app.html
dashboard.html
widget.html
js
actions
DashboardActions.js
components
Dashboard.js
Widget.js
constants
ActionTypes.js
reducers
dashboard.js
index.js
app.js
index.html
This example works, but there are several problems and I would like to figure out how to make it better. For example:
1) How to pass (and should I pass?) store and actions down to Ractive component tree? At now it uses bindActionCreators
in each component and I think this is not good solution.
2) Where to put initial state hydration from server? At now it is hardcoded in reducers/dashboard.js
, but I would like to use backend as data source and data save endpoint. There is middleware approach, but if this is good practice, then how to apply that with RactiveJs?
3) Should I use one big reducer
or by each component one reducer
?
4) Maybe the core concept is incorrect and should be refactored?
views/app.html
<Dashboard dashboard={{store.getState()}} store="{{store}}"></Dashboard>
views/dashboard.html
{{#with dashboard}}
<pre>
====
<a on-click="@this.addWidget('Added by click')" href="#">Add New</a>
{{#dashboard}}
{{#each widgets}}
<Widget id="{{this.id}}" name="{{this.name}}" size="{{this.size}}" actions="{{actions}}" store="{{store}}"></Widget>
{{/each}}
{{/dashboard}}
====
</pre>
{{/with}}
views/widget.html
<div>{{id}}-{{name}} (Size: {{size}})<a href="#" on-click="@this.deleteWidget(id)">X</a></div>
actions/DashboardActions.js
import * as types from '../constants/ActionTypes';
// Add widget to dashboard
export function addWidget(name) {
return {
type: types.ADD_WIDGET,
name
};
}
// Delete widget from dashboard
export function deleteWidget(id) {
return {
type: types.DELETE_WIDGET,
id
};
}
components/Dashboard.js
import Ractive from 'ractive'
import * as DashboardActions from '../actions/DashboardActions';
import { dispatch, bindActionCreators } from 'redux'
import Widget from './Widget'
import template from '../../views/dashboard.html';
export default Ractive.extend({
isolated: true,
components: {
Widget
},
oninit() {
const store = this.get("store");
const actions = bindActionCreators(DashboardActions, store.dispatch);
this.set("actions", actions);
},
addWidget(name) {
this.get("actions").addWidget(name);
},
template: template
});
components/Widget.js
import Ractive from 'ractive'
import * as DashboardActions from '../actions/DashboardActions';
import { dispatch, bindActionCreators } from 'redux'
import template from '../../views/widget.html';
export default Ractive.extend({
isolated: true,
template: template,
oninit() {
console.log(this.get("actions"));
const store = this.get("store");
const actions = bindActionCreators(DashboardActions, store.dispatch);
this.set("actions", actions);
},
deleteWidget(id) {
this.get("actions").deleteWidget(id);
},
})
constants/ActionTypes.js
// Add widget to dashboard
export const ADD_WIDGET = 'ADD_WIDGET';
// Delete widget from dashboard
export const DELETE_WIDGET = 'DELETE_WIDGET';
reducers/dashboard.js
import * as types from '../constants/ActionTypes';
const initialState = {
widgets: [
{id: 1, name: "First widget"},
{id: 2, name: "Second widget"},
{id: 3, name: "Third widget"},
],
};
export default function dashboard(state = initialState, action) {
switch (action.type) {
case types.ADD_WIDGET:
const newId = state.widgets.length + 1;
const addedWidgets = [].concat(state.widgets, {
id: newId,
name: action.name
});
return {
widgets: addedWidgets
}
case types.DELETE_WIDGET:
const newWidgets = state.widgets.filter(function(obj) {
return obj.id != action.id
});
return {
widgets: newWidgets
}
default:
return state;
}
}
reducers/index.js
export { default as dashboard } from './dashboard';
app.js
import Ractive from 'ractive';
import template from '../views/app.html';
import Dashboard from './components/Dashboard.js'
import { createStore, combineReducers, bindActionCreators } from 'redux'
import * as reducers from './reducers'
const reducer = combineReducers(reducers);
const store = createStore(reducer);
let App = new Ractive({
el: '#app',
template: template,
components: {
Dashboard
},
data: {
store
}
});
store.subscribe(() => App.update());
export default App;
Thanks!