LoginSignup
0
0

More than 5 years have passed since last update.

React.js official doc

Last updated at Posted at 2018-08-19

ReactのDocsのMain Concepts部分を訳してみました。Versionは16.4.2。

Main concepts

Hello world

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
    <h1>Hello, world!</h1>,
    document.getElementById('root')
);

Introducing JSX (JSX入門)

JSXに式を埋め込む。

import React from 'react';
import ReactDOM from 'react-dom';

function formatName(user) {
    return user.firstName + ' ' + user.lastName;
}

const user = {
    firstName: 'Harper',
    lastName: 'Perez'
};

//// element ////
const element = (
    <h1>
        Hello, {formatName(user)}!
  </h1>
);

ReactDOM.render(
    element,
    document.getElementById('root')
);

Rendering Elements

Reactの要素(element)は不変(immutable)である。一度作成したら属性などを変更できない。ここまでの知識では,UIを更新するためには新しい要素を作ってReactDOM.render()に渡す必要がある。

import React from 'react';
import ReactDOM from 'react-dom';

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);

Components and Props

コンポーネントを定義するもっとも簡単な方法は,JavaScriptの関数を書くことである。

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

ES6クラスを使ってコンポーネントを定義することもできる。

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

functionを使った場合

import React from 'react';
import ReactDOM from 'react-dom';

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

classを使った場合

import React from 'react';
import ReactDOM from 'react-dom';

class Welcome extends React.Component {
    render() {
        return <h1>Hello, {this.props.name}</h1>;
    }
}

//// Passing a property here ////
const element = <Welcome name="Sara" />;
ReactDOM.render(
    element,
    document.getElementById('root')
);

上の例ではWelcomeコンポーネントに対して{name: 'Sara'}をプロパティとして渡す。

コンポーネントから別のコンポーネントを参照することもできる。次の例では,Welcomeコンポーネントを複数回描画するAppコンポーネントを作ることができる。

import React from 'react';
import ReactDOM 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>
  );
}

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

CodePen

State and Lifecycle (状態とライフサイクル)

import React from 'react';
import ReactDOM from 'react-dom';

function Clock(props) {
    return (
        <div>
            <h1>Hello, world!</h1>
            <h2>It is {props.date.toLocaleTimeString()}.</h2>
        </div>
    );
}

function tick() {
    ReactDOM.render(
        <Clock date={new Date()} />,
        document.getElementById('root')
    );
}

setInterval(tick, 1000);

上の例では,the fact that the Clock sets up a timer and updates the UI every second should be an implementation detail of the Clockという要件を満たしていない。

FunctionをClassに変換する

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

function tick() {
  ReactDOM.render(
    <Clock date={new Date()} />,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);

CodePen

Classにlocal stateを追加する

import React from 'react';
import ReactDOM 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.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

Adding Lifecycle Methods to a Class

Clock が最初にDOMを描画したら,タイマーをセットする。これをマウントする,という。
Clock によって生成されたDOMが削除されたら,タイマーをクリアする。これをアンマウントする,という

import React from 'react';
import ReactDOM from 'react-dom';

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  //// componentDidMound ////
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  //// componentWillUnmount ////
  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    //// update local state ////
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

Handling Events

  • ReactイベントはcamelCaseで書く

Toggle コンポーネントが"ON"/"OFF"を交互に描画する。

import React from 'react';
import ReactDOM from 'react-dom';

class Toggle extends React.Component {
    constructor(props) {
        super(props);
        this.state = { isToggleOn: true };

        // This binding is necessary to make `this` work in the callback
        //// Binding ////
        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>
        );
    }
}

ReactDOM.render(
    <Toggle />,
    document.getElementById('root')
);

JavaScriptでは,デフォルトではクラスメソッドはバインディングされない。this.handleClickonClickをバインドするのを忘れると,コールバックが呼ばれたとき,thisundefinedになる。

Conditional Rendering

import React from 'react';
import ReactDOM 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;

    //// condition ////
    if (isLoggedIn) {
        return <UserGreeting />;
    }
    return <GuestGreeting />;
}

