Last active
August 19, 2017 19:57
-
-
Save vitalipe/d1526ed377855d4aafff7fc29df62518 to your computer and use it in GitHub Desktop.
mobx+immutable-basic-todo-mvc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // simple data manipulation functions, in a real app you will need more.. | |
| // Immutable.js the worst API possible for function composition, but they implemented | |
| // enough Clojure functions to be actually useful for this example, so let's patch a few: | |
| const _patchShittyDotNotation = _.curry((method, target, ...args) => target[method](...args), 3); | |
| export const strictEquals = _.curry((x,y) => x === y, 2); | |
| export const findIndex = (coll, item) => coll.findIndex(strictEquals(item)); | |
| export const filterNot = _patchShittyDotNotation("filterNot"); | |
| export const push = _patchShittyDotNotation("push"); // better to replace this with conj, because it's not polymorphic | |
| export const mergeIn = _patchShittyDotNotation("mergeIn"); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // this is the actual app code.. | |
| const {Record, List} = Immutable; | |
| const TodoItem = Record({ | |
| title : "", | |
| completed : false | |
| }); | |
| // here we define a store with initial state & computed props | |
| export const store = new StoreAtom({ | |
| todos : List([ | |
| new TodoItem({title : "create x"}), | |
| new TodoItem({title: "create Y" , completed :true})]), | |
| // mobx computed! | |
| unfinishedTodoCount() {return this.todos.filter(todo => !todo.completed).size} | |
| }); | |
| // actions are simply functions, but they can data as well, simply remove the "swapIn" | |
| // call and push into a queue.. I personaly prefer the explicit approach. | |
| // also note that all those are just basic CRUD actions, you can auto generate them.. | |
| export const todoItemDestroy = (item) => store.swapIn("todos", filterNot, strictEquals(item)); | |
| export const todoItemCreate = (title) => store.swapIn("todos", push, new TodoItem({title})); | |
| export const todoItemUpdate = (item, data) => store.swapIn("todos", mergeIn, [findIndex(store.todos, item)], data); | |
| export const todoItemToggle = (item) => todoItemUpdate(item, {completed : !item.get("completed")}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // StoreAtom is a mutable ref that holds the immuable data, it's basically a shitty | |
| // version of reagent/atom.. but becuase mobx is awesome it works :) | |
| export defualt function StoreAtom(schema) { | |
| const _state = mobx.observable(schema); | |
| // expose schema as getter | |
| Object.defineProperties(this, | |
| _(schema) | |
| .reduce((props, v, k) => _.extend( | |
| props, {[k] : { | |
| get : function() { // yeah I know, it's lazy :P | |
| return _.isFunction(v) ? _state[k]() : _state[k]}} | |
| }), {})); | |
| // kina like (swap! ...) in clojure. currently deep path is not supported, but it's easy to add | |
| this.swapIn = mobx.action((path, action, ...args) => _state[path] = action(_state[path], ...args)); | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // basically copy-pasted from https://jsfiddle.net/mweststrate/wv3yopo0/ | |
| // just added a delete button and a way to add new todos | |
| const {observable, computed} = mobx; | |
| const {observer} = mobxReact; | |
| const {Component} = React; | |
| @observer | |
| class TodoListView extends Component { | |
| constructor(props) { | |
| super(props); | |
| this.state = {text : "new item"} | |
| } | |
| render() { | |
| return <div> | |
| <ul> | |
| {this.props.todoList.todos.map(todo => | |
| <TodoView todo={todo} key={todo.id} /> | |
| )} | |
| </ul> | |
| <div> | |
| Tasks left: {this.props.todoList.unfinishedTodoCount} | |
| </div> | |
| <hr /> | |
| <div> | |
| Add Item:<input type="text" | |
| value={this.state.text} | |
| onChange={(e)=> this.setState({text : e.target.value})} /> | |
| <button | |
| onClick={()=> todoItemCreate(this.state.text)}>add</button> | |
| </div> | |
| </div> | |
| } | |
| } | |
| const TodoView = observer(({todo}) => | |
| <li> | |
| <input | |
| type="checkbox" | |
| checked={todo.completed} | |
| onClick={() => todoItemToggle(todo)} | |
| /> | |
| <label>{todo.title}</label> | |
| <button onClick={()=> todoItemDestroy(todo)}>delete</button> | |
| </li> | |
| ); | |
| ReactDOM.render(<TodoListView todoList={store} />, document.getElementById('mount')); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
run it -> https://jsfiddle.net/901ymszb/1/