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
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のほとんどが、todosとfilterの型を指定する必要があります。なので、TypeScriptを使用してファイル間で型を共有しましょう。既にTodoApp.types.tsファイルにこれらの型を書き出し、エクスポートしているので、それらをインポートして使いましょう。
import { FilterTypes, Todos, CompleteTodo } from "../TodoApp.types"; interface TodoListProps { complete: CompleteTodo; todos: Todos; filter: FilterTypes; }
Writing TodoListItemProps
TodoListItemを見ると、labelとcompletedが既に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
- TodoFooterを開き、
TodoFooterPropsを書いてください。それはclearとtodosの2つの値を持ちます。このインターフェイスは関数のComponentでは次のように使用します。(props: TodoFooterProps) props.clearを呼び出す_onClick関数を作ってください。- TodoFooterはクラスではないので、
_onClickは戻り値の前のconst格納する必要があります。 - このクリックハンドラを定義するのにアロー関数を使ってください。
- TodoFooterはクラスではないので、
_onClickをボタンのonClickに割り当ててください。TodoFooterはクラスではないのでthisを使う必要はありません。- この機能を試してみてください。いくつかのtodosを完了したら、
Clear Completedボタンをクリックします。
TodoHeader
TodoHeaderを開き、
addTodo,setFilter,filterを含むTodoHeaderPropsを書いてください。そしてクラス宣言の最初のanyと置き換えてください。このComponentはstateを持っています。
TodoHeaderStateを書き、2つ目のanyと置き換えてください。各filterボタンに
_onFilterを割り当ててください- onClickに新しいパラメータを追加することはできませんが、イベントターゲットから情報を取得することはできます。
- これもアロー関数にするのを忘れないでください
送信ボタンから
_onAddを呼び出せるようにしてください。この機能を試してみてください。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クラスの中に、todosとfilterという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
filterとtodosを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つのことが必要です。
入力値を保持するためにstateを作る
javascript this.state = { labelInput: "" };値を更新するためのコールバック関数を作る
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
itemCountを利用して、現在のアイテム数を表示してください。- 三項演算子を利用し、
itemCount === 1であるかどうかで"item"と"items"を切り替えるようにしてください。
TodoListItem
- 分割代入を利用して
label,completedを代入してください。 - テキストを
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では、
classをclassNameにする必要があります。
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
- TodoFooter Componrntを
componentディレクトリに作ってください。<footer>タグとその子要素をstep1-05/TodoApp.htmlからコピーします。TodoFooterは関数またはクラスです。 classをcalssNameに変更してください。
TodoList
- TodoFooterと同じようにTodoList Componentを追加してください。クラスと関数どちらでもいいです。
- 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、CSS、JavaScriptを組み込むことができます。
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を作っても、idやhrefなどの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でリファクタリングしたいと考えています。