Dartからきた

BLoCの話

Roppongi.js #2

2018/5/29

自己紹介

  • 西田雅博
  • 2018/2 ~ merpay
    • go, React
  • ~ 2018/1 Bizreach
    • Scala, Angular4, React

GitHub: adwd

Twitter: @adwd118

 

BLoC? 🤔

BLoC

Business Logic Component

※ Component は React, Vueでいうアレじゃなくて純粋に部品とかって意味

Google AdWordsチームが発表

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