ReactDOM.render(
    <Greeting isLoggedIn={false} />,
    document.getElementById('root')
);

要素変数

要素(element)に変数を使うことができる。これは条件に応じて一部だけ再描画するのを助ける。

import React from 'react';
import ReactDOM from 'react-dom';

function UserGreeting(props) {
    return <h1>Welcome back!</h1>;
}

function GuestGreeting(props) {
    return <h1>Please sign up.</h1>;
}

//// Greeting(message) component ////
function Greeting(props) {
    const isLoggedIn = props.isLoggedIn;
    if (isLoggedIn) {
        return <UserGreeting />;
    }
    return <GuestGreeting />;
}

//// Login button component ////
function LoginButton(props) {
    return (
        <button onClick={props.onClick}>
            Login
    </button>
    );
}

//// Logout button component ////
function LogoutButton(props) {
    return (
        <button onClick={props.onClick}>
            Logout
    </button>
    );
}

class LoginControl extends React.Component {
    constructor(props) {
        super(props);
        //// Binding ////
        this.handleLoginClick = this.handleLoginClick.bind(this);
        this.handleLogoutClick = this.handleLogoutClick.bind(this);

        //// state ////
        this.state = { isLoggedIn: false };
    }

    //// Handler sets state ////
    handleLoginClick() {
        this.setState({ isLoggedIn: true });
    }

    handleLogoutClick() {
        this.setState({ isLoggedIn: false });
    }

    render() {
        const isLoggedIn = this.state.isLoggedIn;
        let button;

        if (isLoggedIn) {
            button = <LogoutButton onClick={this.handleLogoutClick} />;
        } else {
            button = <LoginButton onClick={this.handleLoginClick} />
        }

        return (
            <div>
                <Greeting isLoggedIn={isLoggedIn} />
                {button}
            </div>
        );
    }
}

ReactDOM.render(
    <LoginControl />,
    document.getElementById('root')
);

インラインIf-Else

JSXに式を埋め込むことができる。

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
    </div>
  );
}

コンポーネントの描画を防ぐ

コンポーネントを描画したくない場合は,nullを返す。

function WarningBanner(props) {
  //// Return null if warn is null ////
  if (!props.warn) {
    return null;
  }

  return (
    <div className="warning">
      Warning!
    </div>
  );
}

class Page extends React.Component {
  constructor(props) {
    super(props);
    this.state = {showWarning: true};
    this.handleToggleClick = this.handleToggleClick.bind(this);
  }

  handleToggleClick() {
    this.setState(prevState => ({
      showWarning: !prevState.showWarning
    }));
  }

  render() {
    return (
      <div>
        //// Warning banner ////
        <WarningBanner warn={this.state.showWarning} />
        <button onClick={this.handleToggleClick}>
          {this.state.showWarning ? 'Hide' : 'Show'}
        </button>
      </div>
    );
  }
}

ReactDOM.render(
  <Page />,
  document.getElementById('root')
);

Lists and Keys

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);

結果は

 [2, 4, 6, 8, 10] 

Basic List Component

import React from 'react';
import ReactDOM from 'react-dom';

