Mobx-アプリケーションの状態を管理する

MobXは、アプリケーションのステータスを管理するための簡単な戦闘テスト済みソリューションです。 このチュートリアルでは、MobXの基本概念を学びます。 MobXはスタンドアロンライブラリですが、ほとんどの場合、Reactと組み合わせて使用​​するため、このチュートリアルではこの組み合わせに焦点を当てます。







主なアイデア



状態(state orig。)はすべてのアプリケーションの中心であり、状態の一貫性の欠如のように、タグ付けされた、制御されていないアプリケーションを作成するより速い方法はありません。 または、周囲のローカル変数と矛盾する状態。 したがって、多くの状態管理の決定は、たとえば状態を変更しないなど、変更できる方法を制限しようとしています。 しかし、これは新しい問題を引き起こし、データを正規化する必要があり、参照整合性の保証はなく、プロトタイプ(プロトタイプorig。)などの強力な概念を使用することはほとんど不可能になります。







MobXは、問題の根本に戻ることにより、状態管理を再び簡単にします。状態の不整合を不可能にします。 これを達成するための戦略は非常に簡単です。州から持ち出せるすべてのものを確実に持ち出すようにします。 自動的に。







概念的には、MobXはアプリケーションをスプレッドシートとして処理します(テーブルの操作については、オフィスプログラムを参照してください。約。)







MobXサイクル



シンプルなtodoストア...



十分な理論、それを実際に検討してください。上記を注意深く読むよりもずっと明確です。 独創性のために、非常に単純なTodoリポジトリから始めましょう。 以下は、todoコレクションを管理する非常にシンプルなTodoStoreです。 MobXはまだ関与していません。







import shortid from 'shortid'; class TodoStore { todos = []; get completedTodosCount() { return this.todos.filter( todo => todo.completed === true ).length; } report() { if (this.todos.length === 0) return "<none>"; return `Next todo: "${this.todos[0].task}". ` + `Progress: ${this.completedTodosCount}/${this.todos.length}`; } addTodo(task) { this.todos.push({ id: shortid.generate(), task: task, completed: false, assignee: null }); } } const todoStore = new TodoStore();
      
      





todosコレクションを使用してtodoStore



インスタンスを作成しました。 次に、 todoStore



いくつかのオブジェクトをtodoStore



する必要があります。 変更による効果があることを確認するために、変更のtodoStore.report



を呼び出します。







 todoStore.addTodo("read MobX tutorial"); console.log(todoStore.report()); todoStore.addTodo("try MobX"); console.log(todoStore.report()); todoStore.todos[0].completed = true; console.log(todoStore.report()); todoStore.todos[1].task = "try MobX in own project"; console.log(todoStore.report()); todoStore.todos[0].task = "grok MobX tutorial"; console.log(todoStore.report());
      
      





反応する



これまでのところ、コードに異常はありませんでした。 しかし、 report



明示的に呼び出すのではなく、状態が変化するたびにこのメソッドを呼び出す必要があると宣言した場合はどうでしょうか。 これにより、コードでこのメソッドを呼び出す義務から解放されます。 report



を呼び出した最後の結果が表示されることを確認する必要があります。 しかし、これがどのように行われるか心配する必要はありません。







幸いなことに、これを行うことができるのはMobXです。 状態依存コードを自動的に呼び出します。 したがって、 report



関数は自動的に呼び出されます。 これを実現するには、 TodoStore



がすべての変更を追跡できるように、TodoStoreを監視できる必要があります。 クラスを少し変更しましょう。







また、 completedTodosCount



プロパティはtodos



プロパティから自動的に計算されます。 @observable



および@computed



