Reactéçºè
ã¯æ©èœçãªã¢ãããŒãã«åŒãå¯ããããŸãããMobXã®ç»å Žã«ãããå€ããå°ãªããããªãã¿ã®OOPã¹ã¿ã€ã«ã§ç¶æ
ãæäœããããšãå¯èœã«ãªããŸããã Mobxã¯ãéåžžã®ãªããžã§ã¯ããšåæ§ã«ãã¢ãŒããã¯ãã£ã匷å¶ããªãããã«ããŠããªã¢ã¯ãã£ãç¶æ
ã§åäœã§ããããã«ããŸãã åæã«ã圌ã¯C = A + B
ãæžãã®ã«ååãªãšãã«èšç®ã®èªåãªã³ã¯ãè¡ãã C = A + B
æŽæ°A
ãšC
ãæŽæ°ãããããã«ããŸãã
HelloWorldã§ã¯ã·ã³ãã«ã«èŠããŸããããã§ãããè¿œå ããããŠã³ããŒãã¹ããŒã¿ã¹ãšãšã©ãŒåŠçã衚瀺ãããšãå€ãã®ã³ããŒããŒã¹ããååŸãããfromPromiseãŸãã¯lazyObservableãªã©ã®ãã«ããŒãã³ãŒãã«ãªãŒã¯ãå§ããããšãããããŸãã ãããŠãéåææ§ããªããã®ããã«ã³ãŒããæžãããšã¯ãã§ã«äžå¯èœã§ãã MobXã®ããã€ãã®åæ§ã®äŸã解æããç䌌åæã®ã¢ã€ãã¢ãéçºããããšã«ããããã®åºæ¬æŠå¿µãæ¹åããããšèããŠããŸãã
ããŒã¿ã®èªã¿èŸŒã¿
MobXãšReactã®æãç°¡åãªTo Doãªã¹ããæ€èšããŠãã ããã
const {action, observable, computed} = mobx; const {observer} = mobxReact; const {Component} = React; let tid = 0 class Todo { id = ++tid; @observable title; @observable finished = false; constructor(title) { this.title = title; } } function fetchSomeTodos(genError) { return new Promise((resolve) => setTimeout(() => { resolve([ new Todo('Get Coffee'), new Todo('Write simpler code') ]) }, 500)) } class TodoList { @observable todos = []; @computed get unfinishedTodoCount() { return this.todos.filter(todo => !todo.finished).length; } @action fetchTodos() { fetchSomeTodos() .then(todos => { this.todos = todos }) } } const TodoView = observer(({todo}) => { return <li> <input type="checkbox" checked={todo.finished} onClick={() => todo.finished = !todo.finished} />{todo.title} </li> }) TodoView.displayName = 'TodoView' @observer class TodoListView extends Component { componentDidMount() { this.props.todoList.fetchTodos() } render() { const {todoList} = this.props return <div> <ul> {todoList.todos.map(todo => <TodoView todo={todo} key={todo.id} /> )} </ul> Tasks left: {todoList.unfinishedTodoCount} </div> } } const store = new TodoList() ReactDOM.render(<TodoListView todoList={store} />, document.getElementById('mount'))
åçŽãªå Žåãã³ã³ããŒãã³ãã¯componentWillMountãä»ããŠããŒã¿ã®ããŒããéå§ããå¿ èŠããããŸãã todoListã䜿çšããæ°ããã³ã³ããŒãã³ããäœæãããã³ã«ãããã°ã©ãã¯todoList.todosãããŒãããå¿ èŠãããããšã«çæããå¿ èŠããããŸãã ãããè¡ãããªãå Žåã誰ããã§ã«ãã®ããŒã¿ãã¢ããããŒãããããšãä¿èšŒããŸããïŒ
ãã¡ãããèµ·åã®ããã«componentWillMountã䜿çšããã«ç¶æ ãšUIãããé©åã«åé¢ã§ããŸãã MobXã®èè ã§ããMichel Weststrateãèšäºãç¶æ ãšUIãåé¢ããæ¹æ³ãã§è¿°ã¹ãŠããããšã ããŒãžãéããšããã®ã¬ã³ããªã³ã°ã«å¿ èŠãªãã¹ãŠã®ããŒã¿ããµãŒããŒããèŠæ±ãããŸãã ãããŠèè ã¯ããã®ããŠã³ããŒããåæåãã責任ãã«ãŒã¿ãŒã«ç§»ãããšãææ¡ããŠããŸãã
import { createHistory } from 'history'; import { Router } from 'director'; export function startRouter(store) { // update state on url change const router = new Router({ "/document/:documentId": (id) => store.showDocument(id), "/document/": () => store.showOverview() }).configure({ notfound: () => store.showOverview(), html5history: true }).init() }
ãã®ã¢ãããŒãã«ã¯åé¡ããããŸããã«ãŒã¿ãŒã¯ãéãããŒãžã«è¡šç€ºãããã³ã³ããŒãã³ãã«å¿
èŠãªç¹å®ã®ããŒã¿ãç¥ãå¿
èŠããããŸãã ã³ãŒãã®ãã®æç¹ã§store.showOverview
ã¡ãœãããåŒã³åºããšãã«ãã»ã«åã«éåããŸãã ãªãã¡ã¯ã¿ãªã³ã°äžã«ããµãŒããŒããäœããååŸããå¿
èŠãããããŒãžã«æ°ããã³ã³ããŒãã³ããè¿œå ãããŠããããŠã³ããŒããã«ãŒã¿ãŒã«è¿œå ãããªãå Žåã¯ã©ããªããŸããïŒ ã¹ãã¢ã§ã®äœæ¥ã®è©³çŽ°ã¯ã¢ããªã±ãŒã·ã§ã³å
ã®ããŸããŸãªå Žæã«åºãã£ãŠãããããããã§ééããç¯ãã®ã¯ç°¡åã§ãã
fetchTodosïŒïŒã®åŒã³åºãã¯componentWillMountã«ããå¿ èŠã¯ãããŸããã redux-sagaã®äŸã®ããã«ãäžéšã®ãã¿ã³ã§onClickãåŒã³åºããåŸãindex.jsã§çŽæ¥åŒã³åºãããåŸã§ããã«ãŒã¿ãŒã®èåŸã§HOCã«å€è£ ããããšãã§ããŸãã
... import rootSaga from './sagas' const store = configureStore(window.__INITIAL_STATE__) store.runSaga(rootSaga) ...
store.runSaga(rootSaga)
ã¯ãã¢ããªã±ãŒã·ã§ã³ã®åäœã«å¿
èŠãªãã¹ãŠã®ããŒã¿ã®ããŠã³ããŒããããã«éå§ããŸãã
äžçªäžã®è¡ã¯ãããã°ã©ããŒãããŠã³ããŒããéå§ããã³ãŒãå ã®å Žæããããšããããšã§ãã ãããŠããã®å Žæã¯ã¢ãã«ã®å€ããŸãã¯ã¢ãã«ã®ä»£ããïŒsagasãªã©ïŒã«ãªããŸãããåæååŒã³åºãã®äºå®ã®æå³ã¯ãããã¯ãŒã¯ã§ã®äœæ¥ã®å éšè©³çŽ°ã«ãããŸããã éåææ§ãåé€ãããšãäžèŠã«ãªããŸãã ããã«ããã®ãããªãœãªã¥ãŒã·ã§ã³ã§ã®ããŒãã¯ãã³ã³ããŒãã³ãããã®ããŒã¿ã«ã¢ã¯ã»ã¹ãããšããäºå®ã§ã¯ãªããäºåã«è¡ãããŸãã
éåæèªã¿èŸŒã¿ãšã©ãŒåŠç
MobXã§ã¯ããšã©ãŒãšããŠã³ããŒãã¹ããŒã¿ã¹ã¯ããèªäœã§ã¯ã€ã³ã¿ãŒãã§ã€ã¹ã«å°éããŸããã ãããã衚瀺ããã«ã¯ãããŒããããåãšã³ãã£ãã£ã®ã¹ãã¢ã«ãšã©ãŒããããã£ãäœæããå¿ èŠããããŸãã todoList.todosã䜿çšããåã³ã³ããŒãã³ãã§ã¯ããã®ããããã£ã®åŠçãè¡ãå¿ èŠããããŸããã»ãšãã©ã®å Žåã¯åãã§ããdevã¢ãŒãã§ã©ãã«ãŸãã¯ã¹ã¿ãã¯ãã¬ãŒã¹ã衚瀺ããŸãã ããã°ã©ãããããã®åŠçãå¿ãããšããŠãŒã¶ãŒã«ã¯äœã衚瀺ãããããäœããããããããšããç¢æã衚瀺ãããŸããã
class TodoList { @observable todos = [] @observable error: ?Error @observable pending = false @action fetchTodos(genError) { this.pending = true this.error = null fetchSomeTodos(genError) .then(todos => { this.todos = todos; this.pending = false }) .catch(error => { this.error = error; this.pending = false }) } } @observer class TodoListView extends Component { componentWillMount() { this.props.todoList.fetchTodos() } render() { const {todoList} = this.props return <div> {todoList.pending ? 'Loading...' : null} {todoList.error ? todoList.error.message : null} ... </div> } }
fromPromiseã䜿çšããŸã
åã®äŸã«ã¯ãã¹ãã¢ãšã³ã³ããŒãã³ãã®äž¡æ¹ã«å€ãã®å®åã³ãŒãããããŸãã ã³ããŒãšè²Œãä»ããæžããã«ã¯ã mobx-utilsã® fromPromiseãã«ããŒã䜿çšã§ããŸãããã®ãã«ããŒã¯ãå€ãšãšãã«ãã®å€ã®ããŠã³ããŒãã¹ããŒã¿ã¹ãæäŸããŸãã ããã圌ã®äœåã®ãã¢ã³ã¹ãã¬ãŒã·ã§ã³ã®äŸã§ãïŒ
class TodoList { @observable todoContainer constructor() { this.fetchTodos() } // ... @action fetchTodos(genError) { this.todoContainer = fromPromise(fetchSomeTodos(genError)) } } const StatusView = ({fetchResult}) => { switch(fetchResult.state) { case "pending": return <div>Loading...</div> case "rejected": return <div>Ooops... {JSON.stringify(fetchResult.value)}</div> } } const TodoListView = observer(({todoList}) => { const todoContainer = todoList.todoContainer return <div> {todoContainer.state === 'fulfilled' ? ... : <StatusView fetchResult={todoContainer}/> } ... </div> })
å€ãšã¹ããŒã¿ã¹ãå«ãtodoContainerããããã£ãæ¢ã«ãããŸãã ã³ã³ããŒãã³ãã§ã®åŠçã¯ãã§ã«ç°¡åã§ãã äžèšã®äŸã§ã¯ãTodoListã¹ã¿ãã¯ã®ã³ã³ã¹ãã©ã¯ã¿ãŒã§fetchTodosã®åŒã³åºããè¡ãããŸãã ã«ãŒãã£ã³ã°ã®äŸãšã¯ç°ãªããããã«ãããfetchTodosãå€éšã«å ¬éããã«å®è£ ã®è©³çŽ°ãããé©åã«ã«ãã»ã«åã§ããŸãã fetchTodosã¡ãœããã¯ãTodoListå®è£ ã®ãã©ã€ããŒãéšåã®ãŸãŸã§ãã
ãã®ã¢ãããŒãã®çæïŒ
- èªã¿èŸŒã¿ã®
new TodoList()
ãå£ããnew TodoList()
ããµãŒããŒã«ãªã¯ãšã¹ããéä¿¡ããŸã - ã³ã³ããŒãã³ãã§ã¯ãããŠã³ããŒãã¹ããŒã¿ã¹ã®ãã§ãã¯ãæ¿å ¥ãã察å¿ããã¡ãã»ãŒãžã衚瀺ããå¿ èŠããããŸãã
- ããŠãã³ã³ããŒãã³ãå ã«ã®ã¿ããå Žåã å®éã®ã¢ããªã±ãŒã·ã§ã³ã§ã¯ãå€ãã®ããŒã¿ãœãŒã¹ãååšããå¯èœæ§ãããããããã®ãã¹ãŠãã³ã³ããŒãã³ãã«çŽæ¥ããŒã«ãããããã§ã¯ãªããèšç®ãããå€ãä»ããŠå€æããããã®ããããŸãã ãã®ãããªåå€ã§ã¯ãããŒã¿ã䜿çšããã¢ã¯ã·ã§ã³ã®åã«ãåžžã«ã¹ããŒã¿ã¹ã確èªããå¿ èŠããããŸãã äžèšã®äŸã®unfinishedTodoCountã¡ãœããã®ããã«
class TodoList { //... @computed get unfinishedTodoCount() { return this.todoContainer.value ? this.todoContainer.value.filter(todo => !todo.finished).length : [] } //... }
lazyObservableã䜿çšãã
ïŒæ°ããTodoListã§ã¯ãªãïŒã³ã³ããŒãã³ããã¬ã³ããªã³ã°ãããšãã«ãæåŸã®äŸã®é 延ãããŒãããã«ã¯ãfromPromiseãmobx-utilsã®lazyObservableãã«ããŒã«ã©ããããŸãã ããŠã³ããŒãã¯ãã³ã³ããŒãã³ãã§todoContainer.currentïŒïŒãå®è¡ãããåŸã«éå§ãããŸãã
class TodoList { constructor() { this.todoContainer = lazyObservable(sink => sink(fromPromise(fetchSomeTodos()))) } @computed get unfinishedTodoCount() { const todos = this.todoContainer.current() return todos && todos.status === 'fulfilled' ? todos.filter(todo => !todo.finished).length : [] } } const StatusView = ({fetchResult}) => { if (!fetchResult || fetchResult.state === 'pending') return <div>Loading...</div> if (fetchResult.state === 'rejected') return <div>{fetchResult.value}</div> return null } const TodoListView = observer(({todoList}) => { const todoContainer = todoList.todoContainer const todos = todoContainer.current() return <div> {todos && todos.state === 'fulfilled' ? <div> <ul> {todos.value.map(todo => <TodoView todo={todo} key={todo.id} /> )} </ul> Tasks left: {todoList.unfinishedTodoCount} </div> : <StatusView fetchResult={todos}/> } <button onClick={() => todoContainer.refresh()}>Fetch</button> </div> })
lazyObservableãã«ããŒã¯ãé
延ã®åé¡ã解決ããŸãããã³ã³ããŒãã³ãã®å®åã³ãŒãããããªããæããŸããã ãããŠã lazyObservable(sink => sink(fromPromise(fetchSomeTodos())))
ã¯ã fetchSomeTodos().then(todos => this.todos = todos)
ããã«èŠããã®ã¯ããã»ã©ç°¡åã§ã¯ãããŸããfetchSomeTodos().then(todos => this.todos = todos)
ããªã¹ãã®æåã®ããŒãžã§ã³ã®fetchSomeTodos().then(todos => this.todos = todos)
代æ¿æ¡
ãéåææ§ããªããã®ããã«æžãããšããèããèŠããŠãããŠãã ããã MobXãããå ã«é²ããšã©ããªããŸããïŒ èª°ããããããã§ã«ã§ããŸããïŒ
ãããŸã§ã®ãšããã mol_atomã¯æãé²ãã§ããŸãã ãã®ã©ã€ãã©ãªã¯ã ãŽã£ã³ããŒãžã®molãã¬ãŒã ã¯ãŒã¯ã®äžéšã§ãã ããã§ãããã§ãèè ã¯åœŒãšåœŒã®ä»äºã®ååïŒ Objective Reactive ProgrammingãŸãã¯PIUãªã©ïŒã«ã€ããŠå€ãã®èšäºãæžããŠããŸãã ã¢ã«ã¯ãä»ã®ã©ãã«ãèŠãããªã圌ã®ãªãªãžãã«ã®ã¢ã€ãã¢ã«èå³ãæã£ãŠããŸãã åé¡ã¯ãç¬èªã®ãšã³ã·ã¹ãã ãããããšã§ãã mol_atomã䜿çšããŠãreactãwebpackãªã©ã®ãããžã§ã¯ãã§äœ¿çšãéå§ããããšã¯ã§ããŸããããããã£ãŠãç§ã¯ç¬èªã®å®è£ lom_atomãäœæããå¿ èŠããããŸããã å®éãããã¯ãmol_atomã®é©å¿ã§ãããreactã§äœ¿çšããããã«ã·ã£ãŒãåãããŠããŸãã
é 延æŽæ°
lomã®todoãªã¹ãã䜿çšããåæ§ã®äŸãèããŠã¿ãŸãããã ãŸããã³ã³ããŒãã³ãã®ããåŽãèŠãŠã¿ãŸãããã
/** @jsx lom_h */ //... class TodoList { @force $: TodoList @mem set todos(next: Todo[] | Error) {} @mem get todos() { fetchSomeTodos() .then(todos => { this.$.todos = todos }) .catch(error => { this.$.todos = error }) throw new mem.Wait() } // ... } function TodoListView({todoList}) { return <div> <ul> {todoList.todos.map(todo => <TodoView todo={todo} key={todo.id} /> )} </ul> Tasks left: {todoList.unfinishedTodoCount} </div> }
ããã§ã¯æ¬¡ã®ããšãçºçããŸãã
- ã¬ã³ããªã³ã°ãããTodoListViewã
- ãã®ã³ã³ããŒãã³ãã¯todoList.todosã«å€ããã
get todos()
ãæ©èœãããµãŒããŒããããŒã¿ãããŒãããã³ãŒããå®è¡ãããŸãã - ããŒã¿ã¯ãŸã å°çããŠããªãã®ã§ãããã«ã³ã³ããŒãã³ãã衚瀺ããå¿
èŠããããŸãã ããã§ãããã©ã«ãå€ãè¿ãããäŸã®ããã«äŸå€ãã¹ããŒããããšãã§ããŸãïŒ
throw new mem.Wait()
ã - memãã³ã¬ãŒã¿ã¯ãããã€ã³ã¿ãŒã»ããããtodosã«ã¯TodoListViewã®ãããã·ãä»å±ããŠããŸãã
- ãã®ããããã£ã®ããããã«ã¢ã¯ã»ã¹ãããšãTodoListViewå ã§äŸå€ãã¹ããŒãããŸãã
- ãªãŒããŒã©ã€ããããcreateElementããã®ã³ã³ããŒãã³ããã©ãããããã®ã©ãããŒãäŸå€ããã£ãããããããã©ã€ãã©ãªèšå®ã«ãã£ãŠèšå®ãããErrorableViewã衚瀺ãããŸãã
- ãµãŒããŒããããŒã¿ãéä¿¡ããããšã
this.$.todos = todos
ïŒthisã$-set todos() {}
åŒã³åºãããã€ãã¹ããŠãã£ãã·ã¥ã«æžã蟌ãããšãæå³ããŸãïŒã
ErrorableViewã«ã¯æ¬¡ã®ãããªã³ã³ãã³ããå«ãŸããŸãã
function ErrorableView({error}: {error: Error}) { return <div> {error instanceof mem.Wait ? <div> Loading... </div> : <div> <h3>Fatal error !</h3> <div>{error.message}</div> <pre> {error.stack.toString()} </pre> </div> } </div> }
ã©ã®ã³ã³ããŒãã³ããšã©ã®ããŒã¿ã䜿çšãããŠããŠããããã©ã«ãã®åäœã¯ãã¹ãŠã®ãŠãŒã¶ãŒã§åãã§ããäŸå€ãé€ãããããïŒmem.Waitã®å ŽåïŒãŸãã¯ãšã©ãŒããã¹ãã衚瀺ãããŸãã ãã®åäœã«ãããã³ãŒããšç¥çµãå€§å¹ ã«ç¯çŽãããŸãããå Žåã«ãã£ãŠã¯åå®çŸ©ããå¿ èŠããããŸãã ãããè¡ãã«ã¯ãã«ã¹ã¿ã ErrorableViewãæå®ã§ããŸãã
function TodoListErrorableView({error}: Error) { return <div>{error instanceof mem.Wait ? 'pending...' : error.message}</div> } //... TodoListView.onError = TodoListErrorableView
try / catch todoList.todosã§ã©ããããããšã§ãTodoListViewå ã§äŸå€ãç°¡åã«ãã£ããã§ããŸãã ã³ã³ããŒãã³ãã§ã¹ããŒãããäŸå€ã¯ãã³ã³ããŒãã³ãã®ã¿ãããããããErrorableViewãæç»ããŸãã
function TodoView({todo}) { if (todo.id === 2) throw new Error('oops') return <li>...</li> }
ãã®äŸã§ã¯ã2çªç®ã®ã¿ã¹ã¯ã®ä»£ããã«èŽåœçãšã©ãŒã®ã¿ã衚瀺ãããŸãã
ãã®ãããªäŸå€ã¢ãããŒãã«ã¯ã次ã®å©ç¹ããããŸãã
- äŸå€ã¯èªåçã«åŠçããïŒTodoListã«ã¯this.errorã¯ãªããªããŸããïŒããŠãŒã¶ãŒã«ãšã©ãŒã¡ãã»ãŒãžã衚瀺ãããŸãã
- äŸå€ã¯ã¢ããªã±ãŒã·ã§ã³å šäœãç Žå£ããã®ã§ã¯ãªããçºçããã³ã³ããŒãã³ãã®ã¿ãç Žå£ããŸãã
- äŸå€ãšåæ§ã«ãããŠã³ããŒãã¹ããŒã¿ã¹ã¯èªåçã«åŠçãããŸãïŒTodoListã«ã¯this.statusã¯ãªããªããŸããïŒã
- ã¢ã€ãã¢ã¯éåžžã«åçŽãªã®ã§ãéåæã³ãŒããæ¬äŒŒåæã³ãŒãã«å€æããããã«fromPromiseãlazyObservableãªã©ã®ãã«ããŒã¯å¿
èŠãããŸããã ãã¹ãŠã®éåææäœã¯
get todos()
ãã³ãã©ãŒã«ã«ãã»ã«åãããŸãã - ã³ãŒãã¯ã»ãŒåæããŠããããã«èŠããŸãïŒãã§ãããé€ããŸããããã®äžã§ãæ¬äŒŒåæ圢åŒã§èšè¿°ã§ããã©ãããŒãäœæã§ããŸãïŒã
MobXãšæ¯èŒããŠããã€ã©ãŒãã¬ãŒãã¯ã¯ããã«å°ãããªã£ãŠããŸãã åè¡ã¯ããžãã¹ããžãã¯ã®è¡ã§ãã
ãã³ããããã³ã°ããŠã³ããŒã
ãããŠã1ã€ã®ã³ã³ããŒãã³ãã«ããŒããããè€æ°ã®ãšã³ãã£ãã£ã衚瀺ãããšãã€ãŸããtodoã«å ããŠãããšãã°ãŠãŒã¶ãŒããŸã ããå Žåã¯ã©ããªããŸããã
class TodoList { @force $: TodoList @mem set users(next: {name: string}[] | Error) {} @mem get users() { fetchSomeUsers() .then(users => { this.$.users = users }) .catch(error => { this.$.users = error }) throw new mem.Wait() } //... } function TodoListView({todoList}) { const {todos, users} = todoList //... todos.map(...) users.map(...) }
TodoListViewã®æåã®ã¬ã³ããªã³ã°ã§ãtodosãšãŠãŒã¶ãŒãããŒããããŠããªãå Žåã代ããã«ãããã·ãªããžã§ã¯ããã³ã³ããŒãã³ãã«å±ããŸãã ã€ãŸãã const {todos, users} = todoList
ã get todos()
ããã³get users()
ãå®è¡ããããããã®äžŠåããŒããéå§ãããmem.Waitãã¹ããŒãããmemããããã·ã§äŸå€ãã©ããããŸãã ã³ã³ããŒãã³ãã§ã¯ãtodos.mapããããã£ãŸãã¯users.mapã«ã¢ã¯ã»ã¹ãããšãmem.WaitäŸå€ãã¹ããŒãããErrorableViewãã¬ã³ããªã³ã°ãããŸãã ããŒãåŸãã³ã³ããŒãã³ãã¯åã³ã¬ã³ããªã³ã°ãããŸãããå®éã®ããŒã¿ã¯TodoãšãŠãŒã¶ãŒã«è¡šç€ºãããŸãã
ããã¯molãåæã³ãŒããšåŒãã§ããŸãããéããããã³ã°èŠæ±ã§ãã
ãã®ã¢ãããŒãã«ã¯æ¬åœã«ãã€ãã¹ããããŸããæåã«todoListããtodoãšãŠãŒã¶ãŒãåé€ããŠããäœæ¥ããå¿ èŠããããŸããããããªããšãé 次èªã¿èŸŒã¿ãè¡ãããæé©åã倱æããŸãã
ãã£ãã·ã¥ç®¡ç
äžèšã®äŸã¯éåžžã«åçŽã§ãã memãã³ã¬ãŒã¿ã¯éåžžã«ã¹ããŒããªãã£ãã·ã¥ã§ããã€ãŸããtodoã1åèµ·åãããå Žåã2åç®ã«memããã£ãã·ã¥ããããããè¿ããŸãã
ãã£ãã·ã¥ãããããã set todos
ãã³ãã©ãŒããã€ãã¹ããŠããã£ãã·ã¥ã«æžã蟌ãããšãã§ããã¯ãã§ãã ãã®ããããã£ãã·ã¥ã®ç¡å¹åã®åé¡ããããŸãã äŸåé¢ä¿ãå€æŽãããå Žåãå€ãèªåçã«ãªã»ããããæ¹æ³ãå¿
èŠã§ãããŸãããã¿ã³ãæŒããªã©ããŠããŒã¿ãåã¹ãã¬ããããå¿
èŠãããå Žåã¯ãå€ãæåã§ãªã»ããã§ããå¿
èŠããããŸãã
äŸåé¢ä¿ãå€æŽãããã³ã³ããŒãã³ãã®æŽæ°ãMobXãšåæ§ã«åŠçãããå Žåã®ã¯ãªãŒã³ã¢ããã ãŸããæåã®ãã£ãã·ã¥ç®¡çã®åé¡ã¯ã匷å¶ãã³ã¬ãŒã¿ãŒã«ãã£ãŠè§£æ±ºãããŸãã 圌ã®ä»äºã¯æ¬¡ã®äŸã§ç€ºãããŠããŸãã
class TodoList { @force forced: TodoList // .. } function TodoListView({todoList}) { return <div> ... <button onClick={() => todoList.forced.todos}>Reset</button> </div>
[ãªã»ãã]ãã¿ã³ãã¯ãªãã¯ãããšãtodoList.forced.todosãèŠæ±ãããŸããããã«ããã get todos
ãç¡æ¡ä»¶ã«å®è¡ããããã£ãã·ã¥ãè£å
ãããŸãã todoList.forced.todosã«å€ãå²ãåœãŠããšã set todos
ãã³ãã©ãŒããã€ãã¹ããŠãå€ããã£ãã·ã¥ã«æžã蟌ãŸããŸãã
äžèšã®ã³ãŒããèŠããŠãããŠãã ããthis.$.todos = todos
ïŒ
/** @jsx lom_h */ //... class TodoList { @force $: TodoList @mem set todos(next: Todo[] | Error) {} @mem get todos() { fetchSomeTodos() .then(todos => { this.$.todos = todos }) .catch(error => { this.$.todos = error }) throw new mem.Wait() } // ... }
ãã£ãã·ã¥ãžã®æžã蟌ã¿ã¯ããã©ã€ããŒãã®get todos
ã¢ã€ãã ã§ãã fetchãããŒã¿ãåä¿¡ãããšã set todos
åŒã³åºãããã€ãã¹ããŠããã£ãã·ã¥ã«çŽæ¥æžã蟌ã¿ãŸãã å€éšããtoListã$ãTodosãžã®æžã蟌ã¿ã¯èš±å¯ãããŠããŸããã ãã ãããã£ãã·ã¥ã®ãã©ãã·ã¥ïŒtodoListã$ãTodosã®èªã¿åãïŒã¯ãå€éšããéå§ããŠèŠæ±ãç¹°ãè¿ãããšãã§ããŸãã
çŸåšã®åã§èŠããæ¹æ³ã¯æãçŽæçãªè§£æ±ºçã§ã¯ãããŸããããã³ãŒãã«ãã«ããŒãå°å ¥ããããšã¯ãªããã¯ã©ã¹ããããã£ã®ã€ã³ã¿ãŒãã§ã€ã¹ãå®éã«æªããããšã¯ãããŸããïŒã¡ãœããã§ãã¹ãŠãè¡ãå¿ èŠã¯ãããŸããïŒãã€ãŸãæ§ãããªãŸãŸã§ãã ãŸããMobXã®ãããªã¢ãããŒãã§å¿ ç¶çã«çºçããã¿ã¹ã¯ã®ã¯ã©ã¹å šäœãéåžžã«ç°¡åã«è§£æ±ºããŸãã ããã§ã®äž»ãªããšã¯ãããã€ãã®ã«ãŒã«ãç解ããããšã§ãã
- todoList.todosã®èªã¿åãã¯ãã£ãã·ã¥ããååŸãããŸãã
- ãã£ãã·ã¥å€ããªã»ããããå Žåã¯ã
todoList.$.todos
ããtodoList.$.todos
ã - æ°ããå€ãæžã蟌ã¿ã
set todos
ãå®è¡ãããããã«ãããå ŽåïŒç°ãªãAPIãæ€èšŒã«ããŒã¿ãä¿åã§ããŸãïŒãtodoList.todos = newTodos
ãŸãã -
set todos
ãå®è¡ããã«å€ããã£ãã·ã¥ã«çŽæ¥æžã蟌ã¿ããå Žåã¯ãtodoListã$ãTodosãå®è¡ããŸãã ããã¯get/set todos
å ã§ã®ã¿å®è¡ã§ããŸãã
èŸæž
MobXã®ããã«ãlom_atomã«ã¯ãªããžã§ã¯ãããããã£ãšé åã®èŠ³æž¬å¯èœãªã©ãããŒã¯ãããŸããã ããããåçŽãªããŒãšå€ã®èŸæžããããŸãã ããšãã°ãåtodoãããããã£ã®ä»£ããã«todoIdã®èª¬æãåå¥ã«ã¢ããããŒãããå¿ èŠãããå Žåãæåã®åŒæ°ã説æããã£ãã·ã¥ãããããŒã2çªç®ã説æèªäœã§ããã¡ãœããã䜿çšã§ããŸãã
class TodoList { // ... @force forced: TodoList @mem.key description(todoId: number, todoDescription?: Description | Error) { if (todoDescription !== undefined) return todoDescription // set mode fetchTodoDescription(todoId) .then(description => this.forced.description(todoId, description)) .catch(error => this.forced.description(todoId, error)) throw new mem.Wait() } } function TodoListView({todoList}) { return <div> <ul> {todoList.todos.map(todo => <TodoView todo={todo} desc={todoList.description(todo.id)} reset={() => todoList.forced.description(todo.id)} key={todo.id} /> )} </ul> // ... </div> }
todoList.description(todo.id)
ãå®è¡ãããšãã¡ãœããã¯get todos
ãšåæ§ã«ã²ãã¿ãŒãšããŠæ©èœãget todos
ã
ã¡ãœããã¯1ã€ãããªããé¢æ°2ã¯get / setãªã®ã§ãå
éšã«ãã©ã³ãããããŸãã
if (todoDescription !== undefined) return todoDescription // set mode
ã€ãŸãã todoDescription !== undefined
å Žåãã¡ãœããã¯ã»ãã¿ãŒãšããŠåŒã³åºãããŸãïŒ todoList.description(todo.id, todo)
ã ããŒã¯ä»»æã®ã·ãªã¢ã«åå¯èœãªã¿ã€ãã«ããããšãã§ãããªããžã§ã¯ããšé
åã¯ããã©ãŒãã³ã¹ã®äœäžã䌎ãããŒã«ã·ãªã¢ã«åãããŸãã
ãªãMobXãªã®ãïŒ
æåã«MobXã«ã€ããŠè©±ãå§ããã®ã¯ãªãã§ããïŒ äºå®ãéåžžãããžãã¹èŠä»¶ã§ã¯éåææ§ã«ã€ããŠã¯äœããããŸãã-ãããã¯ããŒã¿åŠçã®å®è£ ã®å人çãªè©³çŽ°ã§ãããã¹ããªãŒã ãçŽæãéåæ/åŸ æ©ããã¡ã€ããŒãªã©ãéããŠå¯èœãªéãããããæ¹æ³ã§æœè±¡åããããšããŸããéªéã«ãªããŸããã ããšãã°ãasync / awaitã¯promiseãããéªéã«ãªããŸãããããã¯èšèªæ§é ã§ãããéåžžã®try / catchãæ©èœãããããthen / catchã«é¢æ°ãæž¡ãå¿ èŠã¯ãããŸããã ã€ãŸããéåæ/åŸ æ©ã³ãŒãã¯éåææ§ã®ãªãã³ãŒãã®ããã«èŠããŸãã
ãã®ã¢ãããŒãã®å察ãšããŠãRxJSã«èšåã§ããŸãã ããã§ã¯ãé¢æ°åããã°ã©ãã³ã°ã«çªå ¥ããéãã©ã€ãã©ãªãèšèªã«æã¡èŸŒã¿ããã®APIãåŠç¿ããå¿ èŠããããŸãã èšå€§ãªæ°ã®ã©ã€ãã©ãªæ¡åŒµãã€ã³ãã«æ¿å ¥ããŠåçŽãªèšç®ã®ã¹ããªãŒã ãäœæãããããã¹ãŠã®æäœãé¢æ°ã«çœ®ãæããŸãã ãã ããRxJSããŸã èšèªæšæºã«å«ãŸããŠããå Žåãããã«å ããŠããã«ã¹ããªãŒã ãããŒã³ã³ãã©ã ãããã®ä»å€ãã®é¡äŒŒããã¹ã¿ã€ã«ãååšããŸãã ãŸããããããã«FPã®å®è£ ã«é¢ããç¬èªã®ä»æ§ããããããžãã¹ããžãã¯ãæžãçŽããªããã°å€æŽã§ããªããªããŸãã
Mobxã¯ã芳枬å¯èœãªæ§é ãèšè¿°ããããã®æ°ããä»æ§ãå°å ¥ããŠããŸããã ãã€ãã£ãã¯ã©ã¹ã¯ãã®ãŸãŸã§ããã³ã¬ãŒã¿ã¯ééçã«æ©èœããã€ã³ã¿ãŒãã§ã€ã¹ãæªããŸããã ãã®APIã¯ãèªåããŒã¿ãã€ã³ãã£ã³ã°ã«ããã¯ããã«åçŽã§ãããããŒã¿ã«å¯Ÿããç®ã«èŠããã©ãããŒã¯å€æ°ãããŸããã
ãªãMobXã§ã¯ãããŸãããïŒ
ã³ã³ããŒãã³ãã®ããŒã¿ãåŠçã¹ããŒã¿ã¹ããšã©ãŒã®æŽæ°ãéåææ§ãæŒãããŠããŸããã€ã³ãã©ã¹ãã©ã¯ãã£ã¯ãã»ãšãã©ã®å Žåããµããžã§ã¯ããšãªã¢ã«éæ¥çã«é¢é£ããŠããŸãã MobXã§ã®ãã§ããã®ãªãã¢ããªã±ãŒã·ã§ã³ã¯ã·ã³ãã«ã«èŠããŸããããã®å¿ èŠãªã¬ã€ã€ãŒãè¿œå ãããšãéåæã®è³ãåãµãŒãã¹ãŸãã¯å€ããå°ãªããè€éãªã³ã³ããŒãã³ãããçªãåºå§ããŸãã å®åã³ãŒãããããããã«ããŒãããžãã¹ããžãã¯ãæ··ä¹±ããããéåææ§ããªããã®ããã«æžãããšããã¢ã€ãã¢ã®çŽåºŠãæªåããããã®ããããã§ãã ããŒã¿èªäœã¯è€éã§ãããŒã¿èªäœãè€éã§ããéä¿¡ãã£ãã«å®è£ ã®ã³ã³ããŒãã³ãã¯ããšã©ãŒãããŒã¿ã®ããŠã³ããŒãã¹ããŒã¿ã¹ãªã©ã®ã³ã³ããŒãã³ãã«æŒããŸãã
MobXã®ä»£æ¿ãšããŠãlom_atomã¯ããã«ããŒãå°å ¥ããããšãªããã³ã¢ã§ãããã®åé¡ã解決ããããšããŸãã è©Šè¬ã®æåã«é©å¿ããããã«ã reactive-diã䜿çšãããŸã ïŒã€ãŸããmobx-reactã«äŒŒãŠããŸãïŒã ç§ã¯æåã®èšäºã§åœŒã«ã€ããŠè©±ããŸãããåå¿ã³ã³ããã¹ãã®ã¢ã€ãã¢ãéçºãããã¥ãŒãã³ã°ã®ããã®ããæè»ãªã³ã³ããŒãã³ããHOCã®åå©çšå¯èœãªä»£æ¿ç©ããããŒã¿ã€ããšå°æ¥çã«ã¯å®äŸ¡ãªSOLIDãšã®ããè¯ãçµ±åãååŸããŠã¿ãŠãã ããã
ãŸãšã
åºæ¬æŠå¿µãå°ãæ¹è¯ããããšã§ãWebã®äžè¬çãªã¿ã¹ã¯ã®ã³ãŒããå€§å¹ ã«ç°¡çŽ åãããã³ã³ããŒãã³ããããŒã¿ã®åä¿¡ã®è©³çŽ°ãç¥ãå¿ èŠããªããªãããšããååã®äŸã§ç€ºãããšãã§ãããšæããŸãã ãããŠãããã¯PPRãã§ããããšã®ã»ãã®äžéšã§ãã ç§ã®æèŠã§ã¯ãããã¯ããã°ã©ãã³ã°ã®å šé åã§ãããç¬èªã®ãã¿ãŒã³ãå©ç¹ãæ¬ ç¹ããããŸãã ãŸããKotlinã®mol_atomãMobXã ããªã²ãŒãããããã£ãªã©ãããã®é åã®æŠèŠãèŠã€ããæåã®è©Šã¿ã§ãã 誰ããä»ã®èšèªããšã³ã·ã¹ãã ã§ã®åæ§ã®ã¢ãããŒãã«ã€ããŠäœããç¥ã£ãŠãããªããã³ã¡ã³ãã«æžããŠãã ãããããã¯é¢çœããããããŸããã