#はじめに
この投稿はReactを趣味で勉強している私が、勉強のために公式サイトに書いてあることを読んでみて実際に手を動かしてみた備忘録です。
書いていることが間違っていたら指摘してほしいです。
私は業務でreactをやった経験とかもない素人ですが、Reactの公式サイトってこんなこと書いてあるんだ、という参考ぐらいにはなると思って投稿させていただきました。
それとGoogle翻訳の内容をそのまま記載している場所も多いです。
あと読んでの感想ですが、公式サイトにはCodePenでの動くサンプルなんかもたくさんあり、本当にわかりやすくできてますね
よろしくお願いいたします。m(_ _)m
v15.6.1です。
#Installation
https://facebook.github.io/react/docs/installation.html
###Trying Out React
コードペンでReactを使う方法が書いてます。
https://codepen.io/gaearon/pen/rrpgNB?editors=0010
###Creating a New Application
create-react-appで環境を作る方法が書いてあります。
npm install -g create-react-app
create-react-app my-app
cd my-app
npm start
###Installing React
yarnとnpmで環境を作る方法が書いてあります。
###Enabling ES6 and JSX
ES6とJSXの使用をおすすめ。
私はwebpackで環境を作ったので、reactと一緒にbabelなどをinstallしました。
npm init -y
npm install --save-dev webpack react react-dom babel-loader babel-core babel-preset-react babel-preset-es2015
###Hello World with ES6 and JSX
ES6とJSXでHello Worldする。
htmlのどこかに<div id="root"></div>
を記述して、以下のように書く。
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);
ES6のimportは{}
で指定したものだけを取得できるので、以下のように書いても同じ意味ですね。
import React from 'react';
import { render } from 'react-dom';
render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);
###Development and Production Versions
Google翻訳〜
デフォルトでは、Reactには多くの有用な警告が含まれています。 これらの警告は、開発に非常に役立ちます。
###Using a CDN
CDNでもできる。
<script src="https://unpkg.com/react@15/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15/dist/react-dom.js"></script>
コピペだけで試せるサンプル
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello React</title>
</head>
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@15/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15/dist/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.38/browser.js"></script>
<script type="text/babel">
ReactDOM.render(
<h1>Hello, World</h1>,
document.getElementById('root')
);
</script>
</body>
</html>
#Hello World
https://facebook.github.io/react/docs/hello-world.html
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);
###A Note on JavaScript
let、const、矢印関数、クラス構文、テンプレートリテラル
ES6のこれぐらいは勉強しておくとReact覚えやすいですよということらしい。
react.jsで使うような書き方で説明していくと以下のような感じっぽいです。
・let const
varにかわって変数宣言をする
letは再代入可能。
constは再代入不可。
// varを使用した書き方
var Hello = function(props){
return <h1>Hello {props.name}</h1>
}
render(
<Hello name="Tarou" />,
document.getElementById('root')
);
単純なコンポーネントを作る時はlet、constで宣言すると良さそう。
// constを使用した書き方
const Hello = function(props){
return <h1>Hello {props.name}</h1>
}
render(
<Hello name="Tarou" />,
document.getElementById('root')
);
・矢印関数
無名関数の省略した書き方。
// before
const Hello = function(props){
return <h1>Hello {props.name}</h1>
}
// after
// functionと書くのを省略して=>を書く
const Hello = (props) => {
return <h1>Hello {props.name}</h1>
}
// 引数が1つあった場合は()を省略できる
const Hello = props => {
return <h1>Hello {props.name}</h1>
}
// returnするだけの関数の場合は「return」と{}を省略できる
const Hello = props => <h1>Hello {props.name}</h1>
・クラス構文
React.Componentを継承してコンポーネントを作成していく。
import React from 'react';
import { render } from 'react-dom';
class Hello extends React.Component {
render() {
return (
<h1>Hello {this.props.name}</h1>
);
}
}
render(
<Hello name="Tarou" />,
document.getElementById('root')
);
・テンプレートリテラル
バッククォートで囲み、${xxx}
で変数展開が可能になった。
改行とかも反映される。
const dt = new Date();
// before
console.log(dt.getFullYear() + '年' + (dt.getMonth()+1) + '月' + dt.getDate() + '日');
// after
console.log(`${dt.getFullYear()}年
${dt.getMonth()+1}月${dt.getDate()}日`);
#Introducing JSX
https://facebook.github.io/react/docs/introducing-jsx.html
<h1>Hello, world!</h1>
このタグ構文はJSXと呼ばれるJavaScriptの構文拡張です。
import React from 'react';
import { render } from 'react-dom';
const element = <h1>Hello, world!</h1>;
render(
element,
document.getElementById('root')
);
###Embedding Expressions in JSX
import React from 'react';
import { render } from 'react-dom';
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Harper',
lastName: 'Perez'
};
// JSXは{}の中に任意のJavaScript式を埋め込むことができる
const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);
render(
element,
document.getElementById('root')
);
###JSX is an Expression Too
コンパイル後、JSX式は通常のJavaScriptオブジェクトになります。
import React from 'react';
import { render } from 'react-dom';
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Harper',
lastName: 'Perez'
};
const element = getGreeting(user);
render(
element,
document.getElementById('root')
);
###Specifying Attributes with JSX
JSXでの属性の指定
import React from 'react';
import { render } from 'react-dom';
const user = {
url: 'sample.jpg'
}
// ""で囲んで属性を指定できる
const element1 = <div tabIndex="0">sample</div>;
// {}で囲むとJavaScript式で属性を設定できる
const element2 = <img src={user.url}></img>;
// これだと{}を含めた文字列になってしまうので失敗する
const element3 = <img src="{user.url}"></img>;
render(
element1,
document.getElementById('root')
);
###Specifying Children with JSX
JSXタグには子を含めることができます
import { render } from 'react-dom';
render(
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>,
document.getElementById('root')
);
###JSX Prevents Injection Attacks
Google翻訳〜
デフォルトでは、React DOMはそれらをレンダリングする前にJSXに埋め込まれた値をエスケープします。
したがって、アプリケーションに明示的に記述されていないものを挿入することは絶対にできません。
すべてがレンダリングされる前に文字列に変換されます。 これにより、XSS(クロスサイトスクリプティング)攻撃を防ぐことができます。
###JSX Represents Objects
BabelはJSXをReact.createElement()の呼び出しまでコンパイルする。
以下の2つの例は同じ意味。
import React from 'react';
import { render } from 'react-dom';
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
render(
element,
document.getElementById('root')
);
import React from 'react';
import { render } from 'react-dom';
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
render(
element,
document.getElementById('root')
);
#Rendering Elements
https://facebook.github.io/react/docs/rendering-elements.html
###Rendering an Element into the DOM
Google翻訳〜
HTMLファイルのどこかに<div>
があるとしましょう
これをReact DOMで管理するため、これを"root" DOM nodeと呼びます。
<div id="root"></div>
React要素をルートDOMノードにレンダリングするには、ReactDOM.render()に両方を渡します。
ReactDOM.render(
<h1>Hello, world</h1>,
document.getElementById('root')
);
###Updating the Rendered Element
setInterval()のコールバックから毎秒ReactDOM.render()を呼び出している例。
import React from 'react';
import { render } from 'react-dom';
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
render(
element,
document.getElementById('root')
);
}
setInterval(tick, 1000);
###React Only Updates What's Necessary
Google翻訳〜
React DOMは、要素とその子要素を前の要素と比較し、DOMを必要な状態にするために必要なDOM更新のみを適用します。
#Components and Props
https://facebook.github.io/react/docs/components-and-props.html
###Functional and Class Components
関数かクラスでコンポーネントを作成できる。
以下2つの例は同じ意味。
import React from 'react';
import { render } from 'react-dom';
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
render(
<Welcome name="Saitou" />,
document.getElementById('root')
);
import React from 'react';
import { render } from 'react-dom';
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
render(
<Welcome name="Saitou" />,
document.getElementById('root')
);
###Rendering a Component
ユーザー定義のコンポーネントを表すこともできます。
const element = <div />;
const element = <Welcome name="Sara" />;
import React from 'react';
import { render } from 'react-dom';
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
render(
<Welcome name="Saitou" />,
document.getElementById('root')
);
###Composing Components
コンポーネントは、他のコンポーネントを参照できます。
import React from 'react';
import { render } from 'react-dom';
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
render(
<App />,
document.getElementById('root')
);
###Extracting Components
以下の例ではコンポーネントは、すべてのネストのために変更するのが難しい場合があり、個々の部分を再利用することも困難です。
import React from 'react';
import { render } from 'react-dom';
const author = {
avatarUrl: 'sample.jpg',
name: 'Suzuki'
};
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{props.date}
</div>
</div>
);
}
render(
<Comment author={author} />,
document.getElementById('root')
);
コンポーネントを抽出することで、再利用可能なコンポーネントにするのが良い。
import React from 'react';
import { render } from 'react-dom';
const author = {
avatarUrl: 'sample.jpg',
name: 'Suzuki'
};
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author} />
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{props.date}
</div>
</div>
);
}
render(
<Comment author={author} />,
document.getElementById('root')
);
###Props are Read-Only
Propsは読み取り専用です。
例えば以下の関数のようにaとbを足した結果を返しているだけだとaもbも変更されていないのでOK
function sum(a, b) {
return a + b;
}
しかし以下のように代入などをしてしまうとそれはpropsとしてはNG
function withdraw(account, amount) {
account.total -= amount;
}
#State and Lifecycle
https://facebook.github.io/react/docs/state-and-lifecycle.html
Google翻訳〜
Rendering Elementsのセクションで以下のような例をあげました。
このセクションでは、Clockコンポーネントを本当に再利用可能にしてカプセル化する方法を学習します。
独自のタイマーを設定し、毎秒更新します。
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('root')
);
}
setInterval(tick, 1000);
###Converting a Function to a Class
Google翻訳〜
Clockのような機能コンポーネントを5つのステップでクラスに変換することができます
- React.Componentを継承する同じ名前のES6クラスを作成します。
- render()という単一の空のメソッドをそれに追加します。
- 関数の本体をrender()メソッドに移動します。
- render()本体のpropsをthis.propsに置き換えます。
- 残りの空の関数宣言を削除します。
import React from 'react';
import { render } from 'react-dom';
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
render(
<Clock date={new Date()} />,
document.getElementById('root')
);
###Adding Local State to a Class
stateを追加する
- this.props.dateをthis.state.dateに置き換えます。
- 初期this.stateを割り当てるクラスコンストラクタを追加します
-
<clock />
要素からdateのpropを削除する
import React from 'react';
import { render } from 'react-dom';
class Clock extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {date: new Date()};
+ }
render() {
return (
<div>
<h1>Hello, world!</h1>
- <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
+ <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
render(
- <Clock date={new Date()} />,
+ <Clock />,
document.getElementById('root')
);
###Adding Lifecycle Methods to a Class
ライフサイクルメソッドをクラスに追加する
- componentDidMount()は、コンポーネント出力がDOMにレンダリングされた後に実行されます。
これはタイマーを設定するのに適しています。 - render()で何かを使用しない場合、それは状態にあるべきではありません。
componentWillUnmount()ライフサイクルでタイマーを解除します。 - 毎秒実行されるtick()メソッドを実装します。
this.setState()を使用して、stateの更新をします。
import React from 'react';
import { render } from 'react-dom';
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
+ componentDidMount() {
+ this.timerID = setInterval(
+ () => this.tick(),
+ 1000
+ );
+ }
+ componentWillUnmount() {
+ clearInterval(this.timerID);
+ }
+ tick() {
+ this.setState({
+ date: new Date()
+ });
+ }
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
render(
<Clock />,
document.getElementById('root')
);
###Using State Correctly
setState()について知っておくべき3つのことがあります。
- Do Not Modify State Directly
stateは直接変更せずにsetState()を使用する
// 失敗
import React from 'react';
import { render } from 'react-dom';
class Sample extends React.Component {
constructor(props) {
super(props);
this.state = {moji: 'ここを変更したい'};
}
render() {
return (
<div>
<h1>{this.state.moji}</h1>
<button onClick={() => this.state.moji = '更新しない...'}>更新</button>
</div>
);
}
}
render(
<Sample />,
document.getElementById('root')
);
// 成功
import React from 'react';
import { render } from 'react-dom';
class Sample extends React.Component {
constructor(props) {
super(props);
this.state = {moji: 'ここを変更したい'};
}
render() {
return (
<div>
<h1>{this.state.moji}</h1>
<button onClick={() => this.setState({moji:'更新してくれる!'})}>更新</button>
</div>
);
}
}
render(
<Sample />,
document.getElementById('root')
);
this.stateを割り当てることができる唯一の場所はコンストラクタです。
- State Updates May Be Asynchronous
stateの更新が非同期である可能性がある、らしいです。
わからなかったので、とりあえず公式にある通り、失敗の可能性のあるコードとうまくいく書き方を書きました。あってますか?
// 失敗
// this.propsとthis.stateは非同期に更新される可能性があるため、次の状態を計算するために値を使用しないでください。
import React from 'react';
import { render } from 'react-dom';
class Count extends React.Component {
constructor(props) {
super(props);
this.state = {counter: 0};
}
render() {
return (
<div>
<p>{this.state.counter}</p>
<input
type='button'
onClick={() =>
this.setState({counter: this.state.counter + this.props.increment})
}
value="更新"
/>
</div>
);
}
}
render(
<Count increment={1} />,
document.getElementById('root')
);
// 成功
// これを修正するには、オブジェクトではなく関数を受け入れる第2の形式のsetState()を使用します。
// その関数は、最初の引数として前のstateを受け取り、2番目の引数として更新が適用されたときのpropsを受け取ります。
import React from 'react';
import { render } from 'react-dom';
class Count extends React.Component {
constructor(props) {
super(props);
this.state = {counter: 0};
}
render() {
return (
<div>
<p>{this.state.counter}</p>
<input
type='button'
onClick={() =>
- this.setState({counter: this.state.counter + this.props.increment})
+ this.setState((prevState, props) => ({
+ counter: prevState.counter + props.increment
+ }))
}
value="更新"
/>
</div>
);
}
}
render(
<Count increment={1} />,
document.getElementById('root')
);
- State Updates are Merged
stateに値がいくつあっても個別に更新できる
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
###The Data Flows Down
stateはそれを所有して設定するコンポーネント以外のコンポーネントからはアクセスできまない。
propsとして子コンポーネントへ渡すことはできる。
すべてのstateは一方向で下のコンポーネントにしか影響を与えない。
{/* stateを子コンポーネントのpropsとして渡す */}
<FormattedDate date={this.state.date} />
#Handling Events
https://facebook.github.io/react/docs/handling-events.html
htmlとJSXでの違い
<!-- html -->
<button onclick="activateLasers()">
Activate Lasers
</button>
// JSX
<button onClick={activateLasers}>
Activate Lasers
</button>
<!-- html -->
<a href="#" onclick="console.log('The link was clicked.'); return false">
Click me
</a>
以下2つとも同じ意味
// JSX
class ActionLink extends React.Component {
handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
render(){
return (
<a href="#" onClick={this.handleClick}>
Click me
</a>
);
}
}
// JSX
class ActionLink extends React.Component {
render(){
return (
<a href="#" onClick={e => {
e.preventDefault();
console.log('The link was clicked.');
}}>
Click me
</a>
);
}
}
クラス構文での書き方
import React from 'react';
import { render } from 'react-dom';
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// このバインディングは `this`をコールバックで動作させるために必要です
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
render(
<Toggle />,
document.getElementById('root')
);
#Conditional Rendering
https://facebook.github.io/react/docs/conditional-rendering.html
条件付きレンダリング
ユーザーがログインしているかどうかに応じて、次のいずれかのコンポーネントを表示するGreetingコンポーネントを作成
import React from 'react';
import { render } from 'react-dom';
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
render(
<Greeting isLoggedIn={false} />,
document.getElementById('root')
);
###Element Variables
公式サイトより短縮して書きました。
stateに応じて、またはのいずれかをレンダリングします。
import React from 'react';
import { render } from 'react-dom';
const Greeting = props => {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) { return <h1>Welcome back!</h1> };
return <h1>Please sign up.</h1>;
}
const LoginButton = props => <button onClick={props.onClick}>Login</button>;
const LogoutButton = props => <button onClick={props.onClick}>Logout</button>;
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.state = {isLoggedIn: false};
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button = null;
if (isLoggedIn) {
button = <LogoutButton onClick={() => this.setState({isLoggedIn: false})} />;
} else {
button = <LoginButton onClick={() => this.setState({isLoggedIn: true})} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
render(
<LoginControl />,
document.getElementById('root')
);
###Inline If with Logical && Operator
式を中括弧で囲んでJSXに組み込むことができます。
&&演算子が使用出来る。
import React from 'react';
import { render } from 'react-dom';
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);
これはJavaScriptで「true && expression」が常にexpressionに評価され、「false && expression」が常にfalseに評価されるために機能します。
したがって、条件がtrueの場合、&&の直後の要素が出力に表示されます。 falseの場合、Reactはそれを無視してスキップします。
###Inline If-Else with Conditional Operator
条件(三項)演算子が使える
import React from 'react';
import { render } from 'react-dom';
function Login(props) {
const isLoggedIn = true;
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}
render(
<Login />,
document.getElementById('root')
);
###Preventing Component from Rendering
より大きな表現もできる
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
</div>
);
}
#Lists and Keys
https://facebook.github.io/react/docs/lists-and-keys.html
Google翻訳〜
まずJavaScriptでリストをどのように変換するかを見てみましょう。
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
Reactでは、配列を要素のリストに変換することはほぼ同じです。
###Rendering Multiple Components
要素のコレクションを作成し、中括弧{}を使用してJSXに含めることができます。
import React from 'react';
import { render } from 'react-dom';
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
###Basic List Component
import React from 'react';
import { render } from 'react-dom';
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
Google翻訳〜
このコードを実行すると、リスト項目にキーを提供する必要があるという警告が表示されます。
「キー」は、要素のリストを作成するときに含める必要がある特別な文字列属性です。
numbers.map()内のリスト項目にキーを割り当て、不足しているキーの問題を修正しましょう。
import React from 'react';
import { render } from 'react-dom';
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
- <li>{number}</li>
+ <li key={number.toString()}>
+ {number}
+ </li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
###Keys
Google翻訳〜
キーは、どのアイテムが変更されたのか、追加されたのか、削除されたのかを識別します。
要素に安定したアイデンティティを与えるために、配列内の要素にキーを与える必要があります。
Google翻訳〜
キーを選択する最も良い方法は、兄弟間でリストアイテムを一意に識別する文字列を使用することです。
多くの場合、データのIDをキーとして使用します。
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
Google翻訳〜
レンダリングされたアイテムのIDが安定していない場合は、アイテムインデックスを最後の手段としてキーとして使用できます。
どういう意味だと思ったのですが、mapは第2引数で現在処理中の配列内の要素のインデックスを取得できるので、
それを一意のkeyとして使ってね、ということのようですね。
参考
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map
以下実装例です。
import React from 'react';
import { render } from 'react-dom';
function NumberList(props) {
return (
<ul>
{props.numbers.map((number,index) => (
<li key={index}>
{number}
</li>
)
)}
</ul>
);
}
render(
<NumberList numbers={[1, 2, 3, 4, 5]} />,
document.getElementById('root')
);
###Extracting Components with Keys
Google翻訳〜
キーは、周囲の配列のコンテキストでのみ意味があります。
たとえば、ListItemコンポーネントを抽出する場合は、ListItem自体のルート<li>
要素ではなく、配列の<ListItem />
要素にキーを保持する必要があります。
import React from 'react';
import { render } from 'react-dom';
function ListItem(props) {
const value = props.value;
return (
// Wrong! ここでキーを指定する必要はありません。
<li key={value.toString()}>
{value}
</li>
);
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Wrong! キーはここで指定されているはずです:
<ListItem value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
import React from 'react';
import { render } from 'react-dom';
function ListItem(props) {
// Correct! ここでキーを指定する必要はありません。
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Correct! キーは配列内で指定する必要があります。
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
###Keys Must Only Be Unique Among Siblings
Google翻訳〜
配列内で使用されるキーは、兄弟間で一意でなければなりません。 しかし、それらは世界的にユニークである必要はありません。 2つの異なる配列を生成する場合、同じキーを使用できます。
import React from 'react';
import { render } from 'react-dom';
function Blog(props) {
const sidebar = (
<ul>
{props.posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
)}
</ul>
);
const content = props.posts.map((post) =>
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
return (
<div>
{sidebar}
<hr />
{content}
</div>
);
}
const posts = [
{id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
{id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
render(
<Blog posts={posts} />,
document.getElementById('root')
);
###Embedding map() in JSX
JSXにmapの埋め込みが出来るということですね。
個人的にはネストがわかりやすいのでこっちの方がいいと思います。
import React from 'react';
import { render } from 'react-dom';
function ListItem(props) {
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
render(
<NumberList numbers={[1,2,3,4,5]} />,
document.getElementById('root')
);
#Forms
https://facebook.github.io/react/docs/forms.html
import React from 'react';
import { render } from 'react-dom';
render(
<form>
<label>
Name:
<input type="text" name="name" />
</label>
<input type="submit" value="Submit" />
</form>,
document.getElementById('root')
);
Google翻訳〜
このフォームには、ユーザーがフォームを送信したときに新しいページを参照するデフォルトのHTMLフォーム動作があります。
あなたがReactでこの動作をしたいのであれば、うまく動作します。
しかし、ほとんどの場合、フォームの送信を処理し、ユーザーがフォームに入力したデータにアクセスできるJavaScript関数を持つと便利です。
これを達成するための標準的な方法は、「被制御コンポーネント」と呼ばれる技術です。
###Controlled Components
HTMLでは、<input>
、<textarea>
、<select>
などのフォーム要素は通常、独自の状態を維持し、ユーザーの入力に基づいて更新します。 Reactでは、可変状態は通常、コンポーネントのstateプロパティに保持され、setState()でのみ更新されます。
import React from 'react';
import { render } from 'react-dom';
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
render(
<NameForm />,
document.getElementById('root')
);
Google翻訳〜
value属性はフォーム要素に設定されているので、表示される値は常にthis.state.valueになり、React状態が真実のソースになります。
React状態を更新するために、すべてのキーストロークでhandleChangeが実行されるため、表示された値はユーザーの入力に従って更新されます。
onChangeを設定すると、inputタグの中身を変更するたびにイベントが発生するということですね。
import React from 'react';
import { render } from 'react-dom';
render(
<input type="text" onChange={(e) => console.log(e.target.value)} />,
document.getElementById('root')
);
###The textarea Tag
textareaはhtmlではタグの子として値を設定するけど、
Reactではvalue属性を使用してという意味のようです。
import React from 'react';
import { render } from 'react-dom';
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'あなたの好きなDOM要素に関するエッセイを書いてください。'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('An essay was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
render(
<EssayForm />,
document.getElementById('root')
);
###The select Tag
import React from 'react';
import { render } from 'react-dom';
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Your favorite flavor is: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Pick your favorite La Croix flavor:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
render(
<FlavorForm />,
document.getElementById('root')
);
###Handling Multiple Inputs
Google翻訳〜
複数の制御入力要素を処理する必要がある場合は、各要素にname属性を追加して、event.target.nameの値に基づいて何を行うかをハンドラー関数に選択させることができます。
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
Is going:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Number of guests:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
console.log(this.state)
で見てみると、狙い通りstateが変更されていることがわかります。
#Lifting State Up
https://facebook.github.io/react/docs/lifting-state-up.html
Google翻訳〜
しばしば、いくつかのコンポーネントが同じ変更データを反映する必要があります。 共有状態を最も近い共通の祖先まで持ち上げることをお勧めします。 これがどのように動作するかを見てみましょう。
このセクションでは、与えられた温度で水が沸騰するかどうかを計算する温度計算機を作成します。
水を沸騰させるのに十分かどうかを表示するBoilingVerdictコンポーネントと、
温度を入力できるようにするCalculatorコンポーネントを作成する。
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>The water would boil.</p>;
}
return <p>The water would not boil.</p>;
}
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value});
}
render() {
const temperature = this.state.temperature;
return (
<fieldset>
<legend>Enter temperature in Celsius:</legend>
<input
value={temperature}
onChange={this.handleChange} />
<BoilingVerdict
celsius={parseFloat(temperature)} />
</fieldset>
);
}
}
###Adding a Second Input
Google翻訳〜
我々の新しい要件は、摂氏入力に加えて、華氏入力を提供し、それらは同期して維持されるということです。
わか化学とかは全くわかりませんので、摂氏の意味はわかりますけど、華氏が何かわかりませんでした、、、
Wikiを見てみました。
https://ja.wikipedia.org/wiki/%E8%8F%AF%E6%B0%8F
温度を測る尺度が数種類あって、その一つに華氏がある。
で、それを判定して摂氏、華氏の両方で水が沸騰するかどうか判定するものを作ると言うことのようです。
import React from 'react';
import { render } from 'react-dom';
+ const scaleNames = {
+ c: 'Celsius',
+ f: 'Fahrenheit'
+ };
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>The water would boil.</p>;
}
return <p>The water would not boil.</p>;
}
- class Calculator extends React.Component {
+ class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value});
}
render() {
const temperature = this.state.temperature;
const scale = this.props.scale;
return (
<fieldset>
- <legend>Enter temperature in Celsius:</legend>
+ <legend>Enter temperature in {scaleNames[scale]}:</legend>
<input
value={temperature}
onChange={this.handleChange} />
<BoilingVerdict
celsius={parseFloat(temperature)} />
</fieldset>
);
}
}
+ class Calculator extends React.Component {
+ render() {
+ return (
+ <div>
+ <TemperatureInput scale="c" />
+ <TemperatureInput scale="f" />
+ </div>
+ );
+ }
+ }
render(
<Calculator />,
document.getElementById('root')
);
###Writing Conversion Functions
Google翻訳〜
まず、摂氏から華氏に変換するための2つの関数を書きます
function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}
function toFahrenheit(celsius) {
return (celsius * 9 / 5) + 32;
}
Google翻訳〜
これら2つの関数は数値を変換します。 文字列温度と変換関数を引数とし、文字列を返す別の関数を記述します。 もう1つの入力に基づいて1つの入力の値を計算するためにこれを使用します。
Google翻訳〜
無効な温度で空の文字列を返し、出力を小数点第3位に丸めます。
function tryConvert(temperature, convert) {
const input = parseFloat(temperature);
if (Number.isNaN(input)) {
return '';
}
const output = convert(input);
const rounded = Math.round(output * 1000) / 1000;
return rounded.toString();
}
###Lifting State Up
説明だけ見てても私はちょっとわかりにくかったので、とりあえず動くサンプルとしては以下のような感じです。
なんとなく言いたいことは、別のコンポーネントへ値を渡したい場合は、親コンポーネントに値をもたせて、それぞれの子コンポーネントから処理をしたら良い、と言うこと?だと思います。
import React from 'react';
import { render } from 'react-dom';
const scaleNames = {
c: 'Celsius',
f: 'Fahrenheit'
};
function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}
function toFahrenheit(celsius) {
return (celsius * 9 / 5) + 32;
}
function tryConvert(temperature, convert) {
const input = parseFloat(temperature);
if (Number.isNaN(input)) {
return '';
}
const output = convert(input);
const rounded = Math.round(output * 1000) / 1000;
return rounded.toString();
}
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>The water would boil.</p>;
}
return <p>The water would not boil.</p>;
}
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
- this.setState({temperature: e.target.value});
+ this.props.onTemperatureChange(e.target.value);
}
render() {
- const temperature = this.state.temperature;
+ const temperature = this.props.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input
value={temperature}
onChange={this.handleChange} />
- <BoilingVerdict
- celsius={parseFloat(temperature)} />
</fieldset>
);
}
}
class Calculator extends React.Component {
+ constructor(props) {
+ super(props);
+ this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
+ this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
+ this.state = {temperature: '', scale: 'c'};
+ }
+
+ handleCelsiusChange(temperature) {
+ this.setState({scale: 'c', temperature});
+ }
+
+ handleFahrenheitChange(temperature) {
+ this.setState({scale: 'f', temperature});
+ }
+ render() {
+ const scale = this.state.scale;
+ const temperature = this.state.temperature;
+ const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
+ const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
+
+ return (
- <div>
- <TemperatureInput scale="c" />
- <TemperatureInput scale="f" />
- </div>
+ <div>
+ <TemperatureInput
+ scale="c"
+ temperature={celsius}
+ onTemperatureChange={this.handleCelsiusChange} />
+ <TemperatureInput
+ scale="f"
+ temperature={fahrenheit}
+ onTemperatureChange={this.handleFahrenheitChange} />
+ <BoilingVerdict
+ celsius={parseFloat(celsius)} />
+ </div>
+ );
+ }
+ }
render(
<Calculator />,
document.getElementById('root')
);
#Composition vs Inheritance
https://facebook.github.io/react/docs/composition-vs-inheritance.html
Google翻訳〜
Reactは強力な合成モデルを持ち、継承の代わりに合成を使用してコンポーネント間でコードを再利用することを推奨します。
このセクションでは、Reactを初めて使用する開発者がしばしば継承の対象となるいくつかの問題を検討し、どのように問題を解決できるかを示します。
###Containment
Google翻訳〜
一部のコンポーネントは、事前に子供を知らないコンポーネントもあります。 これは、一般的な「ボックス」を表すサイドバーやダイアログなどのコンポーネントで特に一般的です。
これにより、JSXをネストすることによって、他のコンポーネントが任意の子をそれらに渡すことができます。
import React from 'react';
import { render } from 'react-dom';
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
render(
<WelcomeDialog />,
document.getElementById('root')
);
#Thinking In React
https://facebook.github.io/react/docs/thinking-in-react.html
Google翻訳〜
Reactは、私たちの意見では、JavaScriptを使って大きくて速いWebアプリケーションを構築するための最も優れた方法です。 それはFacebookとInstagramで非常にうまくスケーリングされています。
Google翻訳〜
Reactの多くの大きな部分の1つは、アプリケーションを構築するときにアプリケーションをどのように考えるかということです。 このドキュメントでは、Reactを使用して検索可能な製品データテーブルを構築するという考え方について説明します。