React勉強日記

Firestoreのリアルタイム更新リスナを登録して、更新があった時にReactで再renderをしたい。 リアルタイム更新をリスンするかどうかにかかわらず、読み出しAPIはプロミスを返すので非同期。 Reactは状態更新のタイミングで再描画をするのでこれを使う。 結果: Firestore上の値が変わるとブラウザ上の表示も自動的に変わるプロトタイプができた

つまずきポイント

  • React.Componentは型関数で、第二型引数としてstateの型を取る

    • 与えなかった場合は{}が与えられたものとして動く
      • 定義: interface Component<P = {}, S = {}, SS = any>
    • その結果this.state.piecesに対して「Readonly<{}>はpiecesを持たない」ってなる
    • Property ‘pieces’ does not exist on type ‘Readonly<{}>‘.
  • FirebaseのサンプルではonSnapshotの引数にfunctionを使っているが、このままだとComponentのthisをshadowする

    • Visual Studio Codeが警告してくれたのでそこにはハマらなかった
    • だがこの問題の解決方法を僕が正しく理解していなかった
    • thisがshadowされてアクセスできなくなるので
    • 外のスコープでlet setState = this.setStateしてこれを使えば良いだろうと考えた
    • かつてのJSでは明示的にバインドすることで解決する
      • const setState = this.setState.bind(this)
    • 今時のES6ではアロー関数が呼び出し時にthisをbindしない(のでshadowしない)ことを使うことができる

疑問点

  • 僕はconstructorを使ったが、componentDidMountを使っている人がちらほらいる。何が違うのか。

typescript

class App extends React.Component<{}, { pieces: any[] }> {
  constructor(props: {}) {
    super(props);
    this.state = { pieces: [] };
    db.collection("pieces").onSnapshot((querySnapshot: any) => {
      let pieces: any[] = [];
      querySnapshot.forEach(function (doc: any) {
        pieces.push(doc.data().text);
      });
      this.setState({ pieces: pieces });
    });
  }
  render() {
    let pieces = this.state.pieces.map((x) => <SimplePiece text={x}></SimplePiece>);
    return (
      <div className="App">
        <header className="App-header">
          {pieces}
        </header>
      </div>
    );
  }
}