【React】 コンポーネントの作り方の基礎
Reactはコンポーネントベースです。1つ1つのパーツをコンポーネントとして切り分け、それらを組み合わせて複雑なUIやアプリケーションを作成していきます。
コンポーネントとは
コンポーネントとは部品やパーツのことです。Reactでは、UIのパーツ1つ1つをコンポーネントとして分離し、そこにDOMやデータ、関数などを1つにまとめます。

Reactはコンポーネントベースです。このコンポーネントを基準にUIや個々の機能を作成していきます。
コンポーネントの例
まず、最初にcreate-react-app
した時点で、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
にします。
export default function Counter() { // これから実装 }
次にボタンを押した回数を保持する値と更新する関数を作ります。値を保持して書き換えられるようにするには、useState
を利用しました。
import { useState } from "react"; export default function Counter() { const [count, setCount] = useState(0); }
useState
に関しては下の記事に載せています。
次に表示部分ですが、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
でインポートします。
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形式で書きます。コンポーネントを使用する側ではimport
やrequire
で読み込み、それをHTMLタグのように書くことで表示できます。