デコレーターを使用してこれを実現できます。







 import shortid from 'shortid'; class ObservableTodoStore { @observable todos = []; @observable pendingRequests = 0; constructor() { mobx.autorun(() => console.log(this.report)); } @computed get completedTodosCount() { return this.todos.filter( todo => todo.completed === true ).length; } @computed get report() { if (this.todos.length === 0) return "<none>"; return `Next todo: "${this.todos[0].task}". ` + `Progress: ${this.completedTodosCount}/${this.todos.length}`; } addTodo(task) { this.todos.push({ id: shortid.generate(), task: task, completed: false, assignee: null }); } } const observableTodoStore = new ObservableTodoStore();
      
      





以上です! 一部のプロパティを@observable



としてマークし、MobXが時間とともに変化する可能性があることを認識できるようにしました。 計算は@computed



デコレーターによってタグ付けされ、状態に基づいて計算できることを認識します。







pendingRequests



assignee



はまだ使用されていませんが、すぐ下に実際の動作が表示されます。 簡潔にするため、すべての例でES6、JSX、およびデコレーターを使用しています。 しかし、心配しないでください。MobXのすべてのデコレータには対応するES5があります。







クラスコンストラクターで、レポートを表示する小さな関数を作成し、 autorun



ラップしました。 1回開始されるリアクションが作成され、その後、関数内の監視対象データが変更されるたびに自動的に再起動します。 report



todos



追跡プロパティを使用するため、必要に応じてreport



結果を出力します。







 observableTodoStore.addTodo("read MobX tutorial"); observableTodoStore.addTodo("try MobX"); observableTodoStore.todos[0].completed = true; observableTodoStore.todos[1].task = "try MobX in own project"; observableTodoStore.todos[0].task = "grok MobX tutorial";
      
      





かっこいいですね。 report



は、中間値が漏洩することなく、自動的に同期的に呼び出されます。 ログへの出力を注意深く調べると、コードの4行目がログの新しいエントリにつながっていないことがわかります。 タスクの名前を変更した結果、 report



は実際には変更されていませんが、内部のデータは変更されているためです。 一方、最初のtodo



name



属性を変更すると、 report



出力の出力が更新されます。これは、 report



結果の出力でname



積極的に使用されるためです。 これは、 todos



配列だけでなく、その中の個々の値も追跡されることを示しています。







Reactをリアクティブにする



さて、これまでのところ、リアクティブな「愚かな」報告を行ってきました。 ここで、同じリポジトリをリアクティブインターフェイスで作成します。 Reactコンポーネント(名前にかかわらず)は、そのままではリアクティブではありません。 mobx-react



@observer



デコレーターは、 mobx-react



render



メソッドをラップすることでこれを修正し、コンポーネントを状態と自動的に同期させます。 これは、概念的には以前にreport



行ったことと変わりません。







次のリストは、いくつかのReactコンポーネントを定義しています。 MobXからは@observer



デコレータのみがあります。 これは、関連するデータが変更されたときに各コンポーネントが再描画されるようにするのに十分です。 setState



を呼び出す必要がsetState



、設定が必要なセレクターまたは高次コンポーネント(Hi Redux)を使用してアプリケーションの一部をサブスクライブする方法を理解する必要がなくなりました。 基本的に、すべてのコンポーネントがスマートになります。 それらが「愚かな」宣言的な方法で定義されていない場合。







 @observer class TodoList extends React.Component { render() { const store = this.props.store; return ( <div> { store.report } <ul> { store.todos.map( (todo, idx) => <TodoView todo={ todo } key={ todo.id } /> ) } </ul> { store.pendingRequests > 0 ? <marquee>Loading...</marquee> : null } <button onClick={ this.onNewTodo }>New Todo</button> <small> (double-click a todo to edit)</small> <RenderCounter /> </div> ); } onNewTodo = () => { this.props.store.addTodo(prompt('Enter a new todo:','coffee plz')); } } class TodoView extends React.Component { render() { const todo = this.props.todo; return ( <li onDoubleClick={ this.onRename }> <input type='checkbox' checked={ todo.completed } onChange={ this.onToggleCompleted } /> { todo.task } { todo.assignee ? <small>{ todo.assignee.name }</small> : null } <RenderCounter /> </li> ); } onToggleCompleted = () => { const todo = this.props.todo; todo.completed = !todo.completed; } onRename = () => { const todo = this.props.todo; todo.task = prompt('Task name', todo.task) || ""; } } ReactDOM.render( <TodoList store={ observableTodoStore } />, document.getElementById('reactjs-app') );
      
      





次のリストは、データを変更するだけでよいことを示しています。 MobXは、リポジトリの状態からユーザーインターフェイスの関連部分を自動的に計算して更新します。







 const store = observableTodoStore; store.todos[0].completed = !store.todos[0].completed; store.todos[1].task = "Random todo " + Math.random(); store.todos.push({ task: "Find a fine cheese", completed: true }); // etc etc.. add your own statements here...
      
      





リンクを使用する



これまでに、追跡されたオブジェクト(プロトタイプの有無にかかわらず)、配列、およびプリミティブを作成しました。 しかし、MobXでリンクがどのように処理されるかおもしろいと思うかもしれません。 前のリストで、 todos



assignee



プロパティに気付くかもしれません。 人々を含む別のリポジトリを作成することで別の意味を与えましょう(まあ、それは単なる配列です) 。そして、それらをタスクに割り当てます。







 var peopleStore = mobx.observable([ { name: "Michel" }, { name: "Me" } ]); observableTodoStore.todos[0].assignee = peopleStore[0]; observableTodoStore.todos[1].assignee = peopleStore[1]; peopleStore[0].name = "Michel Weststrate";
      
      





現在、2つの独立したリポジトリがあります。 1つは人、もう1つはタスクです。 assignee



リポジトリからassignee



プロパティにassignee



を割り当てるには、リンクを介して値を割り当てる必要があります。 これらの値は、 TodoView



によって自動的に取得されます。 MobXを使用すると、コンポーネントを最新の状態に保つためにデータを正規化し、セレクターを書き込む必要がありません。 実際、データの保存場所は問題ではありません。 オブジェクトは「観察可能」ですが、MobXはそれらを追跡します。 実際のJavaScriptリンクも機能します。 MobXは、派生値に関連する場合、それらを自動的に追跡します。







非同期アクション



小さなtodo



アプリケーションのすべてが状態から派生しているため、この状態がどこで変更されるかは関係ありません。 これにより、非同期アクションを簡単に作成できます。







インターフェイスに現在のダウンロードステータスが表示されるpendingRequests



プロパティを更新することから始めます。 ダウンロードが完了したら、 todo



リストを更新pendingRequests



カウンターをpendingRequests



ます。 このコードを上記で見たものと比較して、 pendingRequests



プロパティがどのようにpendingRequests



いるかを確認してください。







 observableTodoStore.pendingRequests++; setTimeout(function() { observableTodoStore.addTodo('Random Todo ' + Math.random()); observableTodoStore.pendingRequests--; }, 2000);
      
      





開発ツール



mobx-react-devtools



は、任意のMobX + Reactアプリケーションで使用できる開発者ツールを提供します。

画像







おわりに



それだけです! 定型なし。 UIを簡単でシンプルにするシンプルで宣言的なコンポーネント。 状態から完全に更新されました。 これで、アプリケーションでmobx



およびmobx-react



使用を開始する準備が整いました。

今日学んだことの概要:









MobXは状態コンテナーではありません



多くの場合、Reduxの代替としてMobXを使用します。 ただし、これはアーキテクチャや状態コンテナではなく、特定の問題を解決するための単なるライブラリであることに注意してください。 この意味で、上記の例は手に負えないため、メソッドにロジックをカプセル化したり、リポジトリやコントローラーに整理したりするなど、適切なアーキテクチャソリューションを使用することをお勧めします。 または誰かがハッカーニュースで書いたように:







「MobXを使用するということは、コントローラー、ディスパッチャー、アクション、スーパーバイザー、またはその他の形式のデータフロー制御を使用することを意味します。 」



もっと



興味がありますか? ここにいくつかの便利なリンクがあります










All Articles