Skip to content

Instantly share code, notes, and snippets.

@teslasand0987
Last active December 8, 2022 06:39
Show Gist options
  • Save teslasand0987/42bd57db296a92881ed242ba16976249 to your computer and use it in GitHub Desktop.
Save teslasand0987/42bd57db296a92881ed242ba16976249 to your computer and use it in GitHub Desktop.
JavaScript学習メモ_React編

React

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

コンポーネント

Reactはコンポーネントという単位で実装します。 Webサイトやアプリは、ボタンやタブなどのコンポーネントの集まりと考えることができます。 コンポーネントとは見た目と機能を組み合わせたものだと考えることができます。

Reactの世界では、Reactコンポーネントの記述方法が2つあります。 それはそれぞれ、関数コンポーネントとクラスコンポーネントと呼ばれます。

JSX

JSXはJavaScript内でHTMLのような表現をするJavascriptの拡張構文である。

const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);

変数や関数の埋め込み

{}をjsx内で用いることで変数や関数(returnあり)をjavascriptで埋め込むことが可能。

const fruit = 'りんご';
const price = 100;
const quantity = 5;
const element = <h1>{fruit}{quantity}個買うと{price * quantity}円です</h1>;

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

コメントアウト

{}内でjavascriptにおけるコメントアウトを記述することで実現可能。 最後の例は改行しないと}もコメントアウトされてしまい、エラーとなるので改行はお忘れなく。

const user = 'admin';
const element = (
<div>
  {/* コメント */}
  
  {//コメント
    user
  }
</div> );

タグ

タグが空の場合、/>で閉じる

<div>
  こんにちは
  <br />
  <img src="example.gif" />
</div>

フラグメント

JSXは親要素を1つしか持つことができません。 そのため親要素を1つにして子要素を2つにする方法がありますが、 <div><div>で子要素を囲うと不要なdivがDOMに追加されるため、フラグメント(React.Fragment)を用います。

const shrotFragment = (
                <> {//内部のReact.Fragmentは省略
                  }
                    <div> hoge </div>
                    <div> fuga </div>
                </>);{//内部のReact.Fragmentは省略
                    }

関数コンポーネント

関数コンポーネントはJSXを返すプレーンなJavaScript関数である。

import React from "react";
 
const FunctionalComponent = () => {
 return <h1>Hello, world</h1>;
};

クラスコンポーネント

クラスコンポーネントはReact.Componentを拡張するJavaScriptクラスです。 これにはrenderメソッドを含みます。

import React from "react";

class ClassComponent extends React.Component {
 render() {
   return <h1>Hello, world</h1>;
 }
}

コンポーネントへpropsの受け渡し

関数コンポーネントでは引数としてpropsを渡しています。ここでは非構造化を使用しています。

const FunctionalComponent = (props) => {
 return <h1>Hello, {props.name}</h1>;
};

クラスコンポーネントではthisを使用してpropsを参照する必要があります。 この場合も、クラスベースのコンポーネントを使用しながら、非構造化を使用してprops内でnameを取得できます。

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

コンポーネントのstateの処理

Reactプロジェクトにおいてはstate変数の処理が不可欠です。 例として、0から始まるシンプルなカウンターを作成し、ボタンを1回クリックすると数が1ずつ増えるようにします。 useState()は、関数コンポーネントでstateを管理(stateの保持と更新)するためのReactフックであり、最も利用されるフックです。stateとはコンポーネントが内部で保持する「状態」のことで、画面上に表示されるデータ等、アプリケーションが保持している状態を指しています。

関数コンポーネントでのstate処理

const FunctionalComponent = () => {
 const [count, setCount] = React.useState(0);

 return (
   <div>
     <p>count: {count}</p>
     <button onClick={() => setCount(count + 1)}>Click</button>
   </div>
 );
};

関数コンポーネントでstate変数を使用するには、初期状態の引数を取るuseStateフックを使用する必要があります。 この場合は0クリックから開始するため、カウントの初期状態は0になります。

クラスコンポーネントでのstate処理

class ClassComponent extends React.Component {
 constructor(props) {
   super(props);
   this.state = {
     count: 0
   };
 }

 render() {
   return (
     <div>
       <p>count: {this.state.count} times</p>
       <button onClick={() => this.setState({ count: this.state.count + 1 })}>
         Click
       </button>
     </div>
   );
 }

クラスコンポーネントでのstateを処理する場合、概念は同じですが方法が少し異なります。 constructorの重要性を理解する必要があります。

基本的に、constructorを実装してsuper(props)の呼び出しを実行しないと、使用したいすべてのstate変数を定義できません。まずコンストラクタを定義しましょう。

ライフサイクル

ライフサイクルはレンダリングのタイミングにおいて重要な役割を果たします。 クラスコンポーネントはライフサイクルを持ちます。 ライフサイクルとは、コンポーネントが生まれて、成長し、死ぬまでの循環のことです。

コンポーネントが誕生した後は、度重なるrenderがユーザーの操作により発生します。 具体的にはrenderは、ユーザーの操作などによりstateが変更されるたびに呼び出されます。 これがライフサイクルの成長の過程であり、renderされる毎にcomponentDitUpdateという関数が実行されます。 コンポーネントが死ぬときつまりはアンマウントされる直前にcomponentWillUnmountという関数が実行されます。

ライフサイクルの種類

クラスコンポーネントにおけるライフサイクルに関して説明します。

Mounting

Mountingとは、コンポーネントが生まれるときの期間のことです。 マウントが行われる直前に実行されるcomponentWillMountとマウントが実装された直後に実行されるcomponentDidMountです。 componentWillMountは非推奨なので使わないほうがよいです。

Updating

Updatingとは、コンポーネントが変更される期間のことです。 つまり、コンポーネントが変更されるとはこのstateが変更されることを指します。 Reactにおける重要な点の一つは、このstateが変更されたときにrenderが走るということです。 つまり、stateが変更されるたびに画面の一部分を再描画していきます。

メソッド 起動条件
componentWillReactiveProps コンポーネントが新しいpropsを受け取る前
shouldComponentUpdate 新しいpropsやstateを受け取った時
componentWillUpdate 新しいpropsやstateを受け取った後、レンダリングする直前
componentDidUpdate 更新が行われた直後

Unmouting

コンポーネントが破棄される期間のことをUnMountingの期間といいます。 componentWillUnmountというライフサイクルメソッドが、コンポーネントが破棄される直前に呼び出されます。

componentDidmountの実装

componentDidmountは、次の用途で用いられます。

  • Ajaxを使ったデータフェッチを行う(初回)
  • DOMに対する処理を行う(初回)
  • タイマーをリセットする
  • イベントリスナのセット

イベントリスナのセットを行っていきましょう。 LikeButtonコンポーネントを作成して、クリックするといいね数が増えていくという機能を実装します。

import React from 'react';
import LikeButton from './LikeButton';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    }
  }

  componentDidMount() {
    document.getElementById("counter").addEventListener('click', () => this.setState({count: this.state.count + 1}))
  }
  render() {
    return (
      <div>
        <LikeButton count={this.state.count} />
      </div >
    )
  }
}
export default App
import React from 'react';

const LikeButton = (props) => {
    return (
        <div>
            <button id="counter">いいね数: {props.count}</button>
        </div>
    )
}
export default LikeButton

また今回の例では componentDidMount()は用いずにApp.jsxからpropsとしてcountを更新する関数を渡し、それを子供のコンポーネントのボタンタグのonClickに指定することでも実装できます。

componentDidUpdateの実装

このライフサイクルメソッドは、stateがある閾値を超えたときに別の動作をするようにしたいときなどに実装すると便利です。 componentDidUpdateはstateが更新され、それによりrenderが走った後に呼び出されます。

今回は、いいね数が5という閾値を超えた際に、いいね数を0にするという機能を実装していきます。

import React from 'react';
import LikeButton from './LikeButton';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    }
  }

  componentDidMount() {
    document.getElementById("counter").addEventListener('click', () => this.setState({count: this.state.count + 1}))
  }

  componentDidUpdate(){
    if (this.state.count >= 5) {
      this.setState({
        count: 0
      })
    }
  }
  render() {
    return (
      <div>
        <LikeButton count={this.state.count} />
      </div >
    )
  }
}
export default App
import React from 'react';

const LikeButton = (props) => {
    return (
        <div>
            <button id="counter">いいね数: {props.count}</button>
        </div>
    )
}
export default LikeButton

componentWillUnmountの実装

このメソッドは主に次の機能を実装したいときに使います。

  • タイマーを解除する
  • イベントリスナーを解除する
  • 非同期処理を中止する

今回は、イベントリスナーを解除してメモリを開放する機能を実装します。

import React from 'react';
import LikeButton from './LikeButton';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    }
  }

  componentDidMount() {
    document.getElementById("counter").addEventListener('click', () => this.setState({count: this.state.count + 1}))
  }

  componentDidUpdate(){
    if (this.state.count >= 5) {
      this.setState({
        count: 0
      })
    }
  }

  componentWillUnmount() {
    document.getElementById('counter').removeEventListener('click', () => this.setState({count: this.state.count + 1}))
  }
  render() {
    return (
      <div>
        <LikeButton count={this.state.count} />
      </div >
    )
  }
}
export default App

これでコンポーネントがアンマウントされたときにイベントリスナーを削除して、メモリを開放することができるようになりました。

Hook

Hookとは、端的に言えばクラスコンポーネントの機能を関数コンポーネントでも使えるようにするためのものです。

クラスコンポーネントの問題

クラスコンポーネントには以下で示すような問題がありました。

  • 処理が散らばりやすい
  • thisを使う必要がある
  • 複数のライフサイクルメソッドに処理がまたがるのが嫌だ
  • クラスコンポーネントの構文が複雑
  • stateの扱い方が複雑

React-hooksの実装

Hooksでstateを用いるときは、useStateというメソッドを使います。

今回は、ScissorsコンポーネントというFunctionalコンポーネントを作成して、それを親コンポーネントであるAppコンポーネントから呼び出します。 Scissorsコンポーネントにcuttingとうstateを持たせて、ボタンを押すことで変化させるという機能を実装します。

App.jsx

import React from 'react';
import Scissors from './Scissors'

const App = () => {

  return(
    <div>
      <Scissors />
    </div>
  )
}
export default App

scissor.jsx

import React, { useState } from 'react';

const Scissors = () => {
    const [cutting, changeCutting] = useState("NoCutting");
    return(
        <div>
            <h1>{cutting}</h1>
            <button onClick={() => changeCutting("YesCutting!")}>change </button>
        </div>
    )
}

export default Scissors

アプリの挙動
App_move1

Hooksによるライフサイクルの管理

Hooksではライフサイクルメソッドの代替としてuseEffectメソッドがあります。 クラスコンポーネントにおける以下3つの代替です。

  • coponentDidMount()
  • componentDidUpdate()
  • componentWillUnmount()

coponentDidMount()に関して

componentDidMountはマウントが行われた直後に実行されるメソッドです。 Ajaxを使ったデータフェッチ(初回)やイベントリスナのセットなどを行います。

componentDidUpdate()に関して

componentDidUpdateはstateの更新が行われた直後に行われるメソッドです。 stateがある閾値を超えたときに別の動作をするようにしたいときなどに使うと便利です。

componentWillUnmount()に関して

componentWillUnmountは、コンポーネントが破棄される直前に呼び出されます。 イベントリスナーを解除してメモリを開放するときなどに利用されます。

useEffectメソッドの実装

第二引数を与える
空配列が渡されるとき
useEffect(() => {
  console.log('mounting')
}, [])

第二引数に空の配列が渡された場合、マウント時のみ、第一引数に渡されたコールバック関数を実行します。 Reactはrenderが呼ばれるたびに第二引数の値がrender前とrender後に変化したかどうかを確認します。

これはcomponentDidMountと同じタイミングになります。

第二引数の配列に値が渡されるとき
const [boolean, changeBoolean] = useState(true)
useEffect(() => {
  console.log('changed')
}, [boolean])

第二引数に値の配列が渡された場合、renderの前後で値が変化した場合に第一引数のコールバック関数が呼ばれます。 第二引数に渡したbooleanの値が変更されるたびに第一引数に渡したコールバック関数が呼ばれるようになります。

マウントとアンマウントのみ実行する場合

useEffectメソッドにおいては、第一引数のコールバック関数内においてreturnを行うと、そのreturnの中の処理がアンマウント時に実行されるようになります。

useEffect(() => {
  console.log("render")
  return () => {
    console.log("Unmounting!")
  }
}, []}

第二引数に空の配列を渡しているので、render毎に実行されることはありません。 console.log("render")の部分がマウント時のみ実行されるようになります。 また、return後の関数が、アンマウント時に実行されるようになります。

React設計のコツ

  • 1. モック(サイトの大まかな見た目)をApp.jsに追加します。returnにカッコを追加し、DOM要素を書いていきます。まずは動かなくていいのでサンプルデータを使って全レイアウトを作成します。
  • 2. パーツ毎にコンポーネント分離しファイル分けを行い、returnの中に書くDOM要素はApp.jsからコピーします。App.jsを修正します。
  • 3. それぞれに動的なパラメータclassコンポーネントならstate等を用いて処理の割り当てを行う。
  • 4. 2から3の繰り返しで完成
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment