Skip to content

Instantly share code, notes, and snippets.

@developit
Last active August 13, 2018 09:52
Show Gist options
  • Save developit/286b5562c70798e1ab2e to your computer and use it in GitHub Desktop.
Save developit/286b5562c70798e1ab2e to your computer and use it in GitHub Desktop.
Mithril.js for React People
<script src="http://lhorie.github.io/mithril/mithril.js"></script>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<!-- we'll mount the component here -->
<div id="demo"></div>

Mithril.js for React People

This is a really basic "todo list" demo built with Mithril.

It's largely an ES6/ES2015 port of the Mithril's Getting Started guide, with a bit of sugar and opinion thrown in to make it look and feel a lot like React.

In this case we're using Babel's JSX transform to convert inline XML into hyperscript (just nested calls to Mithral's m() function).

A Pen by Jason Miller on CodePen.

License.

/** @jsx m */
// convenient aliases
let { prop, withAttr } = m;
/** A more React-like component mount() wrapper */
function mount(component, parent) {
if (typeof component==='function' && component.prototype.render) {
component = new component();
}
m.mount(parent, {
controller: λ => component,
view: λ => component.render()
});
}
/** Just a demo init method, delayed until everything is in place. */
setTimeout(function init() {
mount(TodoComponent, document.getElementById('demo'));
}, 1);
/** Model for a single To-Do entry */
class Todo {
constructor({ description }) {
this.description = prop(description);
this.done = prop(false);
}
}
/** A list of To-Do entries is just an Array */
class TodoList extends Array {}
/** A component is both a View-Model and a Controller.
* Like a React component, it's anything with a render() method.
*/
class TodoComponent {
/** Instead of initialize() or componentWillMount(), use a constructor.
* This is called just before the component gets mounted to the DOM.
*/
constructor() {
this.list = new TodoList();
this.description = prop('');
}
/** People argue about whether or not to pass arguments to actions.
* In this case, let's just support both.
*/
add(description=this.description()) {
if (!description) return;
// Add a new todo and clear the input. Triggers a single batched render().
this.list.push(new Todo({ description }));
this.description('');
}
/** Nearly identical to React, just we've moved actions into the controller for simplicity. */
render() {
let tasks = this.list.map( task => (
<tr done={ task.done() }>
<td>
<input type="checkbox"
value={ task.done() }
onclick={ withAttr('checked', task.done) } />
</td>
<td>{ task.description() }</td>
</tr>
));
return (
<todo>
// Below is just a proxy handler that always returns false. ES6 is beautiful.
<form onsubmit={ e => (this.add(), false) }>
<input class="form-control" placeholder="New Todo..."
// could have been written: e => this.description(e.target.value)
onchange={ withAttr('value', this.description) }
value={ this.description() } />
<button class="btn btn-primary">Add</button>
</form>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th width="50">Done?</th>
<th>Description</th>
</tr>
</thead>
<tbody>{ tasks }</tbody>
</table>
</todo>
);
}
};
form {
margin: 5px 0;
input.form-control {
float: left;
width: 20em;
margin-right: 5px;
}
}
tr[done="true"] {
opacity: 0.3;
text-decoration: line-through;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment