【React】ContextのProviderは正しく囲まないとコンテキストが分岐する

Contextを使用する際のProviderの指定を間違うと共有したはずのコンテキストが子コンポーネント毎に分岐してしまうので注意が必要です。

サンプルコード

以下のようなContextを使って子コンポーネントにログイン情報を共有するコードを例にします。

//Contextファイル
// AuthContext.js

import React, { createContext, useState } from "react";

export const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  const toggleLogin = () => {
    setIsLoggedIn((prev) => !prev);
  };

  return (
    <AuthContext.Provider value={{ isLoggedIn, toggleLogin }}>
      {children}
    </AuthContext.Provider>
  );
};
//子コンポーネント1
//ComponentA.jsx

import React, { useContext } from "react";
import { AuthContext } from "./AuthContext";

const ComponentA = () => {
  const { isLoggedIn, toggleLogin } = useContext(AuthContext);

  return (
    <div>
      <h2>ComponentA</h2>
      <p>ログイン状態: {isLoggedIn ? "ログイン済み" : "未ログイン"}</p>
      <button onClick={toggleLogin}>
        {isLoggedIn ? "ログアウト" : "ログイン"}
      </button>
    </div>
  );
};

export default ComponentA;
//子コンポーネント2
//ComponentB.jsx

import React, { useContext } from "react";
import { AuthContext } from "./AuthContext";

const ComponentB = () => {
  const { isLoggedIn, toggleLogin } = useContext(AuthContext);

  return (
    <div>
      <h2>ComponentB</h2>
      <p>ログイン状態: {isLoggedIn ? "ログイン済み" : "未ログイン"}</p>
      <button onClick={toggleLogin}>
        {isLoggedIn ? "ログアウト" : "ログイン"}
      </button>
    </div>
  );
};

export default ComponentB;

サンプルなので子コンポーネントComponentAComponentB の中身は同じです。

コンテキストが分岐してしまう書き方

以下のような場合は子コンポーネント毎に別々のコンテキストを持つ(分岐してしまう)ことになる。

//親コンポーネント
//App.js
//誤: コンテキストが分岐する

import React from "react";
import { AuthProvider } from "./AuthContext";

const App = () => {
  return (

    <div>
      {/* 子コンポーネントが同じProviderを別々に囲う */}
      <AuthProvider>
        <ComponentA />
      </AuthProvider>
      <AuthProvider>
        <ComponentB />
      </AuthProvider>
    </div>
  );
};
  • isLoggedIn stateが子コンポーネント間で別々のstateとして扱われる
  • コンポーネント内のログインボタンでisLoggedIn stateを更新してもそれぞれの子コンポーネント内のstateしか更新されない
    • 例1) ComponentAでログインボタンクリック → ComponentAのisLoggedIn stateしか更新されない
    • 例2) ComponentBでログインボタンクリック → ComponentBのisLoggedIn stateしか更新されない

結果的にログイン状態を表示するログイン済み/未ログイン の文字の切替、ログインボタンのログイン/ログアウト の文字の切替が子コンポーネント毎にしか動作しなくなります。

コンテキストを正しく共有する書き方

以下のようにコンテクストを共有したいコンポーネントを1つのProvideで囲むことでコンテキストが正しく共有されます。

//親コンポーネント
//App.js
//正: コンテキストが正しく共有される

import React from "react";
import { AuthProvider } from "./AuthContext";

const App = () => {
  return (
    <div>
      {/* 2つの子コンポーネントを1つのProviderで囲う */}
      <AuthProvider>
        <ComponentA />
        <ComponentB />
      </AuthProvider>
    </div>
  );
};
  • 例1) ComponentAでログインボタンクリック → ComponentA、ComponentBのisLoggedIn stateが更新される
  • 例2) ComponentBでログインボタンクリック → ComponentA、ComponentBのisLoggedIn stateが更新される

