本記事ではReact初心者が公式チュートリアルの挑戦課題をへて思った、Reactの感想を述べます。
ポエムです。
初心者のころの気持ちを忘れないために書きます。
React公式チュートリアルでは三目並べ(○✖️ゲーム)を実装することができ、
「タイムトラベルの実装」の解説まで超丁寧に記述してくれています。
なお、挑戦課題はヒントすらない笑
公式チュートリアル
https://ja.reactjs.org/tutorial/tutorial.html
要約
- React公式チュートリアルの三目並べにある挑戦課題が難しい
- React基礎を体系的に学べるめっちゃいい問題だった
- Reactムズおもろい
できたもの
完成コード ※挑戦課題のネタバレを含みます
https://codepen.io/Yamadamadamada/pen/poLpxmp
Reactを始めた経緯
新しくReact案件に入ることになり、社内チームで毎朝開催している勉強会でReactを学習させてほしいとお願いしたのでした。。。
毎朝1時間Reactを勉強する時間をいただき、チュートリアル挑戦課題を終えるまでに1ヶ月かかったというオチ。
全て終わるまでに合計20時間くらいかかりました。
思ったことツラツラ
render ? return ?公式さんタイポしてません?
してません。
Reactには要素という概念があり、 Reactアプリケーションの最小単位の構成ブロックです。
後掲しますが、要素はコンポーネントとは違います。
// 1つのReact要素
const element = <h1>Hello, world</h1>;
render()
に React要素を渡すと、画面にDOM(HTML形式)にして描画してくれます。
// 要素を DOM として描画する
const root = ReactDOM.createRoot(
document.getElementById('root')
);
const element = <h1>Hello, world</h1>;
root.render(element);
コンポーネントは画面に表示するReact要素を返すクラスや関数を指します。
下のコードはどちらも同じ画面が表示されます。
// 関数コンポーネント
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// クラスコンポーネント
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
で、結局renderはなにかというとクラスコンポーネント内で、生成したいDOMと要素を置く場所になります。
そしてreturnは実際に表示するDOMを返す場所です。(!?)
言葉にするとわかりにくいのでコードで書くと、
要素はrender内で定義することができますがrenderにいるだけでは画面に表示されず、return
にある要素またはコンポーネントが実際に描画されるDOMになります。
(クラスコンポーネントの場合)
// クラスコンポーネント
class Welcome extends React.Component {
render() {
const element = <span>renderの中で定義するよ〜<span>;
return <h1>Hello, {element}</h1>;
}
}
クラスコンポーネントの場合、とありますが、関数コンポーネントはrenderを使いません。私は業務でクラスコンポーネントを使っていないのでrenderを意識することはありません
まとめ
- 画面に表示するにはreturnの中に書けばいいんだな〜
- 関数コンポーネントしか使わない場合、renderのことは忘れて大丈夫!!!(暴論)
なんで戻り値にHTMLタグ入ってるんだ...
それがReactだから。
Reactはマークアップとロジックを両方含む疎結合の「コンポーネント」単位でコードを記載します。
単一責任の原則に基づき単一のコンポーネントにJSXタグ、状態管理、イベントリスナをまとめて書くことで1つの部品が1つの役割を全うするように設計されています。
まとめ
- Reactはコンポーネントにマークアップもロジックも込み込みで一つの部品として独立している
- 単一責任の原則にもとづき1つのコンポーネントには1つの機能しかもたせない
1マスを表示するのにコンポーネント分離しすぎだろ...
それがReactだから。(2回目)
チュートリアル内の三目並べでは9マス表示するのに1つのクラスコンポーネントと2つの関数コンポーネントに分離しています。
それそれの役割を見ていきましょう
- Square
- 関数コンポーネント
- renderSquareから○か✖️を受け取り表示する
- クリックされたとき、発火イベントを親のrenderSquareへ知らせる
- renderSquare
- Board内にある関数コンポーネント
- マスの順番をBoradから受け取り、○か✖️を判断しSquareへ渡す
- Squareで起きたクリックイベントをBoardへ渡す
- Board
- クラスコンポーネント
- renderSquareへマスの順番、◯か✖️かの情報を渡す
- renderSquareから受け取ったクリックイベントに対し、処理を行う
...はい!ややこしいですね!!!
-
Square
1マスを表示 -
Board
1マスを9個まとめる -
renderSquare
ただデータをバケツリレーしてるだけ
Reactは1コンポーネントに対し、1つの役割しか持たせません。上記の例だと、
Square
コンポーネントは「1マスを表示する」ことしかしません。
クリックしても、イベントを親コンポーネントへ知らせるのみで、イベント処理を記述しません。
また、Board
コンポーネントは
「Square
コンポーネントへデータを渡す」「イベント処理を行う」など、
状態管理を行うのみで表示には関与しません。
そして、renderSquare
コンポーネントはコンポーネントへデータを受け渡しする際にデータを整形して渡し、親と子それそれのコンポーネントが影響し合わないようにしてくれているコンポーネントです。
(コンポーネントのゲシュタルト崩壊)
まとめ
- 親子コンポーネントは親が状態管理を行い、子が描画を担当する
- データのバケツリレーしがち
state?Vueのrefみたいなやつ?
違います。が、ReactにもuseRefなるものはあります。
Reactにはコンポーネント内でデータ管理ができるstate
オブジェクトが存在します。
state
オブジェクトの1つであるuseState
は定義した変数をフックし値を更新することができます。
ReactとVueのそれそれで簡単な数字カウンターを記述してみます。
Reactのカウンター
import React, { useState } from "react";
function App() {
// 『state変数』を宣言。初期値を設定。
const [count, setCount] = useState(0);
return (
<button type="button" onClick={() => setCount(count + 1)}>
count is {count}
</button>
);
}
export default App;
Vueのカウンター
<script setup>
import { ref } from "vue";
// 初期値を設定
const count = ref(0);
</script>
<template>
<button type="button" @click="count++">
count is {{ count }}
</button>
</template>
Vueのrefではcount
変数に対し、表示もイベント発火もイベント処理も全部まとめて行うことができます。
しかし、Reactのcount
変数は表示しかできません。
これは、setCount
が更新の役割、count
が表示の役割を別で持つことで、
異なるコンポーネント間でステートフルな振る舞いを共有することができる...らしいです。
まとめ
- 関数コンポーネントはstateオブジェクトで状態管理する
- 表示と更新関数が分離しているので、それぞれ別コンポーネントへ渡せる
書きずらいポイント1
cssのclass定義がなぜかclassName
// タイポじゃないです。classNameでスタイルのクラスを定義します。
<div className="App">classにしてくださいよ〜〜(泣)</div>
書きずらいポイント2
styleがなぜかキャメルケース
<div style={{ textAlign: "center" }}>スネークケースで良くない???</div>
まとめ
- スタイルの定義は書きずらい気がする
- 慣れのような気もする
Reactは大規模開発で役立つんだ、公式もそう言ってる
らしいです。
総括
- 役割分担がかっちりしていてReact楽しい!
- 初学者はReactの思想を理解するのが大変かも
そして、Reactの大まかな考えや書き方のクセを理解するには公式チュートリアルが最適です!
公式最高!!みんなもやろう!!!