function NumberList(props) {
  const numbers = props.numbers;

  //// build listItems ////
  const listItems = numbers.map((number) =>
    <li>{number}</li>
  );
  return (
    //// return list ////
    <ul>{listItems}</ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

Keys

For example, if you extract a ListItem component, you should keep the key on the elements in the array rather than on the

element in the ListItem itself.
import React from 'react';
import ReactDOM from 'react-dom';

function ListItem(props) {
  // Correct! There is no need to specify the key here:
  //// valueのみを返す ////
  return <li>{props.value}</li>;
}

function NumberList(props) {
  const numbers = props.numbers;

  //// list Items ////
  const listItems = numbers.map((number) =>
    // Correct! Key should be specified inside the array.
    <ListItem key={number.toString()} value={number} />

  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

Forms

import React from 'react';
import ReactDOM from 'react-dom';

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    //// value ////
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  //// Event handler for text, setting state ////
  handleChange(event) {
    this.setState({value: event.target.value});
  }

  //// Event handler for submit, show alert ////
  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          //// Call event handler every keystoroke ////
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

ReactDOM.render(
  <NameForm />,
  document.getElementById('root')
);

Lifting State Up

複数のコンポーネント間で,同様の変更を反映したい場合がある。その場合は,共通の先祖(common ancestor)と状態(state)を共有することを推奨する。

1.与えられた温度で水が沸騰するか計算する
2.まず,摂氏をプロパティとして受け付けるコンポーネントを作る
3.次に,華氏を入力として受け付ける
4.温度を変化する関数を作る(摂氏を華氏に。華氏を摂氏に)
5.それぞれの入力を同期させる必要がある。摂氏を更新したら,華氏も反映される。

Reactでは共通の先祖に状態を持ち上げることで状態の共有を実現する。
これを"Lifting state up"と呼ぶ。
TemperatureInputから得られるローカルステートを取り除き,代わりにCalculatorに移動させる。

import React from 'react';
import ReactDOM from 'react-dom';

const scaleNames = {
  c: 'Celsius',
  f: 'Fahrenheit'
};

//// 華氏to摂氏 ////
function toCelsius(fahrenheit) {
  return (fahrenheit - 32) * 5 / 9;
}

//// 摂氏to華氏 ////
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);
  }

  //// プロパティで渡されたハンドラ
  ////  handleCelsiusChangeまたはhandleFahrenheitChangeを呼ぶ ////
  handleChange(e) {
    this.props.onTemperatureChange(e.target.value);
  }

  render() {
    //// 渡された温度 ////
    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} />
      </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>
        //// プロパティとしてhandleCelsiusChangeを渡す ////
        <TemperatureInput
          scale="c"
          temperature={celsius}
          onTemperatureChange={this.handleCelsiusChange} />

        //// プロパティとしてhandleFahrenheitChangeを渡す ////
        <TemperatureInput
          scale="f"
          temperature={fahrenheit}
          onTemperatureChange={this.handleFahrenheitChange} />

        <BoilingVerdict
          celsius={parseFloat(celsius)} />
      </div>
    );
  }
}

ReactDOM.render(
  <Calculator />,
  document.getElementById('root')
);

Composition vs Inheritance (コンポジットと継承)

Reactでは継承の代わりにコンポジション(composition)を使うことを推奨する。
Reactを初めて使う開発者は継承を求めるが,それをコンポジションでどのように解決するか示す。

  • childプロパティを使って子エレメントを渡す
function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      //// child: Welcome Thank you for.... ////
      {props.children}
    </div>
  );
}

function WelcomeDialog() {
  return (
    <FancyBorder color="yellow">
      <h1 className="Dialog-title">
        Welcome
      </h1>
      <p className="Dialog-message">
        Thank you for visiting our spacecraft!
      </p>
    </FancyBorder>
  );
}

ReactDOM.render(
  <WelcomeDialog />,
  document.getElementById('root')
);

Thinking in React

Step1: UIをコンポーネント階層に分解する

  • FilterableProductTable (全体を包含する)
    • SearchBar (ユーザーの入力を受け付ける)
    • ProductTable (ユーザー有力に基づきデータコレクションを表示する)
      • ProductCategoryRow (各カテゴリーの先頭)
      • ProductRow (各プロダクト)

Step2: 静的バージョンを作る

インタラクティブでないUIを作ってみる。この段階ではステートを使わないこと。

Step 3: Identify The Minimal (but complete) Representation Of UI State

何がステートか見ていこう。各データについて次の3つの質問に答えるだけ。
1. 親コンポーネントからプロパティを経由して渡されるか?もしそうであれば,それはステートではない。
2. 時間の経過にともない変化しないか?もしそうであれば,それはステートではない。
3. それは,ほかの状態の変化やプロパティの値から計算できるか?もしそうであれば,それはステートではない。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0