diff --git a/README.md b/README.md index 54b3511..8b4942e 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ and architecture, but leverages React to do the bookkeeping. This makes it incredibly easy to nest components and add features without friction. React `propTypes` can be specified on Spindle components. In fact, Spindle takes -this concept a step further and lets you specify `modelType`, `emitType`, and +this concept a step further and lets you specify `modelType`, `cbTypes`, and even `TypedAction`s! To see how to solve lots of different UI challenges with Spindle, check out the diff --git a/examples/constrained-counters/Counter.js b/examples/constrained-counters/Counter.js index 979cb1f..6f7f9f1 100644 --- a/examples/constrained-counters/Counter.js +++ b/examples/constrained-counters/Counter.js @@ -19,6 +19,11 @@ const modelType = recordOf({ }); +const cbTypes = { + onChange: PropTypes.number.isRequired, +}; + + const Action = Union({ Increment: null, Decrement: null, @@ -40,7 +45,7 @@ const propsUpdate = ({ min = -Infinity, max = Infinity }, model) => { const updated = constrain(model.merge({ min, max })); return Update({ model: updated, - cb: { onChange: [model.value] }, + cb: { onChange: model.value }, }); }; @@ -50,7 +55,7 @@ const update = (action, model) => Action.match(action, { const newModel = constrain(model.update('value', v => v + 1)); return Update({ model: newModel, - cb: { onChange: [newModel.value] }, + cb: { onChange: newModel.value }, }); }, @@ -58,7 +63,7 @@ const update = (action, model) => Action.match(action, { const newModel = constrain(model.update('value', v => v - 1)); return Update({ model: newModel, - cb: { onChange: [newModel.value] }, + cb: { onChange: newModel.value }, }); }, }); @@ -82,4 +87,4 @@ const view = (model, dispatch) => ( export default Spindle('Counter', - { Action, init, propsUpdate, update, modelType, view }); + { Action, init, propsUpdate, update, modelType, view, cbTypes }); diff --git a/examples/sum-counters/Counter.js b/examples/sum-counters/Counter.js index 77c8afc..7eca74f 100644 --- a/examples/sum-counters/Counter.js +++ b/examples/sum-counters/Counter.js @@ -11,7 +11,7 @@ const Model = Record({ const init = () => { const model = Model(); - return Update({ model, cb: { onChange: [model.value] } }); + return Update({ model, cb: { onChange: model.value } }); }; @@ -26,7 +26,7 @@ const update = (action, model) => Action.match(action, { const newModel = model.update('value', v => v + 1); return Update({ model: newModel, - cb: { onChange: [newModel.get('value')] }, + cb: { onChange: newModel.get('value') }, }); }, @@ -34,7 +34,7 @@ const update = (action, model) => Action.match(action, { const newModel = model.update('value', v => v - 1); return Update({ model: newModel, - cb: { onChange: [newModel.get('value')] }, + cb: { onChange: newModel.get('value') }, }); }, }); diff --git a/examples/todomvc/Header.js b/examples/todomvc/Header.js index 3c381d3..7e0ee4e 100644 --- a/examples/todomvc/Header.js +++ b/examples/todomvc/Header.js @@ -21,7 +21,7 @@ const update = (action, model) => Action.match(action, { KeyDown: e => { const task = e.target.value.trim(); if (e.keyCode === 13 && task !== '') { - return Update({ cb: { onAdd: [task] }, model: '' }); + return Update({ cb: { onAdd: task }, model: '' }); } else { return Update(); } diff --git a/examples/todomvc/Item.js b/examples/todomvc/Item.js index 8d8794c..c62f7d9 100644 --- a/examples/todomvc/Item.js +++ b/examples/todomvc/Item.js @@ -32,13 +32,13 @@ const propsUpdate = ({ task }, model) => const update = (action, model) => Action.match(action, { Destroy: () => - Update({ cb: { onDestroy: [model.task.id] } }), + Update({ cb: { onDestroy: model.task.id } }), FinishEditing: () => { const task = model.task.set('title', model.editingValue); return Update({ model: model.merge({ task, editing: false }), - cb: { onSave: [task] }, + cb: { onSave: task }, }); }, @@ -47,7 +47,7 @@ const update = (action, model) => Action.match(action, { const task = model.task.set('title', model.editingValue); return Update({ model: model.merge({ task, editing: false }), - cb: { onSave: [task] }, + cb: { onSave: task }, }); } else { return Update(); @@ -67,7 +67,7 @@ const update = (action, model) => Action.match(action, { const task = model.task.update('completed', c => !c); return Update({ model: model.set('task', task), - cb: { onSave: [task] }, + cb: { onSave: task }, }); }, }); diff --git a/spindle.js b/spindle.js index 1fedb3a..1851b4d 100644 --- a/spindle.js +++ b/spindle.js @@ -142,6 +142,7 @@ export default function Spindle(name, { view = () => null, subscriptions = () => [], modelType = PropTypes.any, + cbTypes = {}, propTypes: componentPropTypes = {}, }) { class Component extends React.Component { @@ -218,8 +219,12 @@ export default function Spindle(name, { Object.keys(cb) .filter(prop => this.props[prop]) - .forEach(prop => - this.props[prop].apply(null, cb[prop])); + .forEach(prop => { + if (cbTypes.hasOwnProperty(prop)) { + assertType(`cb: { ${prop} }`, cbTypes[prop], cb[prop], name, source); + } + this.props[prop](cb[prop]); + }); } if (source === 'init') {