ログイン状態を表すログイン済み/未ログイン の文字、ログインボタンのログイン/ログアウト の文字がどちらの子コンポーネントでの切り替わるようになります。

まとめ

  • Contextを使ってコンテキストが共有されない場合はProviderが正しく設定されているか疑う
  • 同じコンテキストを共有する複数の子コンポーネントがある場合、1つの親コンポーネント内で1つのProviderで囲むこと
  • 複数のProviderを使用する場合、それぞれが異なる状態を持つ

感想

React、便利なんだろうけど慣れるまでは修羅の道... はやく楽しく触れるようになりたい。

参照

コンテクストで深くデータを受け渡す – React

useContext – React

JavaScriptでlocalStorageを使う

JavaScriptでlocalStorageを使う方法を調べました。

localStorageとは

Window.localStorage - Web API | MDN

ブラウザにデータを保存させる機能。

  • 保存期間に制限が無い
  • セッションに似ている
    • セッションはブラウザを閉じたらデータは消去される
  • 保存領域はURL毎に異なる
    • 異なるURLの場合は異なるデータを保存する
  • データ容量は5MB
  • JSON形式で保存される

localStorageの機能とJavaScriptのメソッド

保存、取得、削除、初期化が行える。

  • 保存
    • localStorage.setItem('key', 'value');
  • 取得
    • localStorage.getItem('key');
  • 削除
    • localStorage.removeItem('key');
  • 初期化(全削除)
    • localStorage.clear();

その他のメソッド

  • 保存された数
    • localStorage.length;
  • n番目のkey取得
    • localStorage.key(n);

コンソールで使ってみる

ブラウザのコンソールで直接動かせる。

localStorage.length
> 0 //保存件数は0
localStorage.setItem('key', 'value');
> undefined
localStorage.length
> 1 //1件になってる
localStorage.getItem('key');
> 'value' //値が取得できた

jsファイルで保存する

jsファイルからLocalStrageに保存する場合は以下が必要

  • データをJSON形式に変換
    • JSON.stringifyJSON形式に変換
  • localStorage.setItem でデータ保存時は("key", "value") 形式でないとNG

.ex) data配列をキーdataでlocalStorageに保存する

const data = ["text1", "text2", "text3"];
const dataJSON = JSON.stringify(data);
localStorage.setItem("data", dataJSON);

感想

こんなに簡単にブラウザ上に値を保存できるとは驚きでした。 とりあえず基本の使い方はなんとなく分かったので色々使ってみて覚えていこうと思います。

参照

Window.localStorage - Web API | MDN

Railsアプリケーションの規模感の掴み方

既存のRailsアプリケーションの規模感を掴むためのメモです。 各ファイル毎に見ていくと規模感が掴むきっかけになりそうです。

rails routes

エンドポイント、URLの雰囲気を掴む。

  • テキストに落とすと見やすい
  • 量と種別
    • エンドポイント
    • RailsのViewの数
    • APIの数
    • よく分からないものの数
    • APIバージョニング
    • URLの設計
    • 設計思想

rails status

何がどのくらいか、規模感を掴む。

  • モデル
    • 数で規模感が分かる
  • テスト
    • テストが多い処理は複雑
    • どこの処理がふくざつなのか
  • JavaScript
    • フロントの作り込み具合
  • Rails
  • 非同期処理
    • 非同期処理の割合がどれぐらいか

Gemfile、package.json

どんなライブラリが使われているのか把握する。

  • 作り手の意思(選択思想?)
  • Gemfile
    • Deviseは使われているか?
    • 設計に影響の出るGemは使われているか?
      • Railsのレールに乗れるのか?
    • ライブラリのバージョンは?
    • ファイル保存系ライブラリは何か?
    • 知らないGemは?

モデルの行数

モデルで規模感を把握する。

  • git ls files app/models | xargs wc -l | sort
  • userモデルを基準に他がどんな規模なのか?
  • 多くのことをやっているモデルはどのモデルか?
  • どのモデルが大きいのか?
  • userモデルに匹敵するモデルはどのモデルか?

モデルのテストの行数

テストで規模感を把握する。

  • git ls files spec/models | xargs wc -l | sort
  • テストの数で重要なモデルがどのモデルか分かる
  • 複雑なモデルはどのモデルか?
  • 重要なモデルはどのモデルか?

user.rb

user.rb は殆どのアプリケーションで使われているので把握しやすい。

  • コードスタイルの把握
  • 実装思想を掴む
  • user.rbでどれぐらいメンテナンスされているか測れる
  • Rubyの書き方の癖を掴む

config/initializers/-

諸々の確認ができる。

参照

スタートアップで戦い抜いてきたRailsアプリをどう直す? 既存Railsアプリを攻略する時にCTOが見ているポイント - ログミーTech

Reduxの日本語ドキュメントの自分用メモ

Reactに引き続きReduxのキャッチアップが必要になりました... 日本語のドキュメントを読んで必要そうなとこだけまとめた自分用メモです。 間違い、分かりにくい部分がたくさんあるはずです。 Redux自体少し古い技術っぽいので参考にならない部分が大いにあると思います。

Redux/Redux Toolkit

Redux、Redux Toolkitについて。

  • ReduxはJavaScriptアプリの予測可能な状態コンテナ
  • Reduxは状態管理ライブラリ
    • ストア
    • アクション
    • リデューサー
  • npm

  • Redux Toolkit

    • Reduxのサポートツール
    • npmパッケージ
  • Fluxが元になっている

要点

Reduxの概要。

  • Reactのstate をStoreの1つのオブジェクトツリーで保持
  • 状態ツリーはActionで変化できる
  • Actionへの指示をReducerに書く? *状態オブジェクトは書き換えずに、状態が変化した新しいオブジェクトを作成する

  • 状態の変化に応じてUIを更新するためにsubscribe() を使う

    • subscribe() を直接呼び出すことは少ない
    • React Redux ライブラリを使ってsubscribe() を呼び出す
  • 状態を変化させるにはActionをDispatch(送信)すること

    1. Action内でReducerを書く
    2. 書いたActionをDispatchする
    3. 状態が変化する
  • 変更の大元になるのは1つのReduce、1つのStoreだけ

    • 大元のReducerから子Reducerを作ることはある
      • Reactの仮想DOMみたいなもん

モチベーション

Reduxがなぜ開発されたのか。

  • Railsでのデータの更新、ReactでのUI上での見た目の変化を切り離すためにあるっぽい?
    • Railsから送られるJSONの情報を使ってReactがUIを変化させることで複雑性が増すらしい
    • Railsから送られるJSONの情報をReduxがStoreに保持、保持した情報を元にReactがUIを変化させる感じ?

核となるコンセプト

Reduxの考え方について。

  • Actionは何が起きたのかを記述するだけのJavaScriptオブジェクト

  • Actionは何がどう 変化したのかが記述されている

    • Actionを見れば変化の詳細が分かる
    • Actionの最後にReducer 関数を記述する
      • 引き数は今の状態Action次の状態の3つ
        • UIのパーツ毎に関数が増えていくっぽい
  • ReducerがActionの中に書いた関数を呼び出すっぽい

  • Reduxのコードの90%はただのJavaScript

3つの原則

Storeについて。

  • アプリケーションの状態は1つのStoreの中の1つのオブジェクトツリーで保持される

    • オブジェクトツリーを状態ツリーとも呼ぶ
    • デバッグ時は状態ツリーを調査すればok
    • console.log(store.getState()) でStoreの状態が取得できる?
  • 状態は読み込み専用

    • 状態を表現する状態ツリーは書き換えない
    • 状態を変化させるにはActionを送信(Dispatch)することのみ
  • Reducerはただの関数

  • Reducerの引数は今の状態Action次の状態

    • 「こうなっているのを、〇〇を使って、こうしたい」みたい感じ
    • 前の状態を変更するのではなく、次の状態を新たに作って返す(新しいオブジェクト)
  • ReducerでActionを呼び出す順番の制御を行う

    • 状態ツリー(仮想DOMみたいな)の要素毎に子Reducerを追加していく

