LoginSignup
4
4

More than 3 years have passed since last update.

Angular開発者のためのReact基本

Last updated at Posted at 2020-04-02

Angular開発者のためのReact基本

Angularの人気がよろしくない。
OhAngular...png
「今後学習するつもりもないと回答するユーザーの割合多い。徐々に使用割合が減っていく可能性が見えてきている。」JavaScriptフロントエンドフレームワーク、Angularの人気が下落中

ということでReactやVueへ移行するプロジェクト、それらの習得に軸足を移していくフロントエンジニアもいるでしょう。私もそう。Googleのネームバリューとフルスタックフレームワークという豪奢性に飛びついたのだが。

というわけでこれからはAngularのメンテをしながら新たにReactをやっていこう。なによりReact Nativeのコミュニティが妙に熱い。

初期設定

まずnode.jsをインストール

nodejsダウンロード

Reactアプリケーションを作成

$ npx create-react-app <アプリケーション名>
または
$ npm init react-app <アプリケーション名>

最後にHappy hacking!と表示されていればOK。

Reactアプリケーションを起動(http://localhost:3000)

プロジェクトフォルダに移動して、

$ npm start
または
$ yarn start

Reactアプリケーションをビルド

$ npm run build
または
$ yarn build

React Developper Tools のインストール

Chromeプラグイン

https://chrome.google.com/webstore
でインストールする。
image.png

スタンドアロン版

以下のコマンドでインストール

$ npm install -g react-devtools

以下のコマンドで起動

$ react-devtools

そして、アプリケーションのほうのindex.htmlに、

<script src="http://localhost:8097"></script>

<head>タグ内に入れておく。ビルドするときは外す。


開発途中いろいろ

コンポーネント

ng generate component みたくコマンドで作成する必要ない。TSとHTMLとCSSでファイルが分けてあるとかもない。もちろん分けてもよい。原則としては.jsだけで完結できる。デフォルトでindex.jsとApp.jsが存在する。index.jsが最初に読み込まれると考えてよい。App.jsがAngularにおけるapp.component.ts的なもの。例えば以下のように、App.jsファイルにひとつまたは複数のクラス(関数でも可)を書くと、クラスそのものがそのまんまコンポーネントになる。

//App.js
import React, { Component } from 'react';

 // Appコンポーネントクラス
class App extends Component {
  h1 = {
    size:"30px"
  }
  constructor(props){
    super(props); // ← おまじない
  }
  render() {
    return (
      <div>
        <h1 style={this.h1}>Home</h1>
      </div>
    );
  }
}

export default App // Appコンポーネントをエクスポート

HTMLとCSSもApp.js内に書いてあり、最終的にはrender(){return(……)}関数の中にHTMLを書いてJSXにレンダリングさせる。「①TSファイル②HTMLファイル③CSSファイル」の3つでワンセットとするAngularと比べるとカオスな感じがある。

コンストラクターの中のsuper(props);は上位コンポーネントのプロパティを継承するという意味だが必ずつけるおまじないと思ってよい。

このようにして作成したAppコンポーネント、これを利用する側のコンポーネントでは(ここではindex.js)、

//index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'; // ← インポートして

ReactDOM.render(
   <App />         // ← 読み込む
  document.getElementById('root') // ← おまじない
);

のように、<App />のような形で記述して読み込む。それでApp.jsで処理された内容がindex.jsに埋め込まれて表示される。

表示するにはReactDom.render()という関数を使ってレンダリングする。

属性 this.props

index.jsからAppコンポーネント側に渡したい属性があるとする。例えばそれがnameという属性で値は"アトム"だったとしたら
<App name="アトム" />
のように書くことができる。この勝手に加えた属性nameは必ずthis.propsという名前のオブジェクトの中に入る。そういうキマリ。App.jsを以下のように修正すれば、{this.props.name}という形でその値を取り出せる。

//App.js
import React, { Component } from 'react';

 // Appコンポーネントクラス
class App extends Component {
  h1 = {
    size:"30px"
  }
  constructor(props){
    super(props);
  }
  render() {
    return (
      <div>
        <h1 style={this.h1}>Home</h1>
        <p>ハロー, {this.props.name}</p> //← ハロー, アトム と表示される.
      </div>
    );
  }
}

export default App

※ ちなみにthis.props.childrenにはすべての属性(およびDOM)が含まれている。


フォーム

AngularみたくFormsModuleとかいらなくてReact.Componentだけで済む。

以下のコードはinputテキストボックスに入力された文字列をeventキャッチしてmessageステート変数に代入し(doChange()関数)、ボタンクリックで実際の処理を行っている(doAction()関数)。【doAction()関数は、ここではstate変数のmessageをログ出力してから元のカラにしている】

関数についてはコンストラクターでバインドするというおまじないが必要。意味は知らなくていい。
イベント処理についてもe.preventDefault();というおまじないが必要。意味は知らなくていい。

//Form.jsx
import React, { Component } from 'react';

class Form extends Component {
    input = {
      fontSize:"16pt",
      color:"#006",
      padding:"1px",
      margin:"5px 0px"
    }
    btn = {
      fontSize:"14pt",
      color:"#006",
      padding:"2px 10px"
    }

    constructor(props){
      super(props); ← おまじない
      this.state = {
        message:''
      }
      this.doChange = this.doChange.bind(this); // ← おまじない
      this.doAction = this.doAction.bind(this); // ← おまじない
    }  

    doChange(e){
      this.setState({
        message: e.target.value
      });
    }

    doAction(){
      e.preventDefault(); ← おまじない
      console.log('メッセージ' + massage);     // ← 処理
      this.setState((state)=> ({              // ← 処理
        message: ''
      }));
    }

    render(){
      return (
        <div>
          <p>{this.props.message}</p>
          <form onSubmit={this.doAction}>
          <input type="text" size="40" onChange={this.doChange}
            style={this.input} value={this.state.message} required />
          <input type="submit" style={this.btn} value="Add"/>
          </form>
        </div>
      );
    }
  }
  export default App;

image.png

バリデーション

requiredやpatternなど。変わらない。

//App.js
import React, { Component } from 'react';

class App extends Component {
  input = '';

  msgStyle = {
    fontSize:"20pt",
    color:"#900",
    margin:"20px 0px",
    padding: "5px",
  }
  inputStyle = {
    fontSize:"12pt",
    padding:"5px"
  }
 input:invalid {
   border: 2px dashed red;
 }
 input:valid {
   border: 1px solid blue;
 }

  constructor(props){
    super(props); ← おまじない
    this.state = {
      message:'type your name:'
    };
    this.doChange = this.doChange.bind(this); // ← おまじない
    this.doSubmit = this.doSubmit.bind(this); // ← おまじない
  }

  doChange(event) {
    this.input = event.target.value;
  }

  doSubmit(event) {
    this.setState({
      message: 'Hello, ' + this.input + '!!'
    });
    event.preventDefault(); // ← おまじない
  }

  render(){
    return <div>
      <h1>React</h1>
      <h2>{this.state.message}</h2>
      <form onSubmit={this.doSubmit}>
        <label>
          <span style={this.inputStyle}></span>Message:
          <input type="text" style={this.inputStyle} 
                 onChange={this.doChange}
                 required          //← 必須
                 pattern="[A-Za-z _,.]+"  //← 正規表現パターン 
                 minlength="4"       //← 最小文字数
                 maxlength="20"       //← 最大文字数                 
                 />
        </label>
        <input type="submit" style={this.inputStyle} value="Click" />
      </form>
    </div>;
  }
}

image.png

コンテキスト

グローバル変数を設定するみたいな?
使うかのう?。とりあえずスルー。

Redux

Angularのngrxみたく色々なライブラリをインストールする必要なくてシンプル。

Reduxのインストール

$ npm install --save redux
$ npm install --save react-redux
$ npm install --save-dev redux-devtools

Redux Persistのインストール

永続化Reduxを使う場合は以下のコマンドも。

$ npm install --save redux-persist

使い方はここでは省略。書ききれん。Angularでngrx使うよりコードは少なく済みわかりやすい。また、もしかしたらReact Hooksという新しいモノがReact Reduxに取って代わる可能性あり。でもReact-Reduxでもとても良い。

ルーティング

インストール

$ npm install --save react-router-dom

ほかにもreact-router、react-router-reduxがあるがブラウザで動くページを書くにはreact-router-dom。react-routerはReact Nativeで使うようだ。

書き方

Angularのように原則app.module.tsファイルに書くとかapp.route.tsファイルを用意するとかない。App.jsに書くべし、なのか?

ほとんど同じでしょと思ったがAngularと勝手が違う。ひじょう~にめんどくさい。

Home、Aboutページ(コンポーネント)があるだけのシンプルな例:
image.png
image.png

// App.js
import React, { Component } from 'react'
import { BrowserRouter, Route, Link } from 'react-router-dom'

class App extends Component {
  constructor(props){
    super(props);
  }

  render(){
    return(
      <BrowserRouter>
        <nav>
          <Link to="/">Home</Link>
          <Link to="/About">About</Link> 
        </nav>
          <Route exact path='/' component={Home} />
          <Route path='/about' component={About} />
      </BrowserRouter>
    )
  }
}

class Home extends Component { // Homeコンポーネント
  render(){
    return(
      <div>
        <h2>Home</h2>
        <p>Homeページだボヨヨ~ン</p>
      </div>
    )
  }
}

class About extends Component { // Aboutコンポーネント
  render(){
    return(
      <div>
        <h2>About</h2>
        <p>Aboutページだオロローン</p>
      </div>
    )
  }
}

export default App;

ルーティングの設定は<BrowserRouter>というHTMLタグのようなもので挟んで、<Route exact path='/' component={Home} />という風に<Route>で設定。

exactは完全一致を意味してて、それをつけないと/aboutも前方一致してしまう、という。

パスへのリンクには <Link>を使う。toの指定先は"/About"とシンプルなテキストだけでなく例えば下記のようにオブジェクトもOK。

<Link to={{
  pathname: '/about', // パス文字列
  search: '?yourself=atom', // クエリパラメータ
  hash: '#the-hash', // URLハッシュ
  state: { flag: true } // 任意のデータ
}}>About</Link>

ほかにも<Switch>で囲んだりchildren={<SomeComponent />}指定、クエリパラメータの扱いなどいろいろある。Hookにも対応!Vue.jsエンジニアのためのReact Router v5入門に詳しく書いてある。

Next.js

ReactのSSR化ライブラリー。
AngularにおけるUniversalよりはるかに良いかもしれない。まだ試してないが。(Universalはこんなに大変→Angular8: パフォーマンス改善のためUniversalを導入しServer Side Rendering化する

また、Reactだけだとあくまでシングルページアプリケーションを考えてのモノであり、複数ページを持つWebサイトなりWeb appを想定するとNext.jsがほぼ必須?のようだ。なのでNext.jsまで学んで初めてAngularというのはオールマイティの全部入りだったのだなあ、と気づく。

インストール

新しいプロジェクトフォルダを作ってその中にpackage.jsonを前もって作成(後で編集してもいいけど)。

// package.json
{
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start",
    "export": "next export"
  }
}

あと、後ででもいいけどpagesというサブフォルダも作成しておく。

next_app
 |
 |---package.json
 |---pages

そんでインストールコマンド

$ npm install --save next react react-dom

起動コマンド

$ npm run dev

ブラウザからlocalhost:3000へアクセスで、以下のように404エラー。これでよい。
image.png


開発途中いろいろ

まずHello World

pagesフォルダの配下にindex.jsを作成して、そのファイルに下記のコード。pagesというフォルダの下にメインとなるコンポーネント(ページ)を置いていくキマリ。pagesフォルダはNext.jsのAPIとマッピングしているそうだ。

以下ではクラスの代わりに無名関数がコンポーネントになっている。

// pages/index.js
export default () =><div>
  <div>Hello World!</div>
</div>

また、
./pages/_app.js を作成することによって、 <App> を上書きできる。

Next.jsではReactのようにApp.jsはない(隠れた所にある)。以下はNext.jsでReduxを使うときに使うべき./pages/_app.jsのコード。

// pages/_app.js
import App, {Container} from 'next/app';
import React from 'react';
import withReduxStore from '../lib/redux-store';
import { Provider } from 'react-redux';

class _App extends App {
  render () {
    const {Component, pageProps, reduxStore} = this.props
    return (
      <Container>
        <Provider store={reduxStore}>
          <Component {...pageProps} />
        </Provider>
      </Container>
    )
  }
}

export default withReduxStore(_App)

./pages/_error.js を作成することにより、 404 または 500 エラーの上書きができる。

// pages/_error.js
import React from 'react'

class Error extends React.Component {
  static getInitialProps({ res, err }) {
    const statusCode = res ? res.statusCode : err ? err.statusCode : null
    return { statusCode }
  }

  render() {
    return (
      <p>
        {this.props.statusCode
          ? `An error ${this.props.statusCode} occurred on server`
          : 'An error occurred on client'}
      </p>
    )
  }
}

export default Error

Nextをインストール後のデフォルトバンドルはこういうことになっているようだ。つまりdocument.jsもまた./pages/_document.jsで上書きできる。まあやらんだろう。

$ cd .next/dist/bundles
$ tree
.
└── pages
    ├── _app.js
    ├── _app.js.map
    ├── _document.js
    ├── _document.js.map
    ├── _error.js
    ├── _error.js.map

コンフィグファイル

プロジェクトルートにnext.config.jsファイルを置く。angular.json的なものかなあ。デフォルトではこのファイルはないので使いたかったら新規で作成。いろいろな設定ができる。

例えば静的ファイルにエキスポートする設定コードは、

// next.config.js
module.exports = {
    exportPathMap: function (
      defaultPathMap,
      { dev, dir, outDir, distDir, buildId }   
    ) {   
      return {
        '/': { page: '/' }
      }
    }
}

そして以下のコマンドでエキスポート

$ npm run build
$ npm run export

これでhello worldだけの1ページであればoutフォルダができてそこにindex.htmlと404.htmlが生成されているはず。
参考:Static HTML export

スタティックアセット

CSSや画像ファイルなどはstaticというサブフォルダを作成してそこに置く。ちなみにCSSファイルは使えないのでスクリプトの形にしてそれを読み込む。以下のように。

// statc/Style.js
import css from 'styled-jsx/css';

export default <style>{`
  body {
    margin:10px;
    padding:5px;
    color:#669;
  }
  h1 {
    font-size:22pt;
    font-weight:bold;
    text-align:left;
    letter-spacing:0px;
    color:#77a;
    margin:-50px 0px 50px 0px;
  }
  p {
    margin:0px;
    color:#669;
    font-size:16pt;
}
`}</style>;

そして読み込む側のJSファイルでは、
import style from '../static/Style';と。

// pages/sample.js
import React, { Component } from 'react';
import style from '../static/Style';    // ← ココ

class Sample extends Component {

  render() {
    return (<div>
      {style}    // ← ココで使ってる
       <hr/>
       <h1>タイトル</h1>
    <p>メッセージ</p>
    </div>);
  }
}
export default Sample;

画像へのリンク

画像ファイルをstaticフォルダに置いて<img src="" />でOK。たとえば

<img src="/static/image.png"/>

ルーティング

pagesフォルダ配下のファイル同士で移動できる。つまりpageフォルダにhoge.jsやfuga.jsなどを置いておけばよいだけ。Angularのルーティング設定みたいなのはいらない。

// pages/index.js
import Link from 'next/link';   // ← Linkをインポートして

export default () =>(
    <hr/>
    <Link href="./hoge">    // Link href=でhogeやfugaなどを指定するだけ
       <button>
        go to HOGE &gt;&gt;
       </button>
    </Link>
);

<Link>タグで囲む。<Link>タグの中に<a>タグを置いてもいいけどそこにhref属性は置けない。

    <Link href="./hoge">      // ← href="./hoge"... <Link>タグの中に
       <a>              // ← a href="./hoge"... とはできない
        go to HOGE &gt;&gt;    // ← <a>タグならいちおうこの文字がリンカブルブルーになるが別に<p>タグでもいい
       </a>
    </Link>

※ 外部リンクだったら普通にアンカータグ<a href="https://qiita.com/">Google</a>で良いようだ。

  • href:pages/ ディレクトリ内のパスとクエリ文字列
  • as:ブラウザのURLバーに表示されるパス。
<Link href="/post?slug=something" as="/post/something">

※ 動的リンクについてはこちら参照:動的ルーティング

動的コンポーネント

JavaScriptモジュールやReact Componentsを動的にインポートしてそれらを使用できるようだ。AngularではJavaScriptをそのまま使えないのでこれは随分ラクだろう。
以下ではcomponentsフォルダの下に作った動的コンポーネントhello.jsファイルを読み込んでいる。

// pages/home.js
import dynamic from 'next/dynamic'

const DynamicComponent = dynamic(() => import('../components/hello'))

function Home() {
  return (
    <div>
      <DynamicComponent />
      <p>HOME PAGE is here!</p>
    </div>
  )
}

export default Home

ヘッダー情報を編集

HTMLのヘッダー情報を編集できるビルトインコンポーネントがある。<Head>タグで囲むだけ。AngularではTSファイルのコンストラクターでmeta.addTag()して編集できるが…TwitterカードなどのOGPボットが拾ってくれない問題(Universal化してもsizeが大きいせいか拾ってくれない)を解決してくれるかもしれない。

import Head from 'next/head'

function IndexPage() {
  return (
    <div>
      <Head>
        <title>My page title</title>
        <meta name="viewport" content="initial-scale=1.0, width=device-width" />
      </Head>
      <p>Hello world!</p>
    </div>
  )
}

export default IndexPage

Redux on Next.js

複数ページ間で共通データをまたいで扱うことができるReduxを、Next.jsでも使える

インストール

$ npm install --save redux
$ npm install --save react-redux
$ npm install --save redux-thunk

使い方

詳しくはReact.js & Next.js超入門に載っている。とても書ききれん。


補足

Angularでの経験が役に立つのは、現在ReactでもTypescriptを使うケースが増えていること。TypeScriptでのサンプルコードの少なさに悶え、苦しんだ経験が報われるだろう。

TypeScriptでのReactプロジェクト作成は物凄く簡単になったらしく、以下のコマンドだけ。

$ npx create-react-app <アプリケーション名> --typescript



参照:

React.js & Next.js超入門 👈 ReduxとNext.jsの説明が非常にわかりやすくオススメ(超入門とあるがそれなりのレベルの内容)

JSフレームワーク事情2020年始め
Use React Hooks with Storage as Global State Management
react-router@v4を使ってみよう:シンプルなtutorial
【超簡単】Reactのルーティング設定方法
Hookにも対応!Vue.jsエンジニアのためのReact Router v5入門
Next.js
Next.jsの機能
なぜReact+TypeScriptでコンポーネント作成が早くなるのか

4
4
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
4
4