[[JSライブラリ]] > React.js
* React.js [#y37ef538]
#setlinebreak(on)

#todo(Redux)

#contents
-- 参考
--- https://reactjs.org/docs/try-react.html
--- https://reactjs.org/docs/add-react-to-a-new-app.html
--- https://reactjs.org/docs/add-react-to-an-existing-app.html
-- サンプル
--- http://www.magata.net/test/react/


** プロジェクトの作成とローカルサーバ起動 [#va1aa7c6]
#html(<div style="padding-left:10px;">)

https://reactjs.org/docs/add-react-to-a-new-app.html

#myterm2(){{
npm install -g create-react-app
create-react-app my-app

cd my-app
npm start
}}
※http://localhost:3000/ でアクセス可能。

#html(</div>)

** ビルド [#g9dc70a8]
#html(<div style="padding-left:10px;">)
#myterm2(){{
npm run build
}}
※build 配下に出力されるファイルをWeb公開ディレクトリに配置する。

&color(red){ただし、HTTPSアクセスでないと service-worker.js などの読み込みに失敗する。};(証明書が有効でない時も同じ)
なので、プライベートCAで自己署名した場合などは、プライベートCAの証明書をブラウザにインポートする必要がある。
※参考 ... [[Javaでhttps通信時の証明書検証について]]、[[ApacheでSSL(SNI)設定]]

*** サブディレクトリ配下で公開する場合 [#c0bccb32]
#html(<div style="padding-left:10px;">)
package.json に以下を追記してからビルド
#mycode2(){{
{
  .
  .
  "homepage": "/test/react/"
}
}}
※ http://xxx.xxx.xxx/test/react/ 配下で公開する場合

ルーティング定義時(BrowserRouter)は basename で指定する(後述)
#myhtml2(){{
      <BrowserRouter basename="/test/react"> <!-- サブディレクトリ配下で公開する場合 -->
}}

#html(</div>)

*** CDNを利用する場合 [#o31ec96c]
#html(<div style="padding-left:10px;">)
React、ReactDOM は CDNも利用できるらしい。
・・が、通信がクロスドメインになるので、scriptタグに crossorigin 属性付けろ、とか  Access-Control-Allow-Origin: * ヘッダを付けろとか書いてる。

https://reactjs.org/docs/cdn-links.html

#html(</div>)

#html(</div>)

** レンダリング、JSXについて [#k51b6142]
#html(<div style="padding-left:10px;">)

*** 基本ルール [#t64ceeb1]
#html(<div style="padding-left:10px;">)

HTML要素はjs内にJSXで記載し変数として扱う事ができる。また、この変数は変数として扱う事ができる。
#myhtml2(){{
const element = <h1>Hello, world</h1>;
ReactDOM.render( element, document.getElementById('root') );
}}


テンプレートは関数 または コンポーネントとして定義する事ができる。

関数定義
#myhtml2(){{
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
ReactDOM.render( <Welcome name="Test" />, document.getElementById('root') );
}}

コンポーネント定義
#myhtml2(){{
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
ReactDOM.render( <Welcome name="Test" />, document.getElementById('root') );
}}

JSXへの変数展開は &#123; &#125; で記述する。
#myhtml2(){{
<h1>Hello, {user.name}</h1>
}}


HTML要素は createElement する事もできる。
#mycode2(){{
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

// または
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world'
  }
};
}}

#html(</div>)

*** CSSの記述 [#d8cd1654]
#html(<div style="padding-left:10px;">)
ハイフンは使用できない(キャメルケースで記載する)
#mycode(){{
style=&#123;&#123;marginRight: '10em'&#125;&#125;    ..  OK
style="margin-right: 10em"      ..  NG
}}
#html(</div>)

*** classの記述 [#e2542abb]
#html(<div style="padding-left:10px;">)
class は className と記載する。
#mycode2(){{
className="greeting"  .. OK
class="greeting"    ..  NG
}}
#html(</div>)


*** リストの描画 [#me3ae983]
#myhtml2(){{
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];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);
}}

#html(</div>)

** ページ切り替え [#id2f89a7]
#html(<div style="padding-left:10px;">)
参考 : https://qiita.com/takanorip/items/649c7862a8a5380dd8be

react-router-dom を使用する

*** インストール [#q768b31b]
#html(<div style="padding-left:10px;">)
#myterm2(){{
npm install react-router --save-dev
npm install react-router-dom --save-dev
}}
#html(</div>)

*** ルーティング定義など [#k8b7b304]
#html(<div style="padding-left:10px;">)
App.js
#myhtml2(){{
import React, { Component } from 'react';
import { BrowserRouter, Route, Link } from 'react-router-dom';

import Home from "./components/Home";
import Page1 from "./components/Page1";
import Page2 from "./components/Page2";
import './App.css';

class App extends Component {

  name = 'Test1';

  render() {
    return (
      <BrowserRouter basename="/test/react"> <!-- サブディレクトリ配下で公開する場合 -->
        <div className="App">
          <div id="header">
            ヘッダ
          </div>
          <ul id="header-menu">
            <li><Link to="/">Home</Link></li>
            <li><Link to="/page1">Page1へ</Link></li>
            <li><Link to="/page2">Page2へ</Link></li>
          </ul>
          <div id="content">
            <Route exact path="/" component={Home} />
            <Route path="/page1" component={Page1} />
            <Route path="/page2" component={Page2} />
          </div>
          <div id="footer">
            フッタ
          </div>
        </div>
      </BrowserRouter>
    );  
  }
}

export default App;
}}
#html(</div>)

*** 使用例 [#v78c7fcb]
#html(<div style="padding-left:10px;">)
・リンクによる切り替えは Link を使用する。
・処理で遷移する場合は history を使用する。※withRouter を使用しつつ、メソッドを bind する事により history へのアクセスが可能になる。
 ※react router v4

components/Home.js
#myhtml2(){{
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { withRouter } from 'react-router';

class Home extends Component {

  name = 'Home';

  //constructor(props) {
  //  super(props);
  //  //this.movePage = this.movePage.bind(this)
  //} 

  moveTop(e) {
    console.log("moveTop");
    this.props.history.push('/');
  }

  movePage(url,e) {
    console.log("movePage");
    e.preventDefault();
    this.props.history.push(url);
  }

  render() {
    return (
      <div className="Home">
        <h1>{this.name}!</h1>
        <br />
        <div>
          <h3>イベントハンドラからページ切り替え</h3>
          <div style=&#123;&#123;paddingLeft:'10px'&#125;&#125;>
            <button onClick={this.movePage.bind(this, '/')}>Topへ</button><br />
            <button onClick={this.movePage.bind(this, '/page1')}>page1へ</button><br />
            <button onClick={this.movePage.bind(this, '/page2')}>page2へ</button><br />
          </div>
        </div>
        <br />
        <div>
          <h3>Linkによるページ切り替え</h3>
          <ul style=&#123;&#123;marginLeft: '10px', paddingLeft:'10px' &#125;&#125;> 
            <li><Link to="/">Home</Link></li>
            <li><Link to="/page1">Page1へ</Link></li>
            <li><Link to="/page2">Page2へ</Link></li>
          </ul>
        </div>
      </div>
    );  
  }
}

export default withRouter(Home);
}}
#html(</div>)

#html(</div>)


** イベント処理 [#r9b9f211]
#html(<div style="padding-left:10px;">)

https://reactjs.org/docs/handling-events.html

*** 初期処理 [#jd694d88]
#html(<div style="padding-left:10px;">)
componentDidMount で初期処理を行う事ができる。
#mycode2(){{
class Page1 extends Component {

  componentWillMount() {
    // ここで初期処理を行う
  };
  .
  .
}
}}
#html(</div>)

*** クリック時の処理など [#r4a04fe6]
#html(<div style="padding-left:10px;">)

関数定義する場合
#myhtml2(){{
function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}
}}

コンポーネント化する場合
#myhtml2(){{
class LoggingButton extends React.Component {
  handleClick = () => {
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}
}}
#html(</div>)

#html(</div>)

** 非同期通信 [#ob0771a2]
#html(<div style="padding-left:10px;">)

以下には jQuery などの好きなライブラリを使えと書いてある。React 組み込みのものは無い模様。
※ Promiseを返すものを使っていれば実装はそんなに変わらないと思う。
※ Promiseを返すものを使っていれば実装はそんなに変わらないと思うが、ラップはしておいた方が都合は良さそう。

https://reactjs.org/docs/faq-ajax.html#how-can-i-make-an-ajax-call

*** サンプル [#u0288ba5]
#html(<div style="padding-left:10px;">)

とりあえず参考URL の通り実装してみる。

#myhtml2(){{
import React, { Component } from 'react';

class Page1 extends Component {

  constructor(props) {
    super(props);
    this.state = { 
      error: null,
      isLoaded: false,
      items: []
    };  
  }

  componentDidMount(){
    fetch("http://example.com/api/books/")
      .then(res => res.json())
      .then(
        (result) => {
          this.setState({
            isLoaded: true,
            items: result.items
          }); 
        },  
        (error) => {
          this.setState({
            isLoaded: true,
            error
          }); 
        }   
      )   
  }

  render() {
    const { error, isLoaded, items } = this.state;
    if (error) {
      return <div>Error: {error.message}</div>;
    } else if (!isLoaded) {
      return <div>Loading...</div>;
    } else {
      return (
        <div className="Page1">
          <h1>Page1!!</h1>
          <br />
          <table>
            <thead>
              <tr>
                <th>No</th><th>Isbn</th><th>Title</th><th>Price</th><th>Date</th>
              </tr>
            </thead>
            <tbody>
              {items.map(item => (
                <tr key={item.isbn}>
                  <td>X</td><td>{item.isbn}</td><td>{item.title}</td><td>{item.price}</td><td>{item.date}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      );  
    }
  } 
} 

export default Page1;
}}
#html(</div>)


#html(</div>)

トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS