【React】 コンポーネントの作り方の基礎

Reactはコンポーネントベースです。1つ1つのパーツをコンポーネントとして切り分け、それらを組み合わせて複雑なUIやアプリケーションを作成していきます。

コンポーネントとは

コンポーネントとは部品やパーツのことです。Reactでは、UIのパーツ1つ1つをコンポーネントとして分離し、そこにDOMやデータ、関数などを1つにまとめます。

コンポーネントの単位の例

Reactはコンポーネントベースです。このコンポーネントを基準にUIや個々の機能を作成していきます。

コンポーネントの例

まず、最初にcreate-react-appした時点で、App.jsが作成されています。

App.js
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

上のコードを見ると、コンポーネントを作るにはまず関数を作り、それをexportします。読み込む側では、

// ...
import App from './App';

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

のように、読み込んだコンポーネントをHTMLのタグのように書くことで表示します。

コンポーネントの作成と読み込み

実際にコンポーネントを作り、App.jsでコンポーネントを表示してみましょう。今回はボタンと、そのボタンを押した回数をカウントするコンポーネントを作成してみます。

コンポーネントを用意

まずはコンポーネントの関数を作ります。ファイル名はcomponents/Counter.jsxにします。

components/Counter.jsx
export default function Counter() {
    // これから実装
}

次にボタンを押した回数を保持する値と更新する関数を作ります。値を保持して書き換えられるようにするには、useStateを利用しました。

import { useState } from "react";

export default function Counter() {
    const [count, setCount] = useState(0);
}

次に表示部分ですが、Reactではreturn (/*DOM*/)のようにDOMを書くことができます。

import React, { useState } from "react";

export default function Counter() {
    const [count, setCount] = useState(0);

    return (
        <React.Fragment>
            <p>カウント: {count}</p>
            <button onClick={() => {setCount(count + 1)}}>カウントアップ</button>
        </React.Fragment>
    );
}

onClick={() => {setCount(count + 1)}}>は、onClickイベントが発生したときに() => { setCount(count + 1) }を実行するというものです。

Fragment

新しくFragmentが登場しました。そもそもReactでは、コンポーネントの戻り値に複数の要素を含めることができません。必ず何かしらの1つの要素に入れる必要があります。

// NG 要素が2個ある
return (
    <p>aaa</p>
    <p>bbb</p>
);

// OK 1つの要素で返している
return (
    <div>
        <p>aaa</p>
        <p>bbb</p>
    </div>
);

では、必ずdiv等で囲めるかというと、そうではありません。例えば、

// 子コンポーネント
function Item() {
    return (
        <div>
            <td>aaa</td>
            <td>bbb</td>
        </div>
    );
}

// 親コンポーネント
function Table() {
    return (
        <table>
            <tr>
                <Item></Item>
            </tr>
        </table>
    );
}

のようにはできません。trの子要素はtdしか受け付けないためです。このようなときにFragmentを使用します。

import React from "react";

function Item() {
    return (
        <React.Fragment>
            <td>aaa</td>
            <td>bbb</td>
        </React.Fragment>
    );
}

Fragmentは、実際にレンダリングされる際に消滅するタグです。つまり、Fragmentの中身だけが取り出され、

<table>
    <tr>
        <td>aaa</td>
        <td>bbb</td>
    </tr>
</table>

のように出力できます。

また、<>に省略できます。これならimportも不要です。

import { useState } from "react";

export default function Hoge() {
    return (
        <>
            <p>aaa</p>
            <p>bbb</p>
        </>
    );
}

読み込み

コンポーネントが作成できたので、次は読み込みましょう。まずはApp.jsでインポートします。

App.jsx
import Counter from './components/Counter';

// ...

次に、App内でCounterをタグのように書いて表示します。最初からあるDOMは適当に消してしまってよいです。

import Counter from './components/Counter';

function App() {
    return (
        <div className="App">
            {/* importしたものをタグのように書く */}
            <Counter></Counter>
      
            {/* またはこう */}
            <Counter />
        </div>
    );
}

書いたら表示できるか試してみましょう。

props

Reactでは親コンポーネントから子コンポーネントに値を渡すのにpropsを用います。

親コンポーネントからは、タグの属性のように値を渡し、受け取る側では関数の第1引数で連想配列で受け取ります。

属性として渡す際は、普通にキャメルケースで渡します

親コンポーネント側
function App() {
    const v = 10;

    return (
        <div className="App">
            <Counter initValue={v} />
        </div>
    );
}
子コンポーネント
export default function Counter(props) {
    const [count, setCount] = useState(props.initValue);

    // ...
}

initValue={v}のように波括弧を使った書き方は、Javascriptとしての値や変数を渡す場合に用います。

単に固定の文字列を渡したい場合は、initValue="hoge"のようにするのもありです。="10"のようにしても数値ではなく文字列として渡されるので注意が必要です。

ここで、propsを受け取る側では、jsDocを使用してpropsの型を設定しておくとスムーズです。

/**
 * @typedef {{
 *  initValue: number
 * }} Props
 */

/**
 * 
 * @param {Props} props 
 * @returns 
 */
export default function Counter(props) {
    // propsの値を使う際に入力補完が効く
    const [count, setCount] = useState(props.initValue);

    // ...
}

propsは読み取り専用です。書き込むことはできません。

export default function Counter(props) {
    // Uncaught TypeError: Cannot assign to read only property ...
    props.initValue += 5;

    // ...
}

まとめ

Reactはコンポーネントベースであり、コンポーネントを活用して開発することは必須です。

現在のReactのコンポーネントは関数で作成し、戻り値に表示するDOMをJSX形式で書きます。コンポーネントを使用する側ではimportrequireで読み込み、それをHTMLタグのように書くことで表示できます。

react component thumb

役に立ったらシェアしよう!