Action

Actionについて。

  • Actionに状態変化の情報が書いてある
  • ActionはアプリケーションからStoreに送られるデータ
  • StoreはActionのデータだけを参照する
  • store.dispatch() でActionをStoreに送る

  • Actionは単なるJavaScriptオブジェクト

  • Actionは必ずtype プロパティを持つ

    • type プロパティが状態を変化させるActionのタイプを示す
    • type は文字列の定数で定義される
  • Actionのtype を別モジュールで使う場合はimport を使う

    • import {ADD_TODO, REMOVE_TODO} from '../actionTypes'
    • type を定数で定義していることで複数のファイルでも動作できるのかも?
  • Action内にはtype に加えて独自の項目(プロパティ)を追加できる

  • Actionで渡すデータはできるだけ小さく(少なく)する

  • Actionを作る関数をActionクリエイターと呼ぶ

    • Actionオブジェクトを返す関数?
  • Actionを送信するDispatchを行うにはdispatch() 関数を使う

    • 引数にActionクリエイターの関数を渡す
  • 自動でDispatchするActionクリエイターをバウンドActionクリエイターと呼ぶ

  • dispatch() 関数の呼び出しはstore.dispatch() でStoreから直接行う

    • react-reduxconnect() で行うことが多い
  • react-redux とはReactコンポーネントとReduxストアを結びつけるライブラリ

  • react-redux の主な機能

    • Provider コンポーネント
    • connect 関数
      • ReactコンポーネントをReduxストアに送信
      • ReactコンポーネントがReduxストアの状態を参照し、必要なデータをpropsとして取得する
      • ActionをStoreに送信(Dispatch)することもできる
        • mapStateToProps: ReactがReduxからデータを取得
        • mapDispatchToProps: ReactからReduxへのデータの送信
  • importexport を使って関数、変数を複数のモジュールで使用する

    • importfrom でインポート先のパスを記述する

Reducer

Reducerについて。

  • Reducerがイベントに反応して状態を変化させる
  • アプリケーションの状態を1つのオブジェクトとして保持している
  • Railsから送信されるデータとUIの状態をプロパティを使って分けて管理する
{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    {
      text: 'Consider using Redux', //UIの状態
      completed: true, //データの状態
    },
    {
      text: 'Keep all state in a single tree', //UIの状態
      completed: false //データの状態
    }
  ]
}
  • Reducerファイル内プロパティのネストは深くしない
    • ネストを浅くしてオブジェクトのidでアクセスした方が管理しやすい
  • Reducerは前の状態Action次の状態を引数にとり、次の状態 を返すJavaScriptの関数
  • Reducerは内部的にArray.prototype.reducer を呼び出している

    • Array.prototype.reducer とは配列の合計値を出せるRubyのeachの応用みたいなやつ
  • Reducerでやってはいけないこと

    • 引数を変更する
    • 関数の呼び出し
    • 外部の処理(API等)の実行
  • ReducerはAction次の状態 を振り分けるコントローラーみたいなもの

  • ReducerはJavaScriptObject.assign メソッドでAction 元にした次の状態 のオブジェクトを作成する

    • JavaScriptObject.assign メソッドは新しいオブジェクトを作成するメソッド
  • combineReducers() 関数は複数のReducerを結合して1つのReducer関数を作るもの

Store

Storeについて。

  • Store: Action、Reducerをまとめるオブジェクト

    • Action: イベント
    • Reducer: Actionを起点に状態を変化させる
  • Storeの役割

    • アプリケーションの状態のを保持
    • getState() で状態の変更を許可する
    • dispatch(action) で状態の更新を許可する
    • subscribe(listener) で状態の変更を検知した時に発動するメソッドを登録する
  • Storeは必ず1つ

    • 状態で処理を分けたい時はReducerで処理を分岐させる

    • Storeの作成はcombineReducers() で複数のReducerを1つにまとめ、createStore() に渡すだけ

  • Storeの初期状態を設定するにはcreateStore() の2つ目の引数に初期状態のstateを渡す

データフロー

Reduxのデータの流れについて。

  • Reduxのデータの流れは一方向

  • データのライフサイクル

    1. store.dispatchを呼び出しAction(イベント)を送る
    2. ReducerがActionを元に次の状態 を呼び出す(新しいオブジェクトを作成)
    3. combineReducers() で複数のReducerを1つにまとめる(この時点で状態変更は保存されている)
    4. 1つにまとめられたReducerを元にstore.getState で現在の状態を取得する
    5. React Redux を使っている場合はcomponent.setState(newState) で自動で新しい状態を反映する

感想

Reactに続き、Reduxも難解です... じっくり腰を据えて学んでいきたいところですが今は概要が掴めたのでとりあえずokにしときます。 新しいことや分からないことを直視するのはなかなか大変ですが、プログラミングってそもそもそういうモノだったなぁと思い出すきっかけになりました。 そもそも人はやれる事しかやれないので焦らず地道に進んでいきます!

参照

Redux - A predictable state container for JavaScript apps. | Redux

はじめに · Redux

React公式ドキュメントの自分用メモ

Reactのキャッチアップが必要になったので公式 を一通り読んだことをまとめました。 自分用のメモなので間違い、分かりにくいところがたくさんあると思います。

Hello World

Reactを触る上での最低限のポイント。

  • letconst で変数を定義する(var と等価)
  • クラスメソッドの定義の間にカンマは不要
class MyClass {
  method1() {
    // メソッドの定義
  }
//ここのカンマが不要
  method2() {
    // メソッドの定義
  }
}

比較的新しいJavaScriptの構文

  • メソッド内のthis の値に注意

    • オブジェクト指向の言語ではメソッド内のthisはそのメソッドが属するオブジェクト自体を指すこと
    • JavaScriptではメソッドの呼び出し方によってthis の値が変わることがある
  • アロー関数

    • function よりも短く書ける
    • x => x * 2function(x) { return x * 2; } と同じ
    • 関数内のthis を持たない
    • this は外部メソッドの値を保持する
const obj = {
  name: 'John',
  regularFunction: function() {
    console.log(this.name);
  },
  arrowFunction: () => {
    console.log(this.name);
  }
};

obj.regularFunction(); // 出力: John
obj.arrowFunction();   // 出力: undefined

JSXの導入

JSXについて。

// 丸括弧で囲む
return (
  <div>
    <h1>Hello, World!</h1>
    <p>Welcome to React.</p>
  </div>
);
  • コンパイル後はJavaScriptオブジェクトに変換される
  • if文の条件式、引数などに使える式である
  • 文字列リテラル""''で加工
  • HTMLの属性名はキャメルケース
  • HTMLタグの要素が空の場合は/> で閉じれる
  • 描画前に値はエスケープされる(XSS対策済み)
  • Babelでコンパイルされる
  • JSXをコンパイルしたものをReact要素と呼ぶ
  • React要素を元にDOMを描画する

要素のレンダー

描画に関するDOM関連の基礎知識。

  • DOMとReact要素

    • DOM
      • ブラウザが提供する実際のHTML要素
      • 操作コストが高い
    • React要素
      • Reactが提供する仮想的な要素
      • プレーンなJavaScriptオブジェ区tお
      • 仮想DOM
      • DOMと同様の階層構造を持つ
      • 操作コストが低い
      • React DOMを使用してDOMに変換する
        • DOMとの差分のみを更新する
  • React DOMのルートはid="root"

    • ルートDOMノード
    • SPAはルートDOMノードは1つだけ
  • ReactDOM.createRoot() はルートDOMノードに描画するルートオブジェトを作成する

    • ルートオブジェクトがAPIとしてrender メソッドを用意してくれる
  • React要素はいミューダブル(あとから変更できない)

コンポーネントとprops

React特有のコンポーネント、propsについて。

//関数コンポーネント
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

//クラスコンポーネント
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

stateとライフサイクル

状態管理に関する基礎知識。

  • クラスコンポーネント(class クラス名 extends ...)内のコンストラクタは必ず親クラスのコンストラクタを呼び出す必要がある(super(props);)

  • マウント

    • Reactコンポーネントが初めてDOMに描画されるプロセス
    • コンポーネントの初期化、リソース割り当て、イベント登録などが行われる
    • ライフサイクルの最初のフェーズ
    • マウント時に特定の処理を行うにはcomponentDidMount を使う
  • アンマウント

    • コンポーネントがDOMから削除される時のプロセス
    • リソースの解放、イベント解除などが行われる
    • ライフの最後のフェーズ
    • アンマウント時に特定の処理を行うにはcomponentWillUnmount を使う
  • state は直接変更しない

    • state を変更する場合はsetState() を使う
    • setState はコンストラクタ内で行う
  • state の更新はあいまい
    • 非同期に更新される場合がある
  • setState() は現在のstate に値をマージする

  • props でのデータフローは単一方向

イベント処理

状態を変更する際のイベント処理について。

  • Reactのイベント処理

    • DOM要素のイベント処理と似ている
    • イベント名はキャメルケース
    • JSXのイベントハンドラは文字列ではなく関数を渡す
    • クリックイベントでfalseを返すにはpreventDefault を呼び出す
  • this の値はメソッドがどのオブジェクトに属しているかによって変化する

    • メソッド内でのthis は当メソッドが所属しているオブジェクトを参照する
    • JSX内のthis の扱いには注意する
      • 参照するオブジェクトを明示的にbind する必要がある
  • bind とはthis が参照するオブジェトを指定するための処理

  • コールバック関数をアロー関数で定義することでthis が正しく参照される

    • アロー関数の記述を見つけたらthis の参照先に注意する
  • onClick で呼び出すコールバックメソッドのthis の扱いに注意

    • bindthis の参照先を指定する
    • アロー関数を使いthis の参照先を明示する
  • Reactではクリックイベントが発生すると自動でイベントオブジェクトが自動生成される

  • イベントオブジェクトには次のようなものが含まれている
    • target: イベントが発生した要素
    • type: イベントの種類
    • currentTarget: イベントハンドラがバインドされている要素
  • イベントオブジェクトでユーザーの操作に関する情報を取得できる

条件付きレンダー

render 関連。

リストとkey

リストの描画時の注意点。

  • 要素のリスト項目の作成時には各要素にユニークなキー属性を付ける必要がある
    • 再描画時にリスト要素の一位正を追跡できないため要素の順番を正確に反映できない
    • パフォーマンスの低下
//要素のリスト項目にキーが無い
function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li>{number}</li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

//要素のリスト項目にキーを追加
function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>
      {number}
    </li>
  );
  return (
    <ul>{listItems}</ul>
  );
}
  • 要素のリスト項目のキー要素はユニークな文字列が良い
    • レコードのID
    • ユニークな値
  • リスト形式の子コンポーネントを呼び出す場合は親コンポーネント側でユニークなキーを指定して呼び出す
function ParentComponent() {
  const items = ['item1', 'item2', 'item3'];

  const listItems = items.map((item, index) => (
    <ChildComponent key={item} item={item} />
  ));

  return <ul>{listItems}</ul>;
}

function ChildComponent({ item }) {
  return <li>{item}</li>;
}
  • リスト出力時のmap(){} を使ってインラインで埋め込むこともできる

フォーム

