Dartからきた
BLoCの話
Roppongi.js #2
2018/5/29
自己紹介
- 西田雅博
- 2018/2 ~ merpay
- go, React
- ~ 2018/1 Bizreach
- Scala, Angular4, React
BLoC? 🤔
BLoC
Business Logic Component
※ Component は React, Vueでいうアレじゃなくて純粋に部品とかって意味
Google AdWordsチームが発表
- DartConf 2018
- Google I/O '18
BLoC
- もともとの動機
- Web (Angular Dart)とMobile (Flutter)でビジネスロジックのコードを共有したい
- 言語・プラットフォームを問わないビジネスロジック(状態管理)設計パターン
- 今日はコードの共有というより状態管理パターンとして話します
- React, RxJSを使ったサンプルアプリ
BLoC概念図
BLoCざっくり
- ビジネスロジックをコンポーネントツリーと別の場所に置く
- BLoCの外部へのインターフェースはStream(Observable)だけ
BLoCざっくり
- RxJSで言うと
- Observable.subscribe
- Observer.next
BLoCコード例
class TodoBloc {
private _todos = new BehaviorSubject<Todo[]>([]);
public get todos = this._todos.asObservable();
private _addTodo = new Subject<string>();
public get addTodo(): Observer<string> {
return _addTodo;
}
constructor() {
this._addTodo.subscribe(text => {
const current = this._todos.getValue();
this._todo.next([...current, { text, id: newId() }]);
})
}
}
const todoBloc = new TodoBloc();
class Todo extends React.Component {
this.state = {
todos: []
};
componentDidMount() {
todoBloc.todos.subscribe(todos => {
this.setState({ todos });
});
}
render() {
return (
<>
<TodoInput onAddTodo={todoBloc.addTodo.next} />
<TodoList todos={this.state.todos} />
</>
)
}
}
BLoCコード例
const todoBloc = new TodoBloc();
class Todo extends React.Component {
this.state = {
todos: []
};
componentDidMount() {
todoBloc.todos.subscribe(todos => {
this.setState({ todos });
});
}
render() {
return (
<>
<TodoInput onAddTodo={todoBloc.addTodo.next} />
<TodoList todos={this.state.todos} />
</>
)
}
}
- todoBlocのインスタンスを作成
- todoBloc.todosをsubscribeして、自分のstateに設定する
- Todo追加はaddTodo.next
class TodoBloc {
private _todos = new BehaviorSubject<Todo[]>([]);
public get todos = this._todos.asObservable();
private _addTodo = new Subject<string>();
public get addTodo(): Observer<string> {
return _addTodo;
}
constructor() {
this._addTodo.subscribe(text => {
const current = this._todos.getValue();
this._todo.next([...current, { text, id: newId() }]);
})
}
}
- 実際は
- Contextなどを使ってComponentに与える
- Observableのsubscribeはreact公式のcreate-subscriptionパッケージが使えるかも
BLoCコード例
const todoBloc = new TodoBloc();
class Todo extends React.Component {
this.state = {
todos: []
};
componentDidMount() {
todoBloc.todos.subscribe(todos => {
this.setState({ todos });
});
}
render() {
return (
<>
<TodoInput onAddTodo={todoBloc.addTodo.next} />
<TodoList todos={this.state.todos} />
</>
)
}
}
- 公開しているのは2つのStream
- todos: Observable<Todo[]>
- addTodo: Observer<string>
- todosをsubscribeしてTodoのリストを受け取ってもらう
- 新しいTodoが追加されたときにaddTodo.nextを実行してもらう
class TodoBloc {
private _todos = new BehaviorSubject<Todo[]>([]);
public get todos = this._todos.asObservable();
private _addTodo = new Subject<string>();
public get addTodo(): Observer<string> {
return _addTodo;
}
constructor() {
this._addTodo.subscribe(text => {
const current = this._todos.getValue();
this._todo.next([...current, { text, id: newId() }]);
})
}
}
BLoCにすると
- ビジネスロジック(状態)とのIFはすべてStream(Observable)
- BLoCからのデータの取得はObservable#subscribe
- BLoCへのデータの送信はObserver
BLoCにすると
- BLoC側はin/outのStreamを管理するだけ
- ComponentはStreamからデータを受け取る・Streamにデータを追加するだけ
- Flux/Reduxと同じ
- 関心を分離できる
- Angularやってるとこういうふうになる説
個人的なBLoCの
いいところ
- BLoCは単なるクラスなのでいろいろできる
- 簡単にMockと切り替えられる
- constructor injection
- 他のBLoCを受け取る
- 言語によって出力を切り替える
- Local Storageを使って初期状態を取得
- Stream(Observable)を使っている
- RxJSの豊富なオペレータを使える
- 他のBLoCのStreamと自分のStreamを混ぜる
- ユーザーの入力を500ms debounceさせてAPIにクエリを投げた検索結果のStream
個人的なBLoCの
いいところ
Reduxと比べて
- 個人的にReduxに持っていた不満点を解消してくれる
- combineReducerで結合するreducer間でアレコレできない
- combineReducerと同じ粒度のselector書くとか二度手間じゃない…?
- Action, reducerの冗長感
- TypeScriptと相性がイマイチ
- などなど・・・
BLoC Cons
- 設計パターンなのでReduxみたいにライブラリやれば道が決まるわけではない
- Stream(Observable)慣れが必要
- 今のとこGoogleさんしかやってない
- 言語、FWにStreamの仕組みが必要
- など
まとめ
- Dart/Flutterでビジネスロジックのコードを共有するBLoCという設計パターン
- 言語・FWを問わず適用できる
- Webフロントエンドでも使える
- In/OutをStreamにするのがキモ
おわり
DartからきたBLoCの話
By adwd
DartからきたBLoCの話
- 7,767