Closed Yomguithereal closed 9 years ago
A cursor is a way of expressing a data traversal originating from the functional zippers.
Let's consider the following JavaScript object:
var atom = {
primitive: 3,
one: {
subone: {
hello: 'world'
},
subtow: {
colors: ['blue', 'yellow']
}
},
two: {
name: 'John',
surname: 'Dillinger'
}
};
Using a cursor on it would produce the following:
// Considering that the cursor function is left-curried to
// hit the atom object shown above
cursor('primitive')
>>> 3
cursor('two')
>>> {name: 'John', surname: 'Dillinger'}
cursor('one', 'subone')
>>> {hello: 'world'}
cursor('one', 'subone', 'colors', 1)
>>> 'yellow'
// Exposing tree-like traversal
var c = cursor('two')
c.select('name');
>>> 'John'
c.up();
>>> // Same as atom
c.up().select('one', 'subtow', 'colors', 0).next();
>>> 'yellow'
Nothing too fancy here and can be implemented in an easy reduce.
But, would be possible to (see Immutable helpers from React
concerning the update syntax) do the following:
var c = cursor('one', 'subtow');
// Update the cursor in an immutable-clever way
c.update({colors: {$push: 'purple'}});
cursor('one').update('subtow', {colors: {$push: 'purple'}});
// Or in a "bourrin" fashion
c.update({new: 'object', totally: 'different'});
// Listen to updates concerning this cursor
c.on('update', function(details) {
// Something was updated under the cursor
// What's more, precise details are known
});
// Listen to removal of the cursor
// (meaning it is no longer relevant because its root has disappeared)
c.on('removal', fn);
A lot of people are currently thinking about cursors.
The library Om makes it one of its topmost features.
However cursors are currently conflicting with what domino.js offers, which is a store of top-level very important properties.
In domino, only top-level properties are considered as first-class citizen while nested properties have no "official" value or recognition.
In Om, as a comparison, the user defines the state of its application as an atom which is basically the same thing as the atom
object presented above and any component is free to hit on any level of the app state tree.
var controller = new domino({
// Defining the state of the application like you want
state: {
primitive: 3,
one: {
subone: {
hello: 'world'
},
subtow: {
colors: ['blue', 'yellow']
}
},
two: {
name: 'John',
surname: 'Dillinger'
},
notSetYetButNullForLegibility: null
},
// Storing important cursors used elsewhere?
cursors: {
colors: ['one', 'subtow', 'colors']
},
// Facet are still good
facets: {
colorsString: function() {
return this.cursors.colors.get().split(', ');
}
}
});
// Creating a cursor can be done anywhere
// I like the select name, but my naming...
var cursor = controller.select('one', 'subtow', 'colors');
// Idem as
var cursor = controller.cursors.colors;
// Component is rendered when the cursor has updated data
var Colors = React.createClass({
mixins: [cursor.mixin],
renderItem: function(color) {
return (
<li>
<span>{color}</span>
</li>
);
},
render: function() {
return <ul>{this.cursor.get().map(this.renderItem)}</ul>;
}
});
// Component can emit an update upstream
var ColorsAdder = React.createClass({
handleClick: function() {
var color = this.refs.color.getDOMNode().value;
cursor.push(color);
// Same thing as
cursor.update({$push: color});
// Same thing as
cursor.update(cursor.get().concat(color));
},
render: function() {
return (
<div>
<input ref="color" type="text" />
<button onClick={this.handleClick} type="button">Add</button>
</div>
);
}
});
controller.mixin
would be the same as cursor().root().mixin
in a way.Immutable-js from Facebook. Cursor implementation.
Reflexions about cursors as a discussion starter.