フォーム描画時のポイント。

  • Reactは変更される状態はコンポーネントstate に保持される

    • statesetState() メソッドでのみ更新できる
  • ユーザーが入力した内容、イベントをstate に反映することでコンポーネントを制御する

    • ユーザーの入力をトグルにして動的に色々な処理ができる
    • Rails + Reactで実装するメリット
  • preventDefault() は、通常のブラウザの挙動をキャンセルし、ページ遷移やリロードを防止する

    • ユーザーの入力に対して独自の処理を追加する時に使用する
  • フォーム内のinput タグのvalue属正をstate と同期させることでユーザーの入力をリアルタイムにReact側に送信できる

    • ユーザーの入力がリアルタイムにstate に反映される
  • フォームないのtextarea タグもinput と同様にvalue属性をstate と同期させることでリアルタイムにReact側に送信できる

  • select タグのvalue属性に配列を渡すことでオプションを複数表示することができる

  • [] を使うことでstateの持つプロパティを動的に変得ることができる(jsのComputed Property Names)

    • Rubyのハッシュも同じようなことができる
      • Rubyはキーがシンボルが主流、jsは文字列が主流
this.setState({
  [name]: value
});
  • フォームに入力させたくない場合はvalue属性にundefinedかnullに設定すれば良い
  • フォームに一度入力した後に変更不可にする場合はdisabled 属性をstate を使って動的に変更することで実現できる

state のリフトアップ

state を共通親に設定するリフトアップについて。

  • 複数のコンポーネントstate を共通化したい場合は共通の親コンポーネントstate を移動する(stateのリフトアップ)
  • state のリフトアップの影響
    • 同じような記述(ボイラープレート)がたくさん生まれる
    • バグを発見しやすい
    • コンポーネントstate が共通化される
  • state から計算できる値はstate として保持しない
    • データの一貫性が保たれる
    • データフローが明確になる
    • デバッグ時にデータが追いやすい

コンポジション vs 継承

props を子要素で呼び出す時のポイント。

  • props.children は呼び出し側の子要素を子コンポーネント内で表示できる特別なプロパティ
function Sidebar(props) {
  return (
    <div className="sidebar">
      <h2>{props.title}</h2>
      <p>{props.description}</p>
      <div>{props.children}</div>
    </div>
  );
}

function App() {
  return (
    <Sidebar title="Sidebar Heading" description="This is the sidebar content.">
      <button>Click me</button> // props.childrenで呼び出される
      <p>Additional content</p> // props.childrenで呼ばれる
    </Sidebar>
  );
}

Reactの流儀

描画処理を追加するまでの手順。

UIを作る手順

1. JSONのデータ構造を分割して、それぞれの部分をどのReactコンポーネントとしてUIに表示するか決める(図を書いた方が分かりそう)

  • 表示部分を四角く囲んで名前を付ける
  • 出来上がった図をリスト形式でネストさせて構造を把握する

2. データモデルを受け取りUIの描画だけを行う(ユーザーからの操作は不可)

  • 表示とユーザー操作を切り離す
  • この段階ではprops だけ使い、state は使わない
  • 大きい枠組みから作り始めた方が楽

3. 何をstateにするか決める

  • 必要になった時にstate を元に処理を追加することを意識(不要なstateを作らない)
  • 何をstateにするか決める
    • コンポーネントからのprops であればstate ではない
    • 時間経過で変化しないならstate ではない
    • コンポーネント内のpropsstate から算出できるならstate ではない

4. stateをどのクラスに配置するか決める

  1. stateで表示する全てのコンポーネントを洗い出す
  2. 見つけたコンポーネントの共通の親コンポーネントを見つける
  3. 共通の親コンポーネント or そのさらに親コンポーネント がstateを持つべきか決める
  4. stateを持つべきコンポーネントが見つからなかったらstate保持だけのコンポーネントを作る

感想

React難しい... 今まで学習してきたなかで間違い無く一番難易度が高いと感じてます。 ただ、これを乗り越えれば苦手だったフロントエンドとも仲良くなれそうなので地道にやっていきます!

参照

Hello World – React