やったこととか思ったこと

やったことを忘れないように気が向いたら書きます

firebaseでGoogle AuthEntication

firebaseのAuthEnticationを触ってわかったことのメモ。

src/firebase.ts

import * as firebase from "firebase/app";
import "firebase/auth";

const config = {
    // Project Overview -> アプリ -> 設定から
};

firebase.initializeApp(config);

export default firebase;

Auth.tsx

import * as React from "react";
import firebase from "./firebase";

const Auth: React.FC = () => {
    const [user, setUser] = React.useState<firebase.User | null>(null);
    
    React.useEffect(() => {
        const watcher = firebase.auth().onAuthStateChanged(user => {
          setUser(user)
        }
        return () => {
            watcher();
        }
    
    }, [setUser]);
    
    return(
        {user ?
        (
          <button onClick={() => firebase.auth().signOut()}>
            logout
          </button>
        ) : (
          <button onClick={login}>
            login
          </button>
        )
      }
    );
};

const login = () => {
  const provider = new firebase.auth.GoogleAuthProvider();
  firebase.auth().signInWithPopup(provider).then((result) => {
        // なんか処理
  }).catch(error => {
    // エラー処理
  });
};

export default Auth;

firebase.auth().onAuthStateChanged(…)firebase.auth().currentUserでも代用できるっぽい。

参考

https://firebase.google.com/docs/web/setup?authuser=0

https://firebase.google.com/docs/auth/web/start?authuser=0

https://firebase.google.com/docs/auth/web/manage-users?hl=ja

https://qiita.com/ossan-engineer/items/740425a0df937a47e093

Twitter4jでタイムラインを取得する

ScalaでTwitter4jを使ってUserTimeline (特定のユーザーのツイート)を取得したメモ。HomeTimeineとかの他のタイムラインを取得するのもおんなじ感じでいけそう

UserTimelineの取得

import scala.collection.JavaConverters._

val twitter = new TwitterFactory().getInstance()
// ConsumerKey, ConsumerSecretは自分で用意したものを使う
twitter.setOAuthConsumer(ConsumerKey, ConsynerSecret)
// 認証されたAccessTokenをセットする
twitter.setOAuthAccessToken(accessToken)

// userTimelineの取得
twitter.getUserTimeline(accessToken.getUserId).asScala

twitter.getUserTimeline()の帰り値にscala.collection.JavaConverters._asScalaを呼び出すと、Seq[twitter4j.Status]にすることができる。

TwitterFactoryのgetInstance()の引数にはAccessTokenを取ることができるけど、そうするためにはtwitter4j.propertiesファイルにOAuthの認証情報を書いておくことが必要?

Pagingについて

パラメータ 内容
page 何ページ目かについて
count 1ページあたりの件数
since_id 指定したid以降のツイートを取得
max_id 指定したid以前のツイートを取得

自分の指定したいパラメータをとるメソッドがオーバーロードされていなくても、setMaxId(Long)sinceId(Long)メソッドを呼び出すことでパラメータを定義したり上書きすることができる。

まとめ

getUserTimeline()の帰り値のResponseListの扱い方がよくわからなくてハマったのでメモとして残しました。Pagingのパラメータは基本的にpageだけ(1ページの表示数を変えたいならcountも)で良さそうですね

参考

Frontend Bootcampを読む Step1-07

Microsoftが公開していたFrontend Bootcampを読み進めていくメモ

自分なりの適当な翻訳なので間違いがあるかもしれません。
また公開されたばかりで現在進行形で更新されているので僕が読んだ時とは内容が少し変わっているかもしれません。

Step1-07: Types and creating a UI-driven state

これまでで、アプリのstateによって変わるUIを作りました。ですが、まだUIからstateを変更することができないので、変更できるようにする機能を実装する必要があります。これは、TodoHeaderで見たように、setStateを呼び出す関数によって行われます。その後、stateの値は子Componentにpropsとして渡されます。

このワークショップのパート2では、propsを使って明示的に渡すことなくそれらの関数を使う方法を学びます。

これがReactの中心的な"ビジネスロジック"であり、基本的な"CRUD"操作を処理します。Create,Read,Update,Deleteのすべての関数を書くことについてはまだ説明していませんが、デモのTodoAppにすでに実装してあり、Componentに渡されていることがわかります。

Intro to TypeScript

TodoApp Componentを見てみると、propsが、長くなっているのではなく、とても複雑になっていることがわかります。様々な関数、todosオブジェクト、フィルタの文字列を送っています。

アプリケーションが大きくなるにつれて、関数や、todosの内容を覚えておくことが難しくなります。また、JavaScriptは動的型付け言語なので、todosの値を、TodoList内の配列に変えたい場合、JavaScriptは大丈夫でしょう。ですが、TodoListItemsが、オブジェクトを期待していたなら、アプリケーションは壊れてしまうでしょう。

これら2つの理由で、業界は強く型付けされたアプリケーションを書くように移行しています。そしてたくさんの人が移行するためにTypeScriptを使っています。

TypeScript is a superset of JavaScript that compiles to plain JavaScript.

TypeScriptのウェブサイトに書いてあるように、TypeScriptはJavaScriptコンパイルされるJavaScirptのスーパーセットです。

もしSassを使ったことがあるなら、あなたはこの概念に精通しているでしょう。すべての有効なCSSが有効なSassであるように、すべての有効なJavaScriptは有効なTypeScriptです。これが、練習問題がjsとjsxの代わりにtsとtsxで書かれていた理由です。

TypeScriptを使ってComponentのpropsを明確にし、将来の後戻りを防ぎましょう。

Demo

最も多くのデータフローを持っているTodoListから始めましょう。TodoListItemにcompletedを渡しているだけなので、TodoListはインタラクティブなUIは持っていません。ですが、すべて正しく渡されていることを確認するためにpropsインターフェイスを書くことができます。

Writing TodoListProps

TodoAppを見ると、TodoListが3つのprops(filter,todos,complete)を持っていることがわかります。TodoListPropsというtodoListのporopsを表すインターフェイスを作ります。

// TodoList.tsx
interface TodoListProps {
    filter: any;
    todos: any;
    complete: any;
}

今はanyを使用しています。これによって安全性が得られるわけではありませんが、このComponentに渡す有効なpropsの名前を指定できます。

インターフェースを書いたら、それをComponentに追加してください。

export class TodoList extends React.Component<TodoListProps, any>

<>の最初の値はpropsのインターフェイス用で、2つ目の値はstate用です。

これで、型付きのComponentが作成できました。TodoAppに戻り、propsの名前を変更するとどうなるのかを見てみましょう。

Adding type safety

今のところ、propsの名前を指定しただけで、それらの型は指定していません。まずは、filterを、型安全にしましょう。

Filter Type

filterがオブジェクト、配列、関数ではないことがわかっているので、filterが常に文字列であるべきだと指定することができます。

interface TodoListProps {
    filter: string;
    todos: any;
    complete: any;
}

さらに、filterは3種類の値にしかならないことがわかっているので、filter共用型(union type)で明示的にすることができます。

interface TodoListProps {
    filter: 'all' | 'active' | 'completed';
    todos: any;
    complete: any;
}

TodoAppに戻り、TodoListのfilterを別のものにしてみてください。もし、VS Codeを使っているならエディタから、またファイルを保存したらコマンドラインからエラーが出るでしょう。

Complete Type

omplete propはデータではなく関数です。ですがTypeScriptではデータだけでなく関数型も扱うことができます。

interface TodoListProps {
    filter: 'all' | 'active' | 'completed';
    todos: any;
    complete: (id: string) => void;
}

関数の場合は渡されるパラメータと返される値の型を書きます。上の例では、stringのidを引数にとり、voidを返します。voidが意味するのは、返り値がないということです。

戻り値が指定していない場合、JavaScriptの全ての関数はundefinedを返しますが、戻り値の型としてvoidを宣言すると、関数から値を返そうとするとTypeScriptがエラーを出します。

Todos Type

todos propは、オブジェクトです。インターフェイスは次のようになります。

interface TodoListProps {
    filter: 'all' | 'active' | 'completed';
    todos: {
        [id: string]: {
            label: string;
            completed: boolean;
        };
    };
    complete: (id: string) => void;
};

[id: string]は配列を示すわけでわなく、オブジェクトのインデックスシグネチャです。

これで、インターフェイスが完成しました。filter === 'all'のallを変更してみてください、そしたらVS Codeがそれは常にfalseになると表示するでしょう。JacaScriptだった場合、その行にタイプミスがあったら、なぜフィルターが機能しなかったかの理由がわかりません。

Sharing types

Componentのほとんどが、todosfilterの型を指定する必要があります。なので、TypeScriptを使用してファイル間で型を共有しましょう。既にTodoApp.types.tsファイルにこれらの型を書き出し、エクスポートしているので、それらをインポートして使いましょう。

import { FilterTypes, Todos, CompleteTodo } from "../TodoApp.types";

interface TodoListProps {
    complete: CompleteTodo;
    todos: Todos;
    filter: FilterTypes;
}

Writing TodoListItemProps

TodoListItemを見ると、labelcompletedが既にTodoItemインターフェイスで定義されていることがわかります。TodoListPropsにTodoItemインターフェイスを継承して再利用することができます。

import { CompleteTodo } from "./TodoApp.types";

interface TodoListItemProps extends TodoItem {
    id: string;
    complete: CompleteTodo;
}

TodoListItemPropsは、4つのプロパティ(id, complete, completed, label)を持つインターフェイスです。

次に、propsをrenderに取り込むことができます。

const { label, completed, complete, id } = this.props;

そしたら、inputのonChangeイベントを使ってコールバックを書きます。シグネチャの中でidがstringだと宣言しているので、idを渡します。

コールバックはpropsとしてComponentに渡される関数です。

<input type="checkbox" chacked={completed} onChange{() => complete(id)} />

propの名前と関数名が偶然同じですが、これは必須ではありません。

todoがonChangeを起動しているので、クリックしてアプリがどのような動作をするのかみてください。Footerのテキストは未完了のTodoの数に基づいているので、Footerは新しいstateを反映するために自動的に更新されます。

Exercise

まだアプリを実行していない場合はfrontend-bootcampフォルダのルートからnpm startを実行して起動してください。動作を確認するためには、1日目のstep7の"exersice"をクリックしてください。

TodoFooter

  1. TodoFooterを開き、TodoFooterPropsを書いてください。それはcleartodosの2つの値を持ちます。このインターフェイスは関数のComponentでは次のように使用します。(props: TodoFooterProps)
  2. props.clearを呼び出す_onClick関数を作ってください。
    • TodoFooterはクラスではないので、_onClickは戻り値の前のconst格納する必要があります。
    • このクリックハンドラを定義するのにアロー関数を使ってください。
  3. _onClickをボタンのonClickに割り当ててください。TodoFooterはクラスではないのでthisを使う必要はありません。
  4. この機能を試してみてください。いくつかのtodosを完了したら、Clear Completedボタンをクリックします。

TodoHeader

  1. TodoHeaderを開き、addTodo, setFilter, filterを含むTodoHeaderPropsを書いてください。そしてクラス宣言の最初のanyと置き換えてください。

  2. このComponentはstateを持っています。TodoHeaderStateを書き、2つ目のanyと置き換えてください。

  3. 各filterボタンに_onFilterを割り当ててください

    • onClickに新しいパラメータを追加することはできませんが、イベントターゲットから情報を取得することはできます。
    • これもアロー関数にするのを忘れないでください
  4. 送信ボタンから_onAddを呼び出せるようにしてください。

  5. この機能を試してみてください。todoを追加、フィルターすることができるでしょう。

   // TodoFooter
   import React from 'react';
   import { Todos } from '../TodoApp.types';
   interface TodoFooterProps {
     clear: () => void;
     todos: Todos;
   }
   
   export const TodoFooter = (props: TodoFooterProps) => {
     const itemCount = Object.keys(props.todos).filter(id => !props.todos[id].completed).length;
     const _onClick = () => {
       props.clear();
     };
   
     return (
       <footer>
         <span>
           {itemCount} item{itemCount === 1 ? '' : 's'} left
         </span>
         <button onClick={_onClick} className="submit">
           Clear Completed
         </button>
       </footer>
     );
   };
   
   // TodoHeader
   import React from 'react';
   import { FilterTypes } from '../TodoApp.types';
   
   interface TodoHeaderProps {
     addTodo: (label: string) => void;
     setFilter: (filter: FilterTypes) => void;
     filter: FilterTypes;
   }
   
   interface TodoHeaderState {
     labelInput: string;
   }
   
   export class TodoHeader extends React.Component<TodoHeaderProps, TodoHeaderState> {
     constructor(props) {
       super(props);
       this.state = { labelInput: '' };
     }
   
     render() {
       const { filter } = this.props;
       return (
         <header>
           <h1>todos - step1-07 exercise</h1>
           <div className="addTodo">
             <input value={this.state.labelInput} onChange={this._onChange} className="textfield" placeholder="add todo" />
             <button className="submit" onClick={this._onAdd}>
               Add
             </button>
           </div>
           <nav className="filter">
             <button className={filter === 'all' ? 'selected' : ''} onClick={this._onFilter}>
               all
             </button>
             <button className={filter === 'active' ? 'selected' : ''} onClick={this._onFilter}>
               active
             </button>
             <button className={filter === 'completed' ? 'selected' : ''} onClick={this._onFilter}>
               completed
             </button>
           </nav>
         </header>
       );
     }
   
     _onFilter = evt => {
       this.props.setFilter(evt.target.innerText);
     };
   
     _onChange = evt => {
       this.setState({ labelInput: evt.target.value });
     };
   
     _onAdd = () => {
       this.props.addTodo(this.state.labelInput);
       this.setState({ labelInput: '' });
     };
   } 

参考

frontend-bootcamp/step1-07/exercise at master · Microsoft/frontend-bootcamp · GitHub

Frontend Bootcampを読む Step1-06

Microsoftが公開していたFrontend Bootcampを読み進めていくメモ

自分なりの適当な翻訳なので間違いがあるかもしれません。
また公開されたばかりで現在進行形で更新されているので僕が読んだ時とは内容が少し変わっているかもしれません。

Step 1-06 Demo: Creating a State-Driven UI

Reactのデータは、トップダウンのようにComponent階層を上から下に伝わっていきます。stateを持つComponentのみがそのstateを変更できます。UIインタラクションが起きた時、stateを持つコンポーネントはstateの変更を受け取るために、イベントハンドラをUI Componentに渡す必要があります。

"Thinking in React"のステップ3では、アプリケーションに必要な"変更可能なstateの最小セット"を考えることが必要であると書いてあります。このデモでは、最小のstateをアプリケーションに追加し、そのデータからUIを操作します。またstateを変更できるようにします。stateを変更する方法(関数とか)は、UI Componentを通してカスケードダウンします。reconciliationでstateが変化した時に、UIの何が変更されるかを把握できます。

Adding state to TodoApp

TodoAppクラスの中に、todosfilterというstateを作成します。未完了のTodoを数えるのは、completeフィールドがfalseに設定されているtodosの数を数えれば計算できるのでstateを作る必要はありません。

// TodoApp
constructor(props) {
  super(props);
  this.state = {
    todos: {
      '04': {
        label: 'Todo 4',
        completed: true
      },
      '03': {
        label: 'Todo 3',
        completed: false
      },
      '02': {
        label: 'Todo 2',
        completed: false
      },
      '01': {
        label: 'Todo 1',
        completed: false
      }
    },
    filter: 'active'
  };
}

場合によってはTodosを配列にした方が簡単になりますが、この場合、オブジェクトを利用した方が最終的には高性能になります。

Passing state through to UI

filtertodosをComponentに渡します。

// TodoApp
render() {
    const { filter, todos } = this.state;
    return (
        <div>
            <TodoHeader filter={filter} />
            <TodoList todos={todos} filter={filter} />
            <TodoFooter todos={todos} />
        </div>
    );
}

State-driver TodoList

TodoListを見ると、すでにpropsをfilterとtodosという変数に渡し、さらにfilterのstateによってフィルタ処理されたtodosの配列を返す処理もしています。これらを使って、TodoItemをレンダリングします。

todos[id]は渡されたidに一致するtodoを返し、スプレッド演算子(…)はlabel={todos[id].label} completed={todos[id].comleted}と同じです。

return (
    <ul className="todos">
        {filteredTodos.map(id => (
            <TodoListItem key={id} id={id} {...todos[id]} />
        ))}
    </ul>
);

State-driven and stateful TodoHeader

TodoHeaderでは、filterstateを渡すだけではなく、コントロール内で状態を維持したいという場合があります。幸い、Reactにとってこれは全く問題ありません。最初にstateを扱いましょう。

Conditional class names

CSSのスタイルでは、ビジュアルをクラスを追加または削除することによって適用されます。filterを利用して、条件付きでクラスを追加し、正しい装飾をすることができます。

<nav className="filter">
  <button className={filter === 'all' ? 'selected' : ''}>all</button>
  <button className={filter === 'active' ? 'selected' : ''}>active</button>
  <button className={filter === 'completed' ? 'selected' : ''}>completed</button>
</nav>

三項演算子condition ? expressionIfTrue : expressionIfFalseはReactでよく使われています。各式はclassNameやJSXのelementの文字列になる可能性があるからです。

Adding a controlled imput

Reactでは、<input>,<textarea>,<select>などのform要素は未制御の入力(UnControlled input)または制御された入力(Controlled input)として使用できます。

未制御では、現在の値をform要素が内部的に維持し、ユーザーの操作(テキストの入力、オプションの選択など)に基づいて更新します。送信時など、コードは必要な時のみに、入力から値を取得します。イメージとしてはプレーンなHTMLのformです。

制御された場合はpropsから現在の値を取得し、コールバックを利用してユーザーによって行われた変更を親Componentに送ります。入力の値は、親Componentがコールバックに応答して、入力のpropsを更新するまで変わりません。

未制御と制御のものとを区別するのは、form Componentを書くときや使う時に理解することが大切です。詳しくはこの記事を参照してください。

TodoHeader Componentのテキストフィールドを制御に変更してみましょう。制御された入力にするには、デモですでに説明された2つのことが必要です。

  1. 入力値を保持するためにstateを作る

    javascript this.state = { labelInput: "" };

  2. 値を更新するためのコールバック関数を作る

    javascript _onChange = evt => { this.setState({ labelInput: evt.target.value }); };

これらを適用することで、未制御の入力を制御された入力に更新することができます。

<input value={this.state.labelInput} onChange={this._onChange} className="textfield" placeholder="add todo" />

React Dev Toolsがインストールされている場合はそれを確認しがならLabelInputを確認してください。

Exercise

もしアプリケーションを起動していないのなら、frontend-bootcampのルートでnpm startを実行してください。結果を確認するにはday1 step6にある"exercise"をクリックしてください。

TodoFooter

  1. itemCountを利用して、現在のアイテム数を表示してください。
  2. 三項演算子を利用し、itemCount === 1であるかどうかで"item"と"items"を切り替えるようにしてください。

TodoListItem

  1. 分割代入を利用してlabel,completedを代入してください。
  2. テキストをlabelに、checked属性をcompletedにしてください。
// TodoFooter
export const TodoFooter = (props: any) => {
  const itemCount = Object.keys(props.todos).filter(id => !props.todos[id].completed).length;
  return (
    <footer>
      <span>
        {itemCount} item{itemCount === 1 ? '' : 's'} left
      </span>
      <button className="submit">Clear Completed</button>
    </footer>
  );
};

// TodoListItem
export class TodoListItem extends React.Component<any, any> {
  render() {
    const { label, completed } = this.props;
    // onChangeは実行時に出るWarningを防ぐために書いてます。
    return (
      <li className="todo">
        <label>
          <input type="checkbox" checked={completed} onChange={() => undefined} /> {label}
        </label>
      </li>
    );
  }
}

Frontend Bootcampを読む Step1-05

Microsoftが公開していたFrontend Bootcampを読み進めていくメモ

自分なりの適当な翻訳なので間違いがあるかもしれません。
また公開されたばかりで現在進行形で更新されているので僕が読んだ時とは内容が少し変わっているかもしれません。

Step1-05 Demo: Building a Static Page

Thinking in Reactで説明されている手順に従って、TODOアプリを作っていきます。

まず最初に、アプリケーションをCompoentの階層に分割していきます。このアプリケーションでは、4つに分割します。

  • TodoHeader
  • TodoList
  • TodoListItem
  • TodoFooter

このアプリケーションのモックアップを、step1-05/TodoApp.htmlで確認することができます。

TodoHeader

Componentは全てsrc/componentディレクトリに格納するので、ディレクトリを作成してください。作ったら、TodoHeader ComponentをTodoHeader.tsxというファイルに作ります。.tsx拡張子は、このファイルがTypeScript + Reactでコードを書いていると表します。

TypeScriptについては後で説明されます。今わかるのは、TypeScriptファイルにJavaScriptを書くことができるということです。

import React from "react";

export class TodoHeader extends React.Component<any, any> {
  render() {
    return (
      <header>
        <h1>todos</h1>
        <div className="addTodo">
          <input className="textfield" placeholder="add todo" />
          <button className="submit">Add</button>
        </div>
        <nav className="filter">
          <button className="completed">all</button>
          <button>active</button>
          <button>completed</button>
        </nav>
      </header>
    );
  };
}

Reactでは、classclassNameにする必要があります。

TodoListItem

要素が繰り返し表示されている場合は、基本新しいComponentを作った方がいいです。propsを使えば、それらの要素を単一のComponentにすることができます。TodoListItemが、それに当てはまります。

import React from 'react';

export class TodolistItem extends React.Component<any, any> {
  render() {
    return (
      <li className="todo">
        <label>
          <input type="checkbox" /> Todo 1
        </label>
      </li>
    );
  }
}

これは、クラスの代わりに関数として作ることもできます

export const TodoListItem = (props) => {}

Exercise

TodoFooter

  1. TodoFooter Componrntをcomponentディレクトリに作ってください。<footer>タグとその子要素をstep1-05/TodoApp.htmlからコピーします。TodoFooterは関数またはクラスです。
  2. classcalssNameに変更してください。

TodoList

  1. TodoFooterと同じようにTodoList Componentを追加してください。クラスと関数どちらでもいいです。
  2. TodoListItemをインポートし、
      の内側に4つ追加してください。
    • オプションでArrayのmapなどを利用して作成してみてください。

App.tsx

これらのComponentをApp.tsxにインポートし、TodoHeaderの下に配置してください。

// TodoFooter
export const TodoFooter = props => {
  return (
    <footer>
      <span>4 items left</span>
      <button class="submit">Clear Completed</button>
    </footer>
  )
}

// TodoList
import { TodoListItem } from "./TodoListItem;

export const TodoList = props => {
  return (
    <ul className= "todos">
      { [1, 2, 3, 4].map((i) => <TodoListItem key={i} text={"Todo " + i.toString()} />)}
    </ul>
  )
};

// TodoListItem
 <input type="checkbox" /> {this.props.text}

// App.tsx
import React from "react";
import { TodoHeader } from "./components/TodoHeaderr";
import { TodoList } from "./components/TodoList";
import { TodoFooter } from "./component/TodoFooter";

export class TodoApp extends Reract.Component<any, any> {
    render() {
        return (
            <div>
                <TodoHeader />
                <TodoList />
                <TodoFooter />
            </div>
        );
    }
}

参考

Frontend Bootcampを読む Step1-04

Microsoftが公開していたFrontend Bootcampを 1-04から読み進めていくメモ

自分なりの適当な翻訳なので間違いがあるかもしれません。
また公開されたばかりで現在進行形で更新されているので僕が読んだ時とは内容が少し変わっているかもしれません。

上のリンクのREADMEに沿って環境を整えてください。

Step1-04: Introduction To React Demo

原文

このデモでは、クリック数とカウントを表示するシンプルなカウンターを作ります。

CodePenを使って始めましょう。

React Hello World

ReactDOM.render(<p>Hello World</p>, document.getElementById('app'));
  • ReactDOM.render()

    この関数を使ってコードを表示します。2つのパラメータを取ります。

    1つはページに配置するコンテンツ(ここでは<p>Hello World</p>)

    2つ目はコンテンツを配置したい場所(ここではdocument.getElementById('app'))です。

Writing React Component

ReactのComponentはアプリケーションの一部を返すコードです。これには、HTML、CSSJavaScriptを組み込むことができます。

Componentは2つの方法で作成することができます。

1つはReact.Componentクラスを継承するJavaScriptのクラスを利用する方法です。

JavaScriptのクラスは、拡張可能なコンテナ内にメソッド(関数)とProperties(値)を集めることができます。React.Componentは、renderを含むいくつかのメソッドを使えるようにするので、拡張しています。

class App extends React.Component {
    render() {
        return <p>Hello World</p>;
    }
}

Componentは、propsを受け取ってJSXを返す関数として書くこともできます。

const App = props => {
    return <p>Hello World</p>;
};

Hello WorldをAppのrenderに移動したので、`ReactDOM.Render()を次のようにできます。

ReactDOM.render(<App />, document.getElementById('app'));

React Componentは、HTMLタグと同じように記述することで再利用できます。

Props

どちらの方法でComponentを作っても、idhrefなどのHTML属性と同じ構文を利用して追加のpropsを取り込むことができます。

<App text="Hello World" />

propsはComponentから、関数のprops.text、またはクラスのthis.props.textを通してアクセスすることができます。

// 関数の場合
const App = props => {
  return <p>{props.text}</p>;
};

// クラスの場合
class App extends React.Component {
  render() {
    return <p>{this.props.text}</p>;
  }
}

propsを使うと、同じComponentの複数のインスタンスを異なるpropsで使うことができるため、Componentをより再利用しやすくなります。

ReactDOM.render(
    <div>
        <App text="Hello World" />
        <App text="How are you doing?" />
    </div>,
    document.getElementById('app')
);

レンダリングする関数は1つの要素しか返すことができないため、2つのApp Componentをdivでラップする必要があります。

const App = props => {
    <p>{props.text ? props.text : "oops!"}</p>;
};

Destructuring Props

関数内でprops.text(またはクラス内でthis.props.text)を繰り返し書くのは冗長です。なので、textに変数を割り当てることができます。

const App = props => {
  const text = props.text;
  return <p>{text ? text : 'you missed something'}</p>;
};

これは、単一のpropsに対しては便利です。しかし、Componentが複雑になるにつれてコードはこうなります。

<MyComponent
  open={false}
  count={5}
  text="Hi there"
  items={['cat', 'dog', 'bird']}
  config={{
    start: 1,
    end: 10,
    autoStart: true
  }}
/>

// Component内
const open = props.open;
const text = props.text;
const count = props.count;
const items = props.items;
const start = props.config.start;
const end = props.config.end;

string以外の値は全て{}で囲んでJavaScriptとして渡します。

destructuringを使うことによって複雑なデータ型から個々の情報を1行で取り出すことができます。

const {
  open,
  text,
  count,
  items,
  config: { start, end }
} = props;

今のはやり過ぎかもしれませんが、propsを追加することは簡単です。

const App = props => {
  const text = props.text;
  return <p>{text ? text : 'you missed something'}</p>;
};

Cleanup

先に進む前に、ReactDOM.renderをリセットしてAppを返すだけにします。このReactDOM.render関数は基本的にの無い単一のComponentのみを含みます。

次に、CounterComponentを作ります。コードをを以下のようにしてください。

const App = props => {
  return <Counter text="chickens" />;
};

ReactDOM.render(<App />, document.getElementById('app'));

Counterの大文字に気をつけてください。HTMLの場合は大丈夫ですが、JSXでは区別します。React Componentの名前は一般的には対応するHTML要素の大文字の名前を使用することです。(Button, Select, Label, Formなど)

React State

Reactはstateというデータストアを各コントロールに指定することができます。UIをレンダリングするときに、stateの値を参照できます。また、アプリケーションが有効な間にstateを更新することもできます。

最近、Hooksという関数Componentにstateを追加する機能が追加されましたが、今現在見られるステートフルComponentのほとんどがクラスベースです。

Adding State

JavaScriptのクラスはconstructorを利用してクラスをstateと共にインスタンス化します。それでは、Counterという新しいComponentを作り、デフォルト値が0のclicksstateを設定しましょう。

class Counter extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            clicks: 0
        };
    }
}
  • コンストラクタはComponentのpropsを受け取ります。
  • super(props)は親クラスのコンストラクタを呼び出します。(この場合はReact.Component)
  • clicksstateには、this.state.counterを呼ぶことですぐにアクセスできます。また、this.setState({ clicks: 1 })を呼び出すことによってstateを更新することもできます。

Creating out Counter

Counter Componentの目標は、clicksのボタンがクリックされた回数を追跡できるようにすることです。以下のコードを書き込みます。

render() {
  const { text } = this.props;
  const { clicks } = this.state;
  return (
    <div>
      {text}: {clicks}
      <button>Click</button>
     </div>
   )
}

Writing our Button Click Handler

次のステップは、ボタンを押すとclickが増えるようにすることです。

慣例で、他のメソッドはrender()の下に配置されます。また、privateなメソッドはメソッド名の前にアンダースコアがつきます。

このメソッドはComponentのstateを更新し、1づつclicksの値を増やします。(setStateはオブジェクトの値のみを変更します。)

_onButtonClick = () => {
    this.setState({
        clicks: this.state.clicks + 1
    });
};

前の値が決定されるまでstateを更新しないようにthis.setState(prevState => ({ counter: prevState.clicks + 1 });と書くこともできます。

カウントを増やせるようになったので、ボタンに接続しましょう。

<button onClick={this._onButtonClick}>Click</button>

各Counterは、自身のstateを維持しています。なので、他の Counterに影響を与えずに、1つのCounter内のstateを変更できます。

Try it all out!

それぞれ異なるtextを使用してアプリのカウンターを2つにします。それらが自身のstateを維持していることがわかります。

Module Exports and Imports

step1-04/final/components/Counter.tsxを開き、Counter Componentを見てください。

export class Counter extends React.Component {
...
}

このファイルはCounter Componentを'named'エクスポートとしてエクスポートしています。これは次のようにインポートします。

import { Counter } from './components/Counter';

Default Exports

通常はnamedエクスポートを使いますが、次のようにデフォルト値をエクスポートすることもできます。

export default class extends React.Component {
...
}

この場合、エクスポートに名前がないので、Componentをインポートするときに、好きなように名前をつけることができます。

import SomeCounterComponent from './components/Counter';

上の例ではインポート値を{}でラップしていません。これは実際には破壊的な例です。

Using a Button component

ボタンは最も一般的にかかれたComponentの1つです。カスタムボタンは、一般的なスタイルを抽象化したり、アイコンやその他の装飾を追加したり、メニューボタンなどの機能を増やすことができます。カスタムComponentを見てみましょう。

import React from 'react';
import './Button.css';

export const Button = props => {
  return (
    <button className="Button" onClick={props.onClick}>
      {props.children}
    </button>
  );
};

参考

Reactのチュートリアルをやったので簡単に電卓を作った

Reactのチュートリアルを読んだので簡単に電卓をつくりました。
作ったやつ
ソースコード

本当にReactのチュートリアルの技術しか使ってないので解説とかはせず、作った後に考えて気づいたこととかを書こうと思います。

setStateについて

class Board extends React.Component {

  ...
  
  updateState(
    result = this.state.result,
    disp = this.state.disp,
    op = this.state.op
  ) {
    this.setState({
      result: result,
      disp: disp,
      op: op,
    });
  }

  ...
}

Boardコンポーネント内にupdateStateというセッターメソッドを作って子コンポーネントに渡していました。でも、setStateの引数がオブジェクトなので、返り値がオブジェクトの関数を読んであげればもっと綺麗に書けるのかなって思いました。

// 適当に書くとこんな感じ
calculate(op) {
  return { op: op }
}

this.setState(calculate(opelator));

updateStateに囚われてボタンパネルのコンポーネントButtonsにメソッドをたくさんつくっていましたが、そこらへんの処理を別ファイルに切り出してあげるとよさそうだと思います。

Stateを子コンポーネントに渡す

Boardのステータスを複数Buttonsに渡すのに、わざわざひとつづつ渡していましたが、propsでstate={this.state}って感じにしてあげればステータスの数が少ない時はいいのかなって感じです。

// こんなのを
    <Buttons 
      display={this.state.disp} 
      result={this.state.result}
    />

// こうする
    <Buttons 
      state={this.state}
    />

まとめ

ReactはHTMLに比べてプログラミングしてる感があって楽しいなって思います。
Reactの学習が一段落ついてTypeScriptをやり始めたらtsでリファクタリングしたいと考えています。