Reactを学んでみた

はじめに

Reactは以前Reactチュートリアルをした後一切学んでいませんでしたが、Progateの有料会員になったので久しぶりに少し学んでみました。

5/29追記:教本などを用いて内容を追記しました。

Reactチュートリアルをやってみた

✅ゴール
・Reactの理解を深める

✅環境
Windows

✅参考教材
💻React(progate)

💻Reactをすぐ試したいときにCDN(インストールなし)で読み込む方法

HTMLファイルでサクッと試したかったのでこちらを試しました。
※scriptのtypeはtext/babelを指定しないとJSXでの書き方ができない。

⇒結局、Reactの描画の仕組みまで理解したくなったのでNode.jsを利用しました。

💻Reactチュートリアル

📚React.js&Next.js超入門

💻Vue.jsエンジニアのためのReact入門
💻Hookにも対応!Vue.jsエンジニアのためのReact Router v5入門

フックについて触れられていて、関数型コンポーネントの作り方を薦めてくれている。
<React.Fragment>※<>で省略可。も初知り。<div>の代わりにできる。
v-modelがないことでフォーム関連は逆に見落としがちなJSの勉強にもなりますね。
(※Reactは諸々、Vue.jsのように便利な独自構文を省いて、ちゃんと関数で1から作るイメージ)

📚りあクト!!

無料版のP36まで。

アロー関数で記述することを進めてる。
ES6:デフォルト引数、スプレッド演算子、Promise
ES8:オブジェクトのスプレッド構文

概要

✅Reactとは

ユーザインターフェイスを構築するための、宣言型で効率的で柔軟な JavaScript ライブラリ。
複雑な UI を、「コンポーネント」と呼ばれる小さく独立した部品から組み立てることが可能。

✅コンポーネント

Reactで画面に表示される部品のこと。
※作り方は2パターンあるが、フックの導入で今後は関数型が多くなると言われている。

関数型コンポーネントの作り方

import React from 'react';

function Sample() {
  return (
    <h1>sample</h1>
  );
}

export default Sample;

クラス型コンポーネントの作り方

import React from 'react';

class Sample extends React.Component {
  render() {
    return (
      <h1>sample</h1>
    );
  }
}

export default Sample;

Reactコンポーネントのクラスで呼び出されているrenderメソッドは、JSXなどで定義されたReact要素を返す処理を行う。
returnで表示する要素を返す。

※レンダリング:何らかの抽象的なデータ集合を元に、一定の処理や演算を行って画像や映像、音声などを生成すること。

HTMLファイルでHello React

参考:Reactをすぐ試したいときにCDN(インストールなし)で読み込む方法

以下は教本参考にBabelのCDNをつかわないパターン。※JSX記法ができないため。

エレメントの作成

 React.createElement( タグ名 , 属性 , 中に組み込まれるもの );

レンダリングした表示の作成

※ReactDOMというオブジェクトの「render」を使ってレンダリング(オブジェクトを具体的に目に見える形に変換する作業)する。

ReactDOM.render( エレメント , DOM );

Node.jsを利用してHello Reactしてみる

✅環境構築

①インストール

npx create-react-app react-app // npxの場合
npm init react-app react-app // npmの場合
npx create-react-app react-app --typescript // TypeScriptを利用する場合。ファイルも.tsx中心に作成される。

知らないのは損!npmに同梱されているnpxがすごい便利なコマンドだった

②実行

npm start
yarn start

※yarnはFacebookが開発しているパッケージ管理ツール。ReactもFacebook開発なので・・・。

③ビルド

デプロイ用のbuildディレクトリが生成される。

npm run build

✅フォルダ階層

react-app
└- public
   └- index.html
└- src
   └- App.js
   └- index.js
└- node_module 
~~

※以下はReactを表示させるための超ミニマムのテンプレートです。

・index.html

<!DOCTYPE html>
<html>
  <head>
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

・App.js

import React from 'react';
class App extends React.Component {
  render() {
    return (
      <h1>Hello React</h1>
    );
  }
}
export default App;

①React(node_module内)をimport
②React.Componentを継承するクラスを定義
③JSXを戻り値とするrenderメソッドを定義
④クラスをexport

・index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

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

①Reactオブジェクトをimport
②ReactDOMオブジェクトをimport
③Appコンポーネントをimport

ReactDOM.render(<App />, …」で、App.jsのJSXが、HTMLに変換
…, document.getElementById(‘id名’));」で、変換されたHTMLがindex.htmlの指定したid名の要素の中に挿入。

基本

✅JSX記法

HTMLのタグを直接JavaScriptのスクリプトに記述できる仕組み。

JSXを記述するのはrenderメソッドのreturn内のみ。ここをブラウザに表示させる。逆にreturnの外はJSを普段通り書ける。

注意点
①複数の要素がある場合は<div>タグで囲んで1つの要素にまとめる。
②JSXにJSを埋め込みたい際は中括弧{}を用いる。
③imgタグでは閉じタグが必要になる。<img src=’画像のURL’ />※inputやtextareaも書き方がHTMLと異なる。
④JSX記載のreturn内は{/* */}、return外は//でコメントできる。

render() {
  const name = "Sato";
  return (
    <div>
      <h1>Hello React</h1>
      <p>{name}のページです。</p>
    </div>
  );
}

・CSSのクラスを付与したい場合はclassName=’クラス名’とする。

<h1 className='title'>~~~

・inputやtextareaのタグの書き方がHTMLと異なる

<input /> // HTMLだと<input>
<textarea /> // HTMLだと<textarea></textarea>

✅表示の切り替え

・イベント:何かが起きたときに、処理を実行するように指定できる

イベント名={() => { 処理 }}と書く。return内での記載なので、アロー関数(JavaScript)は{}で囲む。

return (
  <div>
    <h1>Hello React</h1>
    <p>{name}のページです。</p>
    <button onClick={()=> {console.log('食べるの!??')}}>食べる</button>
    <button onClick={()=> {console.log('そうしよう')}}>食べない</button>
  </div>
);

※イベントはonイベント名でハンドルする。

・state

コンポーネントの状態を表す値を保管するためのもの。this.stateと記載)。値の変更に応じて自動的に表示も変更される。
※stateはpropsに似ているが、コンポーネントによって完全に管理されるプライベートなもの。

stateは、constructorの中で、オブジェクトとして定義。

this.stateはオブジェクトなので、this.state.プロパティ名とすることで、指定したstateのプロパティ名に対応する値を取得可能。

setState:コンポーネント内で呼び出すと、その内部の子コンポーネントも自動的に更新される。

変更はthis.setState({プロパティ名: 変更する値})とする。
this.state.プロパティ名 = 値で直接代入して変更してはいけない。setStateを利用すること。

class App extends React.Component {
  constructor(props){
    super(props);
    this.state = {name: 'ご飯を食べるか悩んでます。'};
  }
  render() {
    return (
      <div>
        <h1>Hello React</h1>
        <p>{this.state.name}</p>
        <button onClick={()=> {this.setState({name:'え、食べるの??'})}}>食べる</button>
        <button onClick={()=> {this.setState({name:'うん。今日は我慢しておこう。'})}}>食べない</button>
      </div>
    );
  }
}

※「constructor(props)」や「super(props);」といった処理はいつも同じ記述をする定型文。

✅メソッド

メソッド名() { 処理 }でメソッドは定義できる。

また、onClick={() => {this.メソッド名()}}とすることで、クリックされたときに、App.js内の指定したメソッドを実行することができる。(※{}はJSXのため記載している中括弧)

※処理的には上記と同じのものをメソッドで記載した場合。

 

import React from 'react';
class App extends React.Component {
  constructor(props){
    super(props);
    this.state = {name: 'ご飯を食べるか悩んでます。'};
  }
  handleClick(name) {
    this.setState({name:name});
  }
  render() {
    return (
      <div>
        <h1>Hello React</h1>
        <p>{this.state.name}</p>
        <button onClick={()=> {this.handleClick('え??食べるの???')}}>食べる</button>
        <button onClick={()=> {this.handleClick('うん。今日は我慢しておこう◎')}}>食べる</button>
      </div>
    );
  }
}
export default App;

※以下はProgateのReactⅠコースの内容です。

App.js

import React from 'react';
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: 0};
  }
  handleClick() {
    this.setState({count: this.state.count + 1});
  }
  render() {
    return (
      <div>
        <h1>
          {this.state.count}
        </h1>
        <button onClick={() => {this.handleClick()}}>+</button>
      </div>
    );
  }
}
export default App;

✅コンポーネント

components/Language.js

import React from 'react';
class Language extends React.Component{
  render(){
    return (
      <p>Hello Language Component</p>
    );
  }
}
export default Language;

①Reactをインポート。
②React.Componentを継承するクラスを作成。
※クラス名がコンポーネントの名前となる。
③コンポーネント名でエクスポート。

App.js

import React from 'react';
import Language from './components/Language';
class App extends React.Component {
  render(){
    return(
      <div>
        <h1>言語一覧</h1>
        <Language />
      </div>
    );
  }
}
export default App;

①コンポーネントをインポート
②JSX内に<コンポーネント名 />と記載

✅props

親コンポーネントから子コンポーネントにデータを渡すことによって表示を変える仕組み。
props名=値」という形で、コンポーネントを呼び出す箇所で渡し、渡されたpropsは、this.propsで取得する。
また、this.propsは{ props名: 値}というオブジェクトになるので、表示はthis.props.props名でできる。

・mapメソッドの活用

for文などの繰り返し構文はJSXで使えないので、mapメソッドを利用する。

配列.map ( ( value )  => 新しい値 )

Language.js

import React from 'react';
class Language extends React.Component{
  render(){
    return (
      <div>
        <p>{this.props.name}</p>
        <p>{this.props.level}</p>
      </div>
    );
  }
}
export default Language;

App.js

import React from 'react';
import Language from './components/Language';
class App extends React.Component {
  render(){
    const languageList = [
      {name:'HTML&CSS',level:'⭐'},
      {name:'JavaScript',level:'⭐'},
      {name:'PHP',level:'⭐⭐'},
    ];
    return(
      <div>
        <h1>言語一覧</h1>
        {languageList.map((languageItem) => {
          return (
            <Language 
              name={languageItem.name}
              level={languageItem.level}
            />
          )
        })}
      </div>
    );
  }
}
export default App;

※アロー関数(その場限りで使う関数)を用いることで、その場で実行される関数を作れる。

✅子コンポーネントで表示の切り替え

※ProgateⅢの内容。モーダルにはしませんでした。

・App.js

import React from 'react';
import Header from './components/Header';
import Main from './components/Main';
import {Footer} from './components/Footer';
class App extends React.Component {
  render(){
    return(
      <div>
        <Header/>
        <Main/>
        <Footer/>
      </div>
    );
  }
}
export default App;

・components/Header.js

import React from "react";
class Header extends React.Component {
  render() {
    let text = "学習を始めよう!";
    return(
    <h1>{text}</h1>
    );
  }
}
export default Header;

・Main.js

import React from 'react';
import Lesson from './Lesson';
class Main extends React.Component {
  render(){
    const lessonList = [
      {name:'HTML&CSS',level:'⭐',comment:'初心者向けです'},
      {name:'JavaScript',level:'⭐',comment:'上級者向けです'},
      {name:'PHP',level:'⭐⭐',comment:'中級者向けです'},
    ];
    return(
      <div className="main">
        {lessonList.map((lessonItem) => {
          return(
            <Lesson
              name={lessonItem.name}
              level={lessonItem.level}
              comment={lessonItem.comment}
            />
          )
        })}
      </div>
    );
  }
}
export default Main;

・Lesson.js

import React from "react";
class Lesson extends React.Component {
  constructor(props) {
    super(props);
    this.state={isModalOpen: false}
  };
  handleClickLesson(){
    this.setState({isModalOpen: true});
  }
  handleClickFinish(){
    this.setState({isModalOpen: false});
  }
  render() {
    let modal;
    if(this.state.isModalOpen){
      modal = (
        <div className="modal">
          <p>testmodaltrue</p>
        </div>
      )
    }else{
      modal = (
        <div className="modal">
          <p>testmodalfalse</p>
        </div>
      )
    }
    return(
      <div className="lesson-card">
        <div className="lesson-item">
          <h3>Lesson</h3>
          <p>{this.props.name}</p>
          <p>{this.props.level}</p>
          <p>{modal}</p>
          <button
            onClick={() => {this.handleClickLesson()}}
          >let's try</button>
          <button
            onClick={() => {this.handleClickFinish()}}
          >Finish</button>
        </div>
      </div>
    );
  }
}
export default Lesson;

・Footer.js

import React from "react";
class Footer extends React.Component {
  render(){
    let text = "お疲れ様でした!";
    return(
      <h1>{text}</h1>
    );
  }
}
export {Footer};

✅Stateで入力値の管理

※ProgateⅣの内容

①stateと入力値の紐づけ。inputタグのvalue属性にstateの値を指定する。

②フォームの入力や削除の処理実行。onChangeイベントを用いる。

⇒onChangeイベントの関数にeventという引数を追加し、event.target.valueとすると値を取得できる。

③stateを更新する。

・ContactForm.js

※複数のstateを更新する場合はコンマで区切り一度のsetStateでまとめて変更する。

※事前にApp.jsにContactForm.jsをインポートしています。

✅コンテキスト

いくつものコンポーネントで同じ値を利用するためのモノ。変更する際はProviderを用いる。
⇒Reduxで代替できるので今回は割愛。

✅React DevTools

React DevToolsを使うとReactコンポーネントのpropsとstateを確認可能。

Redux

✅Reduxとは

フロントエンドにおける状態管理に特化したライブラリ。
reduxを使うことで状態管理に関わる部分をコンポーネントのツリーから分離することが可能。
値の保管はすべて「Store」ので行う。

Fluxという概念に基づいて設計されており、Action→Dispatcher→Store→Viewで一方向に状態が流れる特徴を持っている。
※更新部分を細分化するとaction type → action → reducer

✅仕組み

store、state、reducer、providerを用いる。

・store:データを保管し管理するもの。ここに保管される値はstateと呼ばれる。
⇒redux の状態そのものは store と呼ばれるオブジェクト。その中に各 reducer に対応した state がネストして入っている。

reducer:storeに保管されるstateを変更するための仕組み。レデューサーで呼び出した処理は常に新しいステートをリターンする。
⇒非同期の処理を行うところ。middeware の関数はその内部で他の dispatch を行うことができる。

・provider:storeを他のコンポーネントに受け渡すための仕組み。ラップしてコンポーネントに渡す。
※connectを使うことで初めて、データの取得や更新をreduxに対して要求(dispatch)できる。

・connect:各コンポーネント単位でどのstateを参照してどのdispatchを実行するのか定義するための関数。※高階関数。

・action type:reducerやmiddlewareがどの処理を行うかを判断(条件分岐)するために使われる。

・action:view 側からactionをdispatch(=reduxに通知)することでreduxのフローが実行される仕組み。必ずtypeという値を用意する。
※「どんな処理をするのか(=action type)」と「それに必要な引数(=payload)」が入ったオブジェクトを返す関数。

・middleware:非同期の処理を行う。redux事態に非同期処理機能がないため、別のライブラリを使うのが一般的。

・dispatch:レデューサーを呼び出し、値を操作するための機能。

📝Providerとconnectでインターフェースを分けることで、stateのスコープを制限し保守性を担保している。

✅使い方

①ReactプロジェクトにReduxとReact Reduxをインストールする。
②index.jsでステート⇒レデューサー⇒ストア⇒プロデューサーの順に記述。
※これでreact-reduxライブラリのProviderを用いてstoreをreactに渡す。
③App.js(コンポーネント)でconnectし、dispatchすることでreduxにアクセス。

✅使ってみる

Redux本体のインストール

npm install --save redux

React Reduxのインストール

※ReactとReduxをシームレスに融合して使うためのパッケージ。

npm install --save react-redux

Redux DevToolsのインストール

npm install --save-dev redux-devtools

👇index.jsでストアを作成する。

stateの作成

※変数で定義すればOK

let state_value = {
  counter: 0,
  message: "COUNTER",
};

reducerの作成

関数として作成する。アクションはレデューサーを実行する際に渡される値で必ずtypeというプロパティが用意される。

function 関数名 ( state = ステートの名前, アクション) {~~処理~~}

※どのtypeの時にどんな値を返すかがポイント。

function counter(state = state_value, action) {
  switch (action.type) {
    // typeがINCREMENTの場合
    case "INCREMENT":
      return {
        counter: state.counter + 1,
        message: "INCREMENT",
      };
    // typeがDECREMENTの場合
    case "DECREMENT":
      return {
        counter: state.counter - 1,
        message: "DECREMENT",
      };
    default:
      return state;
  }
}

storeの作成

引数にreducerを指定して呼び出し。ストアを変数に収めて、JSXでプロバイダーに渡して使用する。

let store = createStore( counter※reducerとして作った関数名 );

providerの作成

storeという属性にストアを指定することで、<provider>内に記述してあるコンポーネント類にストアの内容が渡される。

<Provider store={store}>
  <App />
</Provider>,

👇コンポーネントからストアを利用する

コネクト

コンポーネントにストアを接続するための関数。以下で実行できる。

変数 = connect ( 設定する内容 )( コンポーネント );

最初の()に、接続する際にステートなどに関する設定のための値を用意。二つ目の()にコネクトするコンポーネントを用意。
これでコンポーネントにストアを組み込んだ状態のコンポーネントが作られる。戻り値は引数に指定したコンポーネントの変数に再設定する。

また、connectで渡されるステートはthis.propsで組み込まれるようになっている。

connect()()は2つの関数の組み合わせでできている。

let wrapWithConnect = connect();
App = wrapWithConnect(App);

ディスパッチとアクション

ディスパッチはReduxにアクションを送る働きをする。
アクションは実行する内容に関する情報をまとめたオブジェクト。dispatchの引数に用意し、必ずtypeという値を用意する。

これによって、レデューサーが実行され、新しいステートがストアに設定される。

this.props.dispatch({ type:"INCREMENT" })
流れのまとめ

ステートを用意⇒レデューサーを用意⇒ストアを用意
ディスパッチ呼び出し⇒レデューサー呼び出し⇒アクションのtypeで処理を分岐

Reduxを深掘り(永続化)

※5/31追記

参考:📚React.js&Next.js超入門 P210~簡易メモ

📝前回の使い方のおさらい
・index.jsでステート⇒レデューサー⇒ストア⇒プロデューサーの順に記述。
※これでreact-reduxライブラリのProviderを用いてstoreをreactに渡す。
・App.js(コンポーネント)でconnectし、dispatchすることでreduxにアクセス。

✅基本構成

index.js

「memo」フォルダのStore.jsからストアをMemoStoreという名前でインポート
⇒プロバイダーでMemoStoreを指定して、Appコンポーネントへ渡す。

App.js

memoディレクトリで作成したコンポーネント4つをインポートし組み込み。処理は記載なし。

memo/Store.js

データを扱うストア関係を記載。※かなり大ボリューム。

※メモ(ステート)の構造※

const initData = {
  data:[{message:'sample data',created:new Date()}],
  message:'please type message:',
  mode:'default',
  fdata:[]
};

data メモのデータ
message 表示するメッセージ
mode どういう操作をしたかを表す値
fdata 検索したメモをまとめておく

※レデューサー※

export function memoReducer(state = initData, action) {
  switch (action.type) {
    case 'ADD':
      return addReduce(state, action);
    case 'DELETE':
      return deleteReduce(state, action);
    case 'FIND':
      return findReduce(state, action);
    default:
      return state;
  }
}

処理をtypeごとに分岐して新しいステートの値をリターンしている。具体的な処理(リターン部分)は関数で別途用意。
用意した処理は以下3つ。
addReduce,deleteReduce,findReduce

※レデューサーで呼び出されるメソッド例※

追加

function addReduce(state, action){
  let data = {
    message:action.message,
    created:new Date()
  };
  let newdata = state.data.slice();
  newdata.unshift(data);
  return {
    data:newdata,
    message:'Added!',
    mode:'default',
    fdata:[]
  };
}

※アクションクリエーター※

ディスパッチの際の引数として渡す「アクション」を作成する関数。アクションのタイプを示すtypeや必要な値をここにまとめておく。

追加の一例

export function addMemo(text) {
  return {
    type: 'ADD',
    message:text
  }
}

※ストアを作成してエクスポート

export default createStore(memoReducer);

memo/Memo.js

メモアプリの本体部分。

✅永続化

React Persistでローカルストレージを扱う

インストール

npm i --save redux-persist

🙄予想以上にReduxの理解、難しいので一旦ステイします。。。

躓いた点

✅CSSの適用

参考:React.jsのスタイル設定の基礎とStyled Componentsの使い方

意外と厄介でびっくり。一旦インライン記述で誤魔化します。※progateではindex.htmlで読み込めばOKと記載されている。

✅JSとJSXの混在

参考:Reactアプリでの、jsファイルの拡張子について

TSだと勝手に.tsxで生成されるが、通常だと.jsxではなく.jsで生成されるので、困惑していたが、どうやら書ける内容は一緒らしい??

あくまで、JSX記法を使っているよ!という目印っぽい。

✅serviceWorkerについて

serviceWorker.jsって何?unregister()って何をしているの?

PWAの一機能であるcacheの有効/無効を制御するコードとのこと。

✅<React.StrictMode>について

参考:React 16.3.0で追加されたStrictModeコンポーネントについて

JSでの’use strict’と同様の機能らしい。

✅ESLintの無効

参考:ESLintで特定のソースコードのチェックを無効にする

以下でできる。
// eslint-disable-line

おわりに

Progate、Reduxについては触れられていないものの満足感結構高くてびっくり。

また、JavaScriptを改めてパーフェクトJavaScriptを通じて学習したことで、だいぶコードの理解ができるようになって、面白味を感じるようになった。

これはVue.jsより楽しいかもしれない。。。

Reactはクライアントサイドで動かすための記述が多いのでJSのイベントの部分をしっかり勉強していきたいと思った。

5/29追記:

Reduxが複雑。。。これはVuexと正直同じか。徐々にFluxの思想に慣れていきたい。

React.js&Next.js超入門で作ったカウンターアプリやら何回もコードを見返したい。

5/31追記:

React.js&Next.js超入門にある簡易メモ(P210~)でReduxと永続化について学習しようと思いましたが、想像以上に理解しづらかったので一旦ステイしました。Hooks含めた教材で改めて理解を深めていこうと思います。Reduxに関してはVuexより複雑で苦手かもしれない。

※💻React/ReduxでGoogleカレンダー風カレンダーアプリケーションを作ろう

☝はなかなかに難易度高め。。。

💻たぶんこれが一番分かりやすいと思います React + Redux のフロー図解

これ読みたい。

💻ReactとVueのどちらを選ぶか

Vueも学んだうえでReactの理解ができてきた今、上記の記事に頷ける部分が増えた。

Reactは開発者であることを期待し、Vueはユーザーであることを想定している。
中長期的に開発者としてスキルや設計などのレベルアップを図りたいのであればReactを学ぼう。

💻完全に独断と偏見だけどReact vs Vue してみた

TypeScriptとの相性・親和性においてはReactの方が優っている。これは個人的にも間違いないと思う。

Vue3公開まではReact使ってTS含めたJSの理解を深める方がシンプルに良さそう。Vue.jsでTS使おうとすると逆に初学者は理解しづらい。

💻ReactとVueの比較、全く同じアプリを作成してみて分かった相違点 2019年Edition

コメントを残す