この単一のMedium記事に詰まったReact.jsのすべての基本概念

Reactの理由、内容、および方法を学ぶための紹介

この記事は、jsComplete.com / react-introにあるインタラクティブガイドの改編です。 jsCompleteバージョンには、コンテンツをナビゲートするためのコード例とリンクが埋め込まれています。
始める前に、これは初心者向けのガイドであり、Reactを使用するための基礎として分類した概念をカバーしています。 Reactの完全なガイドではなく、完全な紹介です。
このガイドの最後に、次のレベルのリソースをいくつかリストします。このガイドは、あなたがそれらを理解する道を開くでしょう。

Reactは、ユーザーインターフェイスを構築するためのJavaScriptライブラリとして定義されています。この定義の2つの異なる部分について話を始めましょう。

ReactはJavaScriptの「ライブラリ」です。まさに「フレームワーク」ではありません。完全なソリューションではなく、多くの場合、Reactでより多くのライブラリを使用してソリューションを作成する必要があります。 Reactは、ソリューションの他の部分については何も想定していません。

フレームワークは、特に若いチームやスタートアップにとって大きな目的を果たします。フレームワークを使用する場合、多くの賢明な設計上の決定が既に行われているため、優れたアプリケーションレベルのロジックの作成に集中するための明確な道筋が得られます。ただし、フレームワークにはいくつかの欠点があります。大規模なコードベースで作業している経験豊富な開発者にとって、これらの不利な点は契約を破る場合があります。

フレームワークは柔軟ではありませんが、一部の主張はそうです。通常、フレームワークでは、すべてを特定の方法でコーディングする必要があります。その方法から逸脱しようとすると、通常、フレームワークはそれについてあなたと戦うことになります。通常、フレームワークも大きく、機能が豊富です。それらのほんの一部を使用する必要がある場合は、とにかくすべてを含める必要があります。確かに、この点は今日変化していますが、まだ理想的ではありません。一部のフレームワークはモジュール化されていますが、これは素晴らしいと思いますが、私は純粋なUnix哲学の大ファンです。

1つのことを行うプログラムを作成し、それをうまく実行します。連携して動作するプログラムを作成します。
—ダグ・マキロイ

ReactはUnixの哲学に従います。なぜなら、それはたった1つのこととそのことを非常にうまく行うことに焦点を合わせた小さなライブラリだからです。この「1つのこと」は、Reactの定義の2番目の部分、つまりユーザーインターフェイスの構築です。

ユーザーインターフェイス(UI)は、ユーザーがマシンと対話するためにユーザーの前に置くものです。電子レンジのシンプルなボタンからスペースシャトルのダッシュボードまで、UIはどこにでもあります。インターフェイスしようとしているデバイスがJavaScriptを理解できる場合、Reactを使用してそのUIを記述できます。 WebブラウザーはJavaScriptを理解するため、Reactを使用してWeb UIを記述することができます。

基本的にReactで行うことなので、ここで説明するという言葉を使用するのが好きです。欲しいものを伝えるだけです!その後、ReactはWebブラウザーで実際のUIを構築します。 Reactや同様のライブラリがなければ、ネイティブWeb APIとJavaScriptを使用してUIを手動で作成する必要がありますが、これはそれほど簡単ではありません。

「Reactは宣言型」というステートメントを聞いたとき、これがまさにその意味です。 Reactを使用してUIを記述し、必要なものを(方法ではなく)伝えます。 Reactは「方法」を処理し、宣言的な説明(React言語で記述)をブラウザーの実際のUIに変換します。 Reactはこの単純な宣言力をHTML自体と共有していますが、Reactを使用すると、静的データだけでなく動的データを表すHTML UIを宣言できるようになります。

Reactがリリースされたとき、実際のDOMを調整するために使用できる仮想DOMのスマートなアイデアを導入したため、そのパフォーマンスについて多くの話題がありました(これについては次のセクションで説明します)。

DOMは「ドキュメントオブジェクトモデル」です。 HTML(およびXML)ドキュメント用のブラウザのプログラミングインターフェイスは、それらをツリー構造として扱います。 DOM APIを使用して、ドキュメントの構造、スタイル、およびコンテンツを変更できます。

Reactのパフォーマンスは、今日でも非常に人気がある最も重要な理由の1つですが、私はそれを「最高の」ものとして分類しません。 Reactは、開発者とブラウザーの間に共通の言語を作成し、開発者がUIを宣言的に記述し、DOM要素のアクションではなく、状態のアクションを管理できるため、ゲームチェンジャーだと思います。ユーザーインターフェイスの「成果」の言語です。開発者は、インターフェイス上のアクションを説明する手順を考える代わりに、インターフェイスを「最終」状態(関数など)の観点から説明するだけです。その状態にアクションが発生すると、Reactはそれに基づいてDOMのUIの更新を処理します(これは、次に説明するように効率的に行われます)。

Reactが学習に値する理由の1つを誰かに聞かれたら、この結果ベースのUI言語がそれです。この言語を「React言語」と呼びます。

React言語

次のようなTODOのリストがあるとしましょう:

const todos:[
  {body: 'React Fundamentals'、完了:true}、
  {body: 'TODOアプリの構築'、完了:false}、
  {body: 'ゲームの構築'、完了:false}、
];

このtodos配列は、UIの開始状態です。それらを表示して管理するには、UIを作成する必要があります。 UIには、新しいTODOを追加するフォーム、TODOを完了としてマークする方法、完了したすべてのTODOを削除する方法があります。

これらの各アクションでは、DOMノードを作成、挿入、更新、または削除するために、アプリがDOM操作を実行する必要があります。 Reactを使用すると、これらすべてのDOM操作について心配する必要はありません。それらがいつ発生する必要があるか、どのように効率的に実行するかについて心配する必要はありません。アプリの「状態」にtodos配列を配置し、React言語を使用してReactを「コマンド」し、UIに特定の方法でその状態を表示します。

TODOリスト
      {todos.map(todo =>     
  • {todo.body}   )}
//その他のフォーム要素...

構文についてはまだ心配しないでください。何が起こっているのか疑問に思っている場合は、JavaScriptオブジェクトの配列をReact要素の配列にマッピングするだけです。それについてはもうすぐ。

その後、そのtodos配列でデータ操作を行うことに集中できます!その配列の項目を追加、削除、更新すると、Reactは、ブラウザーでレンダリングされたUIでこのオブジェクトに加えた変更を反映します。

最終状態に基づいてUIをモデリングすることに関するこのメンタルモデルは、特にビューに多くのデータ遷移がある場合、理解および操作が容易です。たとえば、何人の友人がオンラインになっているかを示すビューを考えてみましょう。そのビューの「状態」は、現在オンラインになっている友人の数の1つの数字になります。少し前に3人の友人がオンラインになり、そのうちの1人が切断し、さらに2人が参加したことは関係ありません。現時点では、4人の友人がオンラインであることを知っています。

Reactのツリー調整

Reactの前に、DOM APIとして知られているブラウザーのAPIを使用する必要があるとき、DOMツリーの走査を可能な限り避け、その理由があります。 DOMでの操作は、入力、スクロール、サイズ変更などのユーザーイベントへの反応など、ブラウザーで発生する他のすべての処理を行う同じ単一のスレッドで実行されます。

DOMでの高価な操作は、ユーザーにとって遅くて厄介な体験を意味します。アプリケーションが絶対的な最小限の操作を行い、可能な限りそれらをバッチ処理することが非常に重要です。 Reactは、まさにそれを実現するためのユニークなコンセプトを思い付きました!

ブラウザで要素のツリーをレンダリングするようにReactに指示すると、最初にそのツリーの仮想表現を生成し、後で使用できるようにメモリ内に保持します。次に、DOM操作を実行して、ツリーをブラウザに表示します。

以前にレンダリングした要素のツリーを更新するようにReactに指示すると、更新されたツリーの新しい仮想表現が生成されます。これで、Reactのメモリには2つのバージョンのツリーがあります!

ブラウザーで更新されたツリーをレンダリングするために、Reactは既にレンダリングされたものを破棄しません。代わりに、メモリ内にあるツリーの2つの仮想バージョンを比較し、それらの違いを計算し、メインツリー内のどのサブツリーを更新する必要があるかを把握し、ブラウザー内のこれらのサブツリーのみを更新します。

このプロセスは、ツリー調整アルゴリズムと呼ばれるものであり、ReactをブラウザーのDOMツリーを操作する非常に効率的な方法にします。その例をすぐに表示します。

宣言的な結果ベースの言語と効率的なツリー調整に加えて、Reactが大人気を博したと思う他の理由のいくつかを以下に示します。

  • DOM APIでの作業は困難です。 Reactを使用すると、開発者は実際のブラウザよりも使いやすい「仮想」ブラウザで作業できます。 Reactは基本的に、あなたに代わってDOMとの通信を行うエージェントのように機能します。
  • Reactには「Just JavaScript」というラベルが付けられていることがよくあります。これは、学習するAPIが非常に少ないことを意味します。その後、JavaScriptスキルが優れたReact開発者になります。これは、より大きなAPIを備えたライブラリーよりも優れています。また、React APIはほとんどが関数です(必要に応じてオプションでクラスもあります)。 UIビューはデータの関数であると聞いたとき、Reactでは文字通りそうです。
  • Learning Reactは、iOSおよびAndroidモバイルアプリケーションにも大きなメリットをもたらします。 React Nativeを使用すると、Reactスキルを使用してネイティブモバイルアプリケーションを構築できます。 Web、iOS、Androidアプリケーション間でロジックを共有することもできます。
  • FacebookのReactチームは、facebook.comですぐにReactに導入されるすべての改善点と新機能をテストします。これにより、コミュニティ内の図書館に対する信頼が高まります。 Reactのリリースでは、Facebookでの徹底的な実稼働テスト後にのみリリースされるため、大きな深刻なバグはほとんど見られません。また、Reactは、Netflix、Twitter、Airbnbなど、頻繁に使用される他のWebアプリケーションにも対応しています。

最初のReactの例

ツリー調整プロセスの実際的な利点とそれがもたらす大きな違いを確認するために、その概念に焦点を当てた簡単な例を見ていきましょう。 HTML要素のツリーを2回生成して更新します。1回はネイティブWeb APIを使用し、次にReact API(およびその調整作業)を使用します。この例を単純にするために、コンポーネントやJSX(Reactでよく使用されるJavaScript拡張機能)は使用しません。 JavaScriptインターバルタイマー内で更新操作も行います。これは、Reactアプリケーションを記述する方法ではありませんが、一度に1つのコンセプトに焦点を当てましょう。

このjsCompleteプレイグラウンドセッションjsdrops.com/react-dom1から始めます。

このセッションでは、2つのメソッドを使用して単純なHTML要素がディスプレイにレンダリングされます。

方法#1:Web DOM APIを直接使用する

document.getElementById( 'mountNode')。innerHTML = `
  
    こんにちはHTML    `;

方法#2:ReactのAPIを使用する

ReactDOM.render(
  React.createElement(
    「div」、
    ヌル、
    「Hello React」、
  )、
  document.getElementById( 'mountNode2')、
);

ReactDOM.renderメソッドとReact.createElementメソッドは、ReactアプリケーションのコアAPIメソッドです。実際、React Webアプリケーションは、これらの両方の方法を使用しないと存在できません。それらについて簡単に説明させてください。

ReactDOM.render

これは基本的に、ブラウザーのDOMへのReactアプリケーションのエントリーポイントです。 2つの引数があります。

  1. 最初の引数は、ブラウザにレンダリングするものです。これは常に「React要素」です。
  2. 2番目の引数は、ブラウザーでそのReact要素をレンダリングするWHEREです。これは、静的にレンダリングされたHTMLに存在する有効なDOMノードでなければなりません。上記の例では、プレイグラウンドの表示領域に存在する特別なmountNode2要素を使用しています(最初のmountNodeはネイティブバージョンに使用されます)。

React要素とは正確には何ですか? DOM要素を記述するVIRTUAL要素です。 React.createElement APIメソッドが返すものです。

React.createElement

Reactで(上記のネイティブDOMの例のように)文字列を使用してDOM要素を表す代わりに、React.createElementメソッドの呼び出しを使用して、オブジェクトでDOM要素を表します。これらのオブジェクトは、React要素と呼ばれます。

React.createElement関数には多くの引数があります。

  1. 最初の引数は、DOM要素が表すHTML「タグ」であり、この例ではdivです。
  2. 2番目の引数は、DOM要素に持たせたい属性(id、href、titleなど)用です。使用している単純なdivには属性がないため、そこではnullを使用しました。
  3. 3番目の引数は、DOM要素のコンテンツです。 「Hello React」という文字列をそこに入れました。オプションの3番目の引数と、その後のすべてのオプションの引数は、レンダリングされた要素の子リストを形成します。要素には0個以上の子を含めることができます。
React.createElementは、Reactコンポーネントから要素を作成するためにも使用できます。

反応要素はメモリ内に作成されます。実際にReact要素をDOMに表示するには、ReactDOM.renderメソッドを使用します。ReactDOM.renderメソッドは、React要素の状態をブラウザの実際のDOMツリーに反映する最適な方法を見つけるために多くのことを行います。

このコードセッションで2つのメソッドを実行すると、「Hello HTML」ボックスと「Hello React」ボックスが表示されます。

Nesting Reactエレメント

2つのノードがあります。1つはDOM APIで直接制御され、もう1つはReact API(DOM APIを使用)で制御されます。ブラウザーでこれら2つのノードを作成する方法の唯一の大きな違いは、HTMLバージョンでは文字列を使用してDOMツリーを表し、Reactバージョンでは純粋なJavaScript呼び出しを使用してDOMツリーをオブジェクトで表すことです文字列の代わりに。

HTML UIがどんなに複雑になろうとも、Reactを使用すると、すべてのHTML要素がReact要素で表されます。

このシンプルなUIにさらにHTML要素を追加しましょう。テキストボックスを追加して、ユーザーからの入力を読み取りましょう。 HTMLバージョンの場合、新しい要素のタグをテンプレート内に直接挿入できます。

document.getElementById( 'mountNode')。innerHTML = `
  
    こんにちはHTML     <入力/>    `;

Reactで同じことを行うには、上記のReact.createElementの3番目の引数の後にさらに引数を追加できます。これまでのネイティブDOMの例の内容と一致させるために、inputelementをレンダリングする別のReact.createElement呼び出しである4番目の引数を追加できます。

ReactDOM.render(
  React.createElement(
    「div」、
    ヌル、
    「Hello React」、
    React.createElement( "input")
  )、
  document.getElementById( 'mountNode2')、
);

両方のバージョンで現在の時刻もレンダリングしましょう。 pre要素に入れてみましょう(プレイグラウンドで等幅フォントにするため)。 new Date()。toLocaleTimeString()を使用して、単純な時間文字列を表示できます。ネイティブDOMバージョンで必要なことは次のとおりです。

document.getElementById( 'mountNode1')。innerHTML = `
  
    こんにちはHTML     <入力/>     
 $ {new Date()。toLocaleTimeString()} 
  
`;

Reactで同じことを行うには、最上位のdiv要素に5番目の引数を追加します。この新しい5番目の引数は、別のReact.createElement呼び出しです。今回は、コンテンツに新しいDate()。toLocaleTimeString()文字列でpreタグを使用します。

ReactDOM.render(
  React.createElement(
    「div」、
    ヌル、
    「Hello React」、
    React.createElement( 'input')、
    React.createElement(
      「pre」、
      ヌル、
      new Date()。toLocaleTimeString()
    )
  )、
  document.getElementById( 'mountNode2')
);

どちらのバージョンも、ブラウザーでまったく同じHTMLをレンダリングしています。

おそらく今考えているように、Reactを使用することは、シンプルで使い慣れたネイティブの方法よりもはるかに困難です。 Reactがこれほどうまく機能しているのは、使い慣れたHTMLをあきらめ、HTMLで簡単に書くことができるものを書くために新しいAPIを学ぶ必要があるからです。

答えは、最初のHTMLビューをレンダリングすることではありません。 DOMの既存のビューを更新するために必要なことです。

React要素を更新する

これまでのDOMツリーで更新操作を行いましょう。単純に時間文字列を毎秒刻みます。

setInterval WebタイマーAPIを使用して、ブラウザーでJavaScript関数呼び出しを簡単に繰り返すことができます。両方のバージョンのすべてのDOM操作を関数に入れ、レンダリングという名前を付け、setInterval呼び出しで使用して、毎秒繰り返すようにしましょう。

この例の完全な最終コードは次のとおりです。

// jsdrops.com/react-dom2
const render =()=> {
  document.getElementById( 'mountNode')。innerHTML = `
    
      こんにちはHTML       <入力/>       
 $ {new Date()。toLocaleTimeString()} 
    
  `;
  ReactDOM.render(
    React.createElement(
      「div」、
      ヌル、
      「Hello React」、
      React.createElement( 'input'、null)、
      React.createElement(
        「pre」、
        ヌル、
        new Date()。toLocaleTimeString()
      )
    )、
    document.getElementById( 'mountNode2')
  );
};
setInterval(render、1000);

jsdrops.com/react-dom2でこのコードを実行した結果を確認し、両方のバージョンで時間文字列が毎秒刻々と変化していることに注目してください。 DOMでUIを更新しています。

これは、Reactが潜在的にあなたの心を吹き飛ばす瞬間です。ネイティブDOMバージョンのテキストボックスに何かを入力しようとすると、できません。基本的に、すべてのティックでDOMノード全体を破棄して再生成するため、これは非常に期待されています。ただし、Reactでレンダリングされるテキストボックスに何かを入力しようとすると、間違いなく入力できます。

Reactレンダリングコード全体がティックタイマー内にありますが、ReactはDOMツリー全体ではなく、pre要素のコンテンツのみを変更しています。これが、テキスト入力ボックスが再生成されず、入力できるようになった理由です。

Chrome DevToolsの要素パネルで2つのDOMノードを調べると、DOMを更新するさまざまな方法を視覚的に確認できます。 Chrome DevToolsの要素パネルでは、更新されるDOM要素が強調表示されます。ネイティブHTMLバージョンは、ティックごとにdiv#mountNodeコンテナー全体を再生成し、Reactはdiv#mountNode2コンテナーでpreタグのみを再生成する方法を確認できます。

これは、Reactのスマートな差分アルゴリズムの動作です。メインのDOMツリーで更新する必要があるのは、実際に更新する必要があるものだけですが、他のすべては同じままです。この差分プロセスは、Reactのメモリ内に保持される仮想DOM表現のために可能です。何回UIビューを再生成する必要がある場合でも、Reactは必要な「部分的な」更新のみをブラウザーに反映します。

この方法ははるかに効率的であるだけでなく、UIの更新について考える方法の複雑さの大きなレイヤーを削除します。 ReactにDOMを更新するかどうかに関するすべての計算を実行させることで、データ(状態)とそのUIを記述する方法について考えることに集中できます。次に、ブラウザの実際のUIにこれらの更新を反映するために必要な手順を心配することなく、必要に応じてデータ状態の更新を管理します(Reactが正確にそれを実行し、効率的な方法でそれを行うためです!)

Reactはコンポーネントに関するすべてです

Reactでは、再利用可能、構成可能、およびステートフルなコンポーネントを使用したUIについて説明します。

小さなコンポーネントを定義し、それらをまとめて大きなコンポーネントを形成します。小規模または大規模なすべてのコンポーネントは、異なるプロジェクト間でも再利用可能です。

コンポーネントは、単純な関数(あらゆるプログラミング言語)として考えることができます。いくつかの入力を使用して関数を呼び出し、出力を提供します。必要に応じて関数を再利用し、小さな関数から大きな関数を作成できます。

Reactコンポーネントはまったく同じです。入力は「小道具」のセットであり、出力はUIの説明です。 1つのコンポーネントを複数のUIで再利用でき、コンポーネントには他のコンポーネントを含めることができます。 Reactコンポーネントの基本的な形式は、実際には昔ながらのJavaScript関数です。

一部のReactコンポーネントは純粋ですが、コンポーネントに副作用を導入することもできます。たとえば、コンポーネントは、ブラウザにマウントされるとWebページのHTML「タイトル」を変更したり、ブラウザビューを特定の位置にスクロールしたりする場合があります。

最も重要なことは、Reactコンポーネントは、コンポーネントのライフサイクルにわたって変化する可能性のあるデータを保持するプライベート状態を持つことができることです。このプライベート状態は、コンポーネントの出力を駆動する入力の暗黙的な部分であり、実際にReactに名前を付けているのはこのためです!

とにかくReactの名前が「React」なのはなぜですか?
Reactコンポーネント(入力の一部)の状態が変化すると、それが表すUI(出力)も変化します。 UIの説明のこの変更は、使用しているデバイスに反映する必要があります。ブラウザでは、DOMツリーを更新する必要があります。 Reactアプリケーションでは、手動でそれを行いません。 Reactは状態の変化に単純に反応し、必要に応じて自動的に(そして効率的に)DOMを更新します。

関数を使用してコンポーネントを作成する

Reactコンポーネント(最も単純な形式)は、昔ながらのJavaScript関数です。

// jsdrops.com/bx1
機能ボタン(小道具){
  //ここでDOM / React要素を返します。例えば:
  return 
//ブラウザでButton要素をレンダリングするには
ReactDOM.render(

上記のButton関数の返された出力で、HTMLのように見えるものをどのように書いたかに注意してください。これはHTMLでもJavaScriptでもなく、Reactでもありません。これはJSXです。これはJavaScriptの拡張機能であり、HTMLのような構文で関数呼び出しを記述できます。

先に進み、Button関数内の他のHTML要素を返して、それらがすべてサポートされる方法を確認してください(たとえば、入力要素またはtextarea要素を返します)。

JSXはHTMLではありません

JSXはブラウザに認識されません。通常のブラウザコンソールでButton関数を実行しようとすると、JSXパートの最初の文字について文句を言います。

ブラウザが理解するのは(Reactライブラリが含まれている場合)、React.createElementAPI呼び出しです。同じボタンの例は、JSXを使用せずに次のように記述できます。

// jsdrops.com/bx2
機能ボタン(小道具){
  React.createElement(
    "ボタン"、
    {type: "submit"}、
    props.label
  );
}
ReactDOM.render(
  React.createElement(Button、{label: "Save"})、
  mountNode
);

このようにReactを使用できます(JSXなし)。 (Reactライブラリーをロードした後)ブラウザーで直接Button関数を実行すると、問題なく動作します。ただし、関数呼び出しを処理するのではなく、HTMLを表示して処理したいのです。 JavaScriptのみを使用してHTMLを使用しないWebサイトを最後に構築したのはいつですか?望むならできますが、誰もそれをしません。それがJSXが存在する理由です。

JSXは基本的に妥協です。 React.createElement構文を使用してReactコンポーネントを記述する代わりに、HTMLに非常に類似した構文を使用し、コンパイラを使用してそれをReact.createElement呼び出しに変換します。

ある形式の構文を別の形式に変換するコンパイラーは、「トランスパイラー」と呼ばれます。 JSXを翻訳するには、BabelやTypeScriptなどのトランスパイラーを使用できます。たとえば、jsCompleteプレイグラウンドはTypeScriptを使用して、そこに配置したJSXをトランスパイルします。 create-react-appを使用すると、生成されたアプリは内部的にBabelを使用してJSXをトランスパイルします。

babeljs.io/repl/を使用して、React用に変換されたJSX構文を確認できますが、JSXを単独で使用することもできます。これは、React専用のものではありません。

したがって、Reactコンポーネントは、React要素を返すJavaScript関数です(通常はJSXを使用)。 JSXを使用すると、

名前は大文字で始まる必要があります

コンポーネントに「ボタン」という名前を付けたことに注意してください。最初の文字が大文字であることは、実際には要件です。これは、HTML要素とReact要素の混合を扱うからです。 JSXコンパイラ(Babelなど)は、小文字で始まるすべての名前をHTML要素の名前と見なします。 HTML要素は文字列としてReact.createElement呼び出しに渡されるのに対し、React要素は変数として渡す必要があるため、これは重要です。

先に進み、「ボタン」ではなくReactコンポーネントに「ボタン」という名前を付けて、ReactDOMが関数を完全に無視し、通常の空のHTMLボタン要素をレンダリングする方法を確認してください。

// jsdrops.com/bx3
// 違う:
機能ボタン(){
  return 
My Fancy Button ; };
//以下はHTMLボタンをレンダリングします
//(および派手なボタン機能を無視します)
ReactDOM.render(

最初の引数は「props」のオブジェクトです

HTML要素にidやtitleなどの属性を割り当てることができるように、React要素もレンダリング時に属性のリストを受け取ることができます。上記のButton要素(jsdrops.com/bx2)はlabel属性を受け取りました。 Reactでは、React要素が受け取る属性のリストは小道具として知られています。 React関数コンポーネントは、このリストを最初の引数として受け取ります。リストは、属性名を表すキーと、割り当てられた値を表す値を持つオブジェクトとして渡されます。

関数コンポーネントを使用する場合、属性のリストを保持するオブジェクトに「props」という名前を付ける必要はありませんが、これは標準的な方法です。以下で行うクラスコンポーネントを使用する場合、同じ属性リストには常にpropsという名前の特別なインスタンスプロパティが表示されます。

小道具の受け取りはオプションです。一部のコンポーネントにはプロップがありません。ただし、コンポーネントの戻り値はオプションではありません。 Reactコンポーネントは「明示的または暗黙的に」「未定義」を返すことはできません。値を返す必要があります。 「null」を返すと、レンダラーはその出力を無視します。

コンポーネントの小道具(または状態)を使用するたびに、オブジェクトの構造化を使用するのが好きです。たとえば、ボタンコンポーネント関数は、propsを破壊して次のように記述できます。

constボタン=({ラベル})=>(
  

このアプローチには多くの利点がありますが、最も重要なのは、コンポーネントで使用されている小道具を視覚的に検査し、コンポーネントが不要な小道具を受け取らないようにすることです。

通常の矢印関数の代わりに矢印関数を使用したことに注意してください。これは個人的にはスタイルの好みです。一部の人々は通常の関数スタイルを好むが、それは何も悪いことではない。重要なのはあなたが選んだスタイルと一致することだと思います。ここでは矢印関数を使用しますが、これを要件として解釈することはしません。

JSXの式

JSX内のどこにでも中括弧のペアを使用してJavaScript式を含めることができます。

// jsdrops.com/bx4
const RandomValue =()=>(
  
    {Math.floor(Math.random()* 100)}    );
ReactDOM.render(、mountNode);

これらの中括弧内には式のみを含めることができます。たとえば、正規のif文を含めることはできませんが、3項式は問題ありません。値を返すものはすべて大丈夫です。いつでも関数に任意のコードを入れて、何かを返すようにして、中括弧内でその関数を呼び出すことができます。ただし、これらの中括弧に入れたロジックは最小限に抑えてください。

JavaScript変数も式であるため、コンポーネントが小道具のリストを受け取ると、中括弧内でこれらの小道具を使用できます。これが、ボタンの例で{label}を使用した方法です。

JavaScriptオブジェクトリテラルも式です。 JavaScriptオブジェクトを中括弧内で使用することもありますが、これは二重中括弧のように見えます:{{a:42}}。これは別の構文ではありません。これは、通常のJSX中括弧内で定義された単なるオブジェクトリテラルです。

たとえば、これらの中括弧でオブジェクトリテラルを使用する1つのユースケースは、CSSスタイルオブジェクトをReactの特別なスタイル属性に渡すことです。

// jsdrops.com/bx5
const ErrorDisplay =({メッセージ})=>(
  
    {メッセージ}    );
ReactDOM.render(
  、
  mountNode
);

上記のスタイル属性は特別なものです。オブジェクトをその値として使用し、そのオブジェクトは、JavaScript DOM API(キャメルケースのプロパティ名、文字列値)を介してスタイルを設定するかのようにスタイルを定義します。 Reactは、これらのスタイルオブジェクトをインラインCSSスタイル属性に変換します。これはReactコンポーネントのスタイルを設定する最良の方法ではありませんが、要素に条件付きスタイルを適用する際に使用すると非常に便利だと思います。たとえば、約半分の時間でテキストを緑または赤でランダムに出力するコンポーネントを次に示します。

// jsdrops.com/bx6
クラスConditionalStyleはReact.Componentを拡張します{
  render(){
    リターン(
      
        いかがですか?            );   } }
ReactDOM.render(
  、
  mountNode、
);

このスタイリングのロジックは、コンポーネント内にあります。私はすきです!これは、クラス名を条件付きで使用してから、そのクラス名が何をしているかをグローバルCSSスタイルシートで追跡するよりも簡単です。

JSXはテンプレート言語ではありません

HTMLを処理する一部のライブラリは、HTML用のテンプレート言語を提供します。動的ビューは、ループと条件付きの「強化された」HTML構文で記述します。これらのライブラリは、JavaScriptを使用してテンプレートをDOM操作に変換します。その後、ブラウザでDOM操作を使用して、拡張HTMLで記述されたDOMツリーを表示できます。

Reactはそのステップを排除しました。 Reactアプリケーションのテンプレートをブラウザにまったく送信しません。 React APIで記述されたオブジェクトのツリーを送信しました。 Reactはこれらのオブジェクトを使用して、目的のDOMツリーを表示するために必要なDOM操作を生成します。

HTMLテンプレートを使用すると、ライブラリはアプリケーションを文字列として解析します。 Reactアプリケーションは、オブジェクトのツリーとして解析されます。

JSXはテンプレート言語のように見えますが、実際はそうではありません。それは、HTMLテンプレートのような構文でReactのオブジェクトツリーを表現できるようにするJavaScript拡張機能です。ブラウザはJSXをまったく処理する必要がなく、ReactもJSXを処理する必要はありません!コンパイラーのみが行います。ブラウザに送信するのは、テンプレートとJSXフリーのコードです。

たとえば、上で見たtodos配列の場合、テンプレート言語を使用してUIでその配列を表示するには、次のようにする必要があります。

      <仕事リストの各仕事の%%>     
  • <%= todo.body%>   <%END FOR%>
<%%>は、動的に強化された部分を表す1つの構文です。 {{}}構文も表示される場合があります。一部のテンプレート言語は、拡張ロジックに特別な属性を使用します。一部のテンプレート言語は、空白インデントを使用します(オフサイドルール)。

todos配列に変更が生じた場合(テンプレート言語でDOMにレンダリングされたものを更新する必要がある場合)、そのテンプレートを再レンダリングするか、DOMツリーのどこにtodos配列の変更を反映する必要があるかを計算する必要があります。

Reactアプリケーションでは、テンプレート言語はまったくありません。代わりに、JSXを使用します。

      {todos.map(todo =>     
  • {todo.body}   )}

ブラウザで使用される前に、次のように翻訳されます:

React.createElement(
  「ul」、
  ヌル、
  todos.map(todo =>
    React.createElement( "li"、null、todo.body)
  )、
);

Reactはこのオブジェクトのツリーを取得し、それをDOM要素のツリーに変換します。私たちの観点からは、このツリーで完了です。私たちはそれに対するアクションを管理しません。 todos配列自体でアクションを管理するだけです。

クラスを使用してコンポーネントを作成する

Reactは、JavaScriptクラス構文によるコンポーネントの作成もサポートしています。クラス構文で記述された同じButtonコンポーネントの例を次に示します。

// jsdrops.com/bx7
クラスButton React.Componentを拡張します{
  render(){
    リターン(
      
//使用します(同じ構文)
ReactDOM.render(

この構文では、ReactトップレベルAPIのメインクラスの1つであるReact.Componentを拡張するクラスを定義します。クラスベースのReactコンポーネントは、少なくともrenderという名前のインスタンスメソッドを定義する必要があります。このrenderメソッドは、コンポーネントからインスタンス化されたオブジェクトの出力を表す要素を返します。 (をレンダリングすることにより)Buttonクラスベースのコンポーネントを使用するたびに、Reactはこのクラスベースのコンポーネントからオブジェクトをインスタンス化し、そのオブジェクトの表現を使用してDOM要素を作成します。また、DOMでレンダリングされた要素を、クラスから作成されたインスタンスに関連付けます。

レンダリングされたJSX内でthis.props.labelをどのように使用したかに注意してください。すべてのコンポーネントは、インスタンス化されたときにそのコンポーネントの要素に渡されるすべての値を保持するpropsという名前の特別なインスタンスプロパティを取得します。関数コンポーネントとは異なり、クラスベースのコンポーネントのレンダリング関数は引数を受け取りません。

関数とクラス

Reactで制限されていた関数で作成されたコンポーネント。コンポーネントを「ステートフル」にする唯一の方法は、クラス構文を使用することでした。これは、2019年初頭にリリースされたReactバージョン16.8以降の「React Hooks」のリリースで変更されました。Reactフックリリースでは、関数コンポーネントをステートフルにする(および他の多くの機能を提供する)新しいAPIが導入されました。

この新しいAPIを使用すると、Reactで通常行われることのほとんどを関数で行うことができます。クラスベースの構文は、高度で非常にまれな場合にのみ必要です。

新しいAPIが古いAPIを徐々に置き換えていくと思いますが、それだけではありません(可能な場合のみ)。

大規模なアプリケーションで両方のAPIを使用しましたが、新しいAPIは多くの理由で古いAPIよりもはるかに優れていると言えますが、私が個人的に最も重要だと思うものは次のとおりです。

  • クラスの「インスタンス」とその暗黙的な状態を操作する必要はありません。各レンダリングで更新される単純な関数を使用します。状態は明示的に宣言され、何も隠されていません。これはすべて、基本的に、コードでの驚きが少なくなることを意味します。
  • 関連するステートフルロジックをグループ化し、自己完結型の構成可能および共有可能なユニットに分けることができます。これにより、複雑なコンポーネントをより小さなパーツに簡単に分割できます。また、コンポーネントのテストが容易になります。
  • コンポーネントツリーで階層的な「ネスト」を使用する必要なく、宣言的な方法でステートフルロジックを使用できます。

近い将来、クラスベースのコンポーネントはReactの一部になりますが、エコシステムの新規参入者として、純粋に機能(およびフック)から始めて、新しいAPIの学習に集中することは理にかなっていると思います(ただし、既にクラスを使用しているコードベースで作業する必要があります)。

コンポーネントと要素
Reactガイドとチュートリアルでは、「コンポーネント」と「要素」という言葉が混同されている場合があります。 React学習者は重要な違いを理解する必要があると思います。
Reactコンポーネントはテンプレートです。青写真。グローバルな定義。これは、関数またはクラス(レンダーメソッドを使用)のいずれかです。
React Elementは、コンポーネントから返されるものです。コンポーネントが表すDOMノードを仮想的に記述するオブジェクトです。関数コンポーネントの場合、この要素は関数が返すオブジェクトであり、クラスコンポーネントの場合、要素はコンポーネントのrenderメソッドが返すオブジェクトです。 React要素は、ブラウザーに表示されるものではありません。それらはメモリ内のオブジェクトにすぎず、変更することはできません。
Reactはオブジェクトを内部的に作成、更新、破棄して、ブラウザーにレンダリングする必要があるDOM要素ツリーを見つけます。クラスコンポーネントを使用する場合、ブラウザでレンダリングされたDOM要素をコンポーネントインスタンスと呼ぶのが一般的です。同じコンポーネントの多くのインスタンスをレンダリングできます。インスタンスは、クラスベースのコンポーネント内で使用する「this」キーワードです。クラスからインスタンスを手動で作成する必要はありません。 Reactの記憶のどこかにあることを覚えておく必要があります。関数コンポーネントの場合、Reactは関数の呼び出しを使用して、レンダリングするDOM要素を決定します。

コンポーネントの利点

「コンポーネント」という用語は、他の多くのフレームワークやライブラリで使用されています。カスタム要素やHTMLインポートなどのHTML5機能を使用して、ネイティブにWebコンポーネントを作成することもできます。

コンポーネントをネイティブで使用する場合でも、Reactのようなライブラリを使用する場合でも、コンポーネントには多くの利点があります。

まず、コンポーネントを使用すると、コードが読みやすくなり、操作しやすくなります。このUIを検討してください。


 

このUIは何を表していますか? HTMLを話す場合は、ここですばやく解析して、「クリック可能な画像です」と言うことができます。このUIをコンポーネントに変換する場合は、ClickableImageという名前を付けるだけです。

物事がより複雑になると、このHTMLの解析が難しくなるため、コンポーネントを使用すると、使い慣れた言語を使用してUIが何を表しているかをすばやく理解できます。より大きな例を次に示します。


  
  
  

実際のHTMLコードを見なくても、このUIが何を表しているかを正確に把握できます。さらに、残りの文字セクションの出力を変更する必要がある場合、どこに行くべきかを正確に知っています。

Reactコンポーネントは、同じアプリケーションで複数のアプリケーションで再利用することもできます。たとえば、ClickableImageコンポーネントの可能な実装は次のとおりです。

const ClickableImage =({href、src})=> {
  リターン(
    
      
    
  );
};

hrefとsrcの両方の小道具に変数があると、このコンポーネントが再利用可能になります。たとえば、このコンポーネントを使用するには、一連の小道具を使用してレンダリングできます。

そして、異なる小道具のセットを使用することでそれを再利用できます:

関数型プログラミングでは、純粋関数の概念があります。これらは基本的に外部の状態から保護されています。同じ入力を与えると、常に同じ出力が得られます。
Reactコンポーネントがその定義以外のものに依存しない(または変更しない)場合(たとえば、グローバル変数を使用しない場合)、そのコンポーネントにも純粋なラベルを付けることができます。純粋なコンポーネントは問題なく再利用される可能性が高くなります。

ビューを表すコンポーネントを作成します。 ReactDOMの場合、定義するReactコンポーネントはHTML DOMノードを表します。上記のClickableImageコンポーネントは、ネストされた2つのHTML要素で構成されていました。

HTML要素は、ブラウザの組み込みコンポーネントと考えることができます。独自のカスタムコンポーネントを使用して、より大きなコンポーネントを作成することもできます。たとえば、検索エンジンのリストを表示するコンポーネントを作成しましょう。

const SearchEngines =()=> {
  リターン(
    
                     ); };

ClickableImageコンポーネントを使用してSearchEnginesコンポーネントを作成した方法に注意してください!

また、変数にデータを抽出し、その変数で動作するように設計することにより、SearchEnginesコンポーネントを再利用可能にすることもできます。

たとえば、次のような形式のデータ配列を導入できます。

constデータ= [
  {href: "http://google.com"、src: "google.png"}、
  {href: "http://bing.com"、src: "bing.png"}、
  {href: "http://yahoo.com"、src: "yahoo.png"}
];

次に、を機能させるために、オブジェクトのリストのデータ配列をClickableImageコンポーネントのリストにマッピングします。

const SearchEngines =({エンジン})=> {
  リターン(
    <リスト>
      {engines.map(engine => )}
    
  );
};
ReactDOM.render(
 、
 document.getElementById( "mountNode")
);

このSearchEnginesは、Googleが提供する検索エンジンの任意のリストで機能します。

フックとは正確には何ですか?

Reactコンポーネントのフックは、特別な関数の呼び出しです。すべてのフック関数は、「use」という単語で始まります。それらの一部は、ステートフルな要素(useStateなど)を備えた関数コンポーネントを提供するために使用でき、その他はuseEffectなどの副作用の管理や、useCallbackなどの関数とオブジェクトのキャッシュ/メモ化に使用できます。フックは非常に強力であり、フックでできることに関しては空が限界です。

Reactフック関数は、関数コンポーネントでのみ使用できます。クラスコンポーネントでは使用できません。

基本的なuseStateフックの例を見るために、上記のButtonコンポーネントがクリックイベントに応答するようにします。 「count」変数でクリックされる回数を維持し、その変数の値を、レンダリングするボタンのラベルとして表示しましょう。

constボタン=()=> {
  let count = 0;
  リターン(
    
ReactDOM.render(

このカウント変数は、この例に導入する必要がある状態要素になります。これはUIが依存するデータの一部であり(表示しているため)、時間とともに変化するため状態要素です。

コードで変数を定義するたびに状態を導入し、変数の値を変更するたびにその状態を変更します。心に留めておきます。

カウント状態の値を変更する前に、イベントについて学習する必要があります。

ユーザーイベントへの応答

「onEvent」プロパティを持つイベントハンドラーを追加できます(この場合、ボタン要素に)。これは、onClick、onMouseOver、onScroll、onSubmitなどです。

ここで必要なのはonClickイベントで、ターゲット要素の属性として定義するだけです。たとえば、ボタンがクリックされるたびにプログラムがコンソールにメッセージを記録するようにするには、次のようにします。

constボタン=()=> {
  let count = 0;
  リターン(
    
ReactDOM.render(

DOMバージョンのonClick属性(文字列を使用)とは異なり、ReactのonClick属性は関数参照を使用します。中かっこを指定します。

関数func(){}

onClickハンドラとしてfunc参照(名前)を渡したことに注意してください。そこではfuncを呼び出しませんでした。ボタンがクリックされると、Reactはfuncを呼び出します。

上記のButtonコンポーネントのonClickイベントでは、呼び出されたときにコンソールにメッセージを出力する関数定義を「インライン化」しました。ボタンをクリックするたびに、onClickハンドラー(インライン矢印関数)が呼び出され、そのメッセージが表示されます。

イベント名がキャメルケースであることに注意してください。すべてのDOM関連属性(Reactによって処理される)はキャメルケースである必要があります(そうでない場合、Reactはエラーを表示します)。 Reactは、カスタムHTML属性の使用もサポートしており、それらはすべて小文字の形式である必要があります。
Reactの一部のDOM属性は、通常のDOM APIで行うものとは若干異なります。その例は、onChangeイベントです。通常のブラウザでは、通常、フォームフィールドの外側をクリックする(またはタブを外す)と起動します。 Reactでは、フォームフィールドの値が変更されるたびに(追加/削除されるすべての文字で)onChangeが発生します。
Reactの一部の属性は、対応するHTMLとは異なる名前が付けられています。その例は、ReactのclassName属性です。これは、HTMLのclass属性を使用するのと同等です。 React属性とDOM属性の違いの完全なリストについては、jscomplete.com / react-attributesを参照してください。

状態の読み取りと更新

状態の更新を追跡し、仮想DOM差分と実際のDOM調整をトリガーするには、Reactは、コンポーネント内で使用される状態要素に発生する変更を認識する必要があります。これを効率的な方法で行うために、Reactでは、コンポーネントに導入する各状態要素に対して特別なゲッターとセッターを使用する必要があります。ここでuseStateフックが役立ちます。状態要素を定義し、そのゲッターとセッターを返します!

実装しようとしているcount state要素に必要なものは次のとおりです。

const [count、setCount] = React.useState(0);

useState関数は、正確に2つの項目を持つ配列を返します。最初の項目は値(ゲッター)で、2番目の項目は関数(セッター)です。これらのアイテムに名前を付けるために、配列の構造化を使用しました。任意の名前を付けることができますが、[name、setName]が慣例です。

最初の項目「値」には、文字列、数値、配列、またはその他のタイプを指定できます。この場合、数値が必要であり、その数値を0で初期化する必要がありました。React.useStateへの引数は、状態要素の初期値として使用されます。

2番目の項目「関数」は、呼び出されると状態要素の値を変更します(必要に応じてDOM処理をトリガーします)。 setCount関数が呼び出されるたびに、ReactはButtonコンポーネントを再レンダリングし、コンポーネントで定義されたすべての変数(カウント値を含む)を更新します。 setCountに渡す引数は、countの新しい値になります。

ボタンのラベルをインクリメントするには、onClickイベント内でsetCount関数を呼び出し、1ずつインクリメントされた現在のカウント値を渡す必要があります。ラベルをインクリメントするボタンの例の完全なコードは次のとおりです。

constボタン=()=> {
  const [count、setCount] = useState(0);
  リターン(
    
ReactDOM.render(

さあ、それをテストしてください。ボタンは、クリックするたびにラベルを増やします。

UI自体を変更するアクションを実装しなかったことに注意してください。代わりに、JavaScriptオブジェクト(メモリ内)を変更するアクションを実装しました! UIの実装は基本的に、ボタンのラベルにcountobjectの値を常に反映させることをReactに伝えていました。私たちのコードはDOMの更新を行いませんでした。反応しました。

また、constキーワードを使用してカウントを定義した方法にも注意してください。これは変更される値です。コードはその値を変更しません。 Reactは、Button関数の新しい呼び出しを使用して、新しい状態のUIをレンダリングする場合になります。その新しい呼び出しで、useState関数呼び出しは新しい新しいカウント値を提供します。

useState関数は、プレイグラウンドでグローバルに使用できます。これは、React.useStateの単なるエイリアスです。コードでは、名前付きインポートを使用してuseStateをモジュールのスコープで直接使用できます。
import React、{useState} from 'react';

この力を理解するには、さらにいくつかの例が必要です。この基本的な例にいくつかの機能を追加しましょう。多くのボタンを用意し、すべてのボタンで1つの共有カウント値を増やします。

複数のコンポーネントの使用

これまでのButtonコンポーネントを2つのコンポーネントに分割しましょう。

  • Buttonコンポーネントを保持して、ボタン要素を表しますが、静的ラベルを使用します。
  • 新しいディスプレイコンポーネントを追加して、カウントの値を表示します。

新しいDisplayコンポーネントは、それ自体の状態や相互作用のない純粋にプレゼンテーション用のものです。それは正常です。すべてのReactコンポーネントがステートフルフックを持っている必要はなく、インタラクティブである必要もありません。

const Display =(props)=>(
  
 COUNT VALUE HERE ... 
);

Displayコンポーネントの責任は、小道具として受け取る値を単に表示することです。たとえば、pre要素が値をホストするために使用されたという事実は、その責任の一部です。このアプリケーションの他のコンポーネントは、それについて何も言いません!

兄弟コンポーネントのレンダリング

これで、レンダリングする2つの要素、ボタンと表示があります。次のように、それらを直接隣り合わせにレンダリングすることはできません。

//これは機能しません
ReactDOM.render(

JSXが変換されると、各要素は関数呼び出しに変換されるため、Reactではこのように隣接する要素をレンダリングできません。この問題に対処するには、いくつかのオプションがあります。

まず、要素の配列をReactDOM.renderに渡し、必要な数のReact要素をその配列に挿入できます。

オプション1

ReactDOM.render([

これは通常、レンダリングするすべての要素が動的ソースからのものである場合に適したソリューションです。ただし、ここで行っているケースには理想的ではありません。

別のオプションは、兄弟React要素を別のReact要素の子にすることです。たとえば、それらをdiv要素で囲むだけです。

オプション#2

ReactDOM.render(
  
    <ボタン/>     <表示/>   、   mountNode );

React APIはこのネストをサポートしています。実際、新しいDOM親ノードを導入せずに、このような複数の隣接する要素を囲む必要がある場合、Reactには特別なオブジェクトがあります。 React.Fragmentを使用できます:

オプション#3

ReactDOM.render(
  
    <ボタン/>
    <表示/>
  、
  mountNode
);

このケースはReactでは非常に一般的であるため、JSX拡張機能にはショートカットがあります。 React.Fragmentと入力する代わりに、空のタグ<> を使用できます。

オプション#3 +

ReactDOM.render(
  <>
    <ボタン/>
    <表示/>
  、
  mountNode
);

空のタグは、React.Fragment構文に変換されます。この構文を使用して、例を続行します。

ただし、ReactDOM.renderの最初の引数は、先ほど行ったネストされたツリーではなく、単一のコンポーネント呼び出しを常に行うようにしてください。これは基本的にコード品質の設定です。コンポーネントの階層、名前、および関係について考えるように強制されます。次はそれをしましょう。

トップレベルのコンポーネント

ButtonコンポーネントとDisplayコンポーネントの両方をホストするトップレベルコンポーネントを紹介しましょう。ここでの質問は、この新しい親コンポーネントに何を命名するべきかということです。

信じられないかもしれませんが、コンポーネントとそのstate / props要素に名前を付けることは、これらのコンポーネントが機能し実行する方法に影響を与える非常に難しいタスクです。適切な名前を付けると、適切な設計上の決定が行われます。少し時間をかけて、Reactアプリに紹介するすべての新しい名前について考えてください。

この新しい親コンポーネントは、表示されるカウントをインクリメントするボタンを持つディスプレイをホストするため、カウント値マネージャーと考えることができます。 CountManagerと名付けましょう。

const CountManager =()=> {
  リターン(
    <>
      <ボタン/>
      <表示/>
    
  );
};
ReactDOM.render(、mountNode);

新しいディスプレイコンポーネントにカウントの値を表示するため、ボタンのラベルとしてカウントの値を表示する必要がなくなりました。代わりに、ラベルを「+1」などに変更できます。

constボタン=()=> {
  リターン(
    

ボタンコンポーネントから状態要素を削除したことに注意してください。新しい要件では、ButtonコンポーネントとDisplayコンポーネントの両方がcount状態要素にアクセスする必要があります。 Displayコンポーネントはそれを表示し、Buttonコンポーネントはそれを更新します。コンポーネントが、その兄弟コンポーネントが所有する状態要素にアクセスする必要がある場合、1つの解決策は、その状態要素を1レベル上に「持ち上げ」、親コンポーネント内で定義することです。この場合、親は導入したばかりのCountManagerコンポーネントです。

状態をCountManagerに移動することにより、コンポーネントプロパティを使用して親から子にデータを「フロー」できます。 Displayコンポーネントにカウント値を表示するには、次のようにします。

const Display =({content})=>(
  
 {content} 
);
const CountManager =()=> {
  const [count、setCount] = useState(0);
  リターン(
    <>
      <ボタン/>
      
    
  );
};
ReactDOM.render(、mountNode);

CountManagerで、ボタンコンポーネントとまったく同じuseState行を使用したことに注意してください。同じ状態要素を持ち上げています。また、小道具を介してディスプレイコンポーネントにカウント値を流したときに、別の名前(コンテンツ)を使用したことに注意してください。それは正常です。まったく同じ名前を使用する必要はありません。実際、場合によっては、新しい汎用名を導入すると、子コンポーネントが再利用可能になるため、子コンポーネントの方が適しています。 Displayコンポーネントを再利用して、count以外の数値を表示できます。

また、親コンポーネントは子に振る舞いを下げることができます。これは次に行う必要があります。

count state要素はCountManagerコンポーネントにあるため、更新を処理するためにそのレベルの関数が必要です。この関数にincrementCounterという名前を付けましょう。この関数のロジックは、実際には、ButtonコンポーネントのhandleClick関数で以前と同じロジックです。新しいincrementCounter関数は、CountManagerコンポーネントのカウント状態を更新して、前の値を使用して値をインクリメントします。

const CountManager =()=> {
  // ....
  const incrementCounter =()=> {
    setCount(count + 1);
  }
  // ...
};

ButtonコンポーネントのonClickハンドラーを変更する必要があります。 CountManagerコンポーネントにあるincrementCounter関数を実行したいのですが、コンポーネントは自分の関数にしかアクセスできません。したがって、ButtonコンポーネントがCountManagerコンポーネントのincrementCounter関数を呼び出せるようにするために、propertyとしてincrementCounterへの参照をButtonコンポーネントに渡すことができます。はい、小道具はデータだけでなく機能も保持できます。関数はJavaScriptのオブジェクトであり、オブジェクトを渡すことができるオブジェクトと同じです。

この新しい小道具には何でも名前を付けることができます。 clickActionという名前を付け、incrementCounterの値を渡します。これは、CountManagerコンポーネントで定義した関数への参照です。この新しい継承された動作を、onClickハンドラ値として直接使用できます。 Buttonコンポーネントの小道具になります。

const Button =({clickAction})=> {
  リターン(
    
// ...
const CountManager =()=> {
  // ...
  リターン(
    
      

ここで何か非常に強力なことが起こっています。このclickActionプロパティにより、ButtonコンポーネントはCountManagerコンポーネントのincrementCounter関数を呼び出すことができました。そのボタンをクリックすると、ButtonコンポーネントがCountManagerコンポーネントにアクセスして、「親よ、先に進んでそのインクリメントカウンターの動作を今すぐ呼び出してください」と言っているようなものです。

実際には、CountManagerコンポーネントがここで制御され、Buttonコンポーネントは一般的な規則に従っています。現在のコードを分析すると、ボタンコンポーネントがクリックされたときに何が起こるかについての手掛かりがないことがわかります。親によって定義されたルールに従い、一般的なclickActionを呼び出します。親は、その一般的な動作を制御します。これは、責任分離の概念に従います。ここの各コンポーネントには特定の責任があり、それに焦点を合わせます。

別の例については、Displayコンポーネントをご覧ください。その観点から、カウント値は状態ではありません。 CountManagerコンポーネントが渡すのは単なる小道具です。 Displayコンポーネントは、常にその小道具を表示します。これも責任の分離です。

これらのコンポーネントの設計者として、責任レベルを選択できます。たとえば、CountManagerコンポーネント自体のカウント値部分を表示し、そのために新しいDisplayコンポーネントを使用しないようにすることができます。

CountManagerコンポーネントには、カウント状態を管理する責任があります。それは私たちが行った重要な設計上の決定であり、Reactアプリケーションで多くのことをしなければならないものです。状態を定義する場所

私が従うプラクティスは、その状態要素にアクセスする必要があるすべての子にできるだけ近い共有された親ノードに状態要素を定義することです。このような小さなアプリケーションの場合、通常はトップレベルのコンポーネント自体を意味します。大規模なアプリケーションでは、サブツリーは、トップレベルのルートコンポーネントで定義されているグローバルな状態要素に依存するのではなく、独自の状態「ブランチ」を管理する場合があります。

トップレベルコンポーネントは、他のすべてのコンポーネントの親であるため、共有アプリケーションの状態とアクションを管理するために広く使用されています。最上位コンポーネントの状態要素を更新すると、コンポーネントのツリー全体が(メモリ内で)再レンダリングされるため、この設計に注意してください。

これまでのこの例の完全なコードは次のとおりです。

// jsdrops.com/bx8
const Button =({clickAction})=> {
  リターン(
    
const Display =({content})=>(
  
 {content} 
);
const CountManager =()=> {
  const [count、setCount] = useState(0);
  const incrementCounter =()=> {
    setCount(count + 1);
  };
  リターン(
    
      

コンポーネントを再利用可能にする

コンポーネントはすべて再利用性に関するものです。 Buttonコンポーネントを変更して、1だけでなく任意の値でグローバルカウントをインクリメントできるように変更してみましょう。

この新しい機能をテストできるように、CountManagerコンポーネントにさらにButton要素を追加することから始めましょう。

const CountManager =()=> {
  // ..
  リターン(
    <>
      

上記でレンダリングされたすべてのButton要素には現在+1ラベルがあり、カウントが1ずつ増加します。各ボタンに固有の異なるラベルを表示し、特定の値に基づいて異なるアクションを実行するようにします。それらのそれぞれに。 Reactエレメントにプロップとして任意の値を渡すことができることに注意してください。

各ボタンを1回クリックした後、私が念頭に置いているUIは次のとおりです。

上記のスクリーンショットでは、カウントは0から始まりました。1、5、10を追加して16になりました

この演習を行う前に、時間をかけて考えてみて、自分で実装してみてください。それはほとんど簡単です。ヒント:Buttonの新しい小道具を1つ導入する必要があります。試してみます。ソリューションを自分のものと比較する準備ができたら、戻ってください。

新しい小道具を追加する

最初に行う必要があるのは、Buttonコンポーネントの+1ラベルをカスタマイズ可能なラベルにすることです。

Reactコンポーネントでカスタマイズ可能なものを作成するために、新しいプロップ(親コンポーネントが制御できる)を導入し、コンポーネントがその値を使用するようにします。この例では、Buttonコンポーネントに、インクリメントする量(1、5、10)を新しい小道具として受け取らせることができます。 clickValueという名前を付けます。 CountManagerのrenderメソッドを変更して、テストする値をこの新しいプロパティに渡すことができます。

リターン(
  <>
    

これまでのところ、このコードに関するいくつかのことに注意してください。

  • カウントに関連するもので新しいプロパティに名前を付けませんでした。 Buttonコンポーネントは、クリックイベントの意味を認識する必要はありません。クリックイベントがトリガーされたときに、このclickValueを渡すだけです。たとえば、この新しいプロパティにcountValueという名前を付けるのは最良の選択ではありません。これは、Buttonコンポーネントで、ボタン要素がカウントに関連していることを理解するためにコードを読み取るためです。これにより、Buttonコンポーネントの再利用性が低下します。たとえば、同じButtonコンポーネントを使用して文字列に文字を追加する場合、そのコードは混乱を招きます。
  • 中括弧を使用して、新しいclickValueプロパティの値を渡します(clickValue = {5})。そこで文字列を使用しませんでした(clickValue = "5")。 (ボタンがクリックされるたびに)これらの値を処理する数学的な操作があるため、これらの値は数値である必要があります。それらを文字列として渡す場合、追加操作を実行するときに文字列から数値への変換を行う必要があります。
数字を文字列として渡すことは、Reactでよくある間違いです。 React関連の一般的な間違いについては、この記事をご覧ください。

動作のカスタマイズ

CountManagerコンポーネントでジェネリックにする必要があるもう1つのことは、incrementCounterアクション関数です。現在のように、ハードコードされたcount + 1演算を持つことはできません。 Buttonコンポーネントに対して行ったのと同様に、関数をジェネリックにするために、引数を受け取り、その引数の値を使用します。例えば:

incrementCounter =(incrementValue)=> {
  setCount(count + incrementValue);
};

ここで必要なのは、ButtonコンポーネントでラベルとしてclickValueプロパティを使用し、clickValueを引数としてonClickアクションを呼び出すようにすることだけです。

const Button =({clickValue、clickAction})=> {
  リターン(
    

onClickプロップをボタンのclickValueにバインドするためにインライン矢印関数でラップする必要があることに注意してください。この新しい矢印関数のJavaScriptクロージャーがそれを処理します。

3つのボタンは、3つの異なるクリック値で増加するはずです。この例のコードはjsdrops.com/bx9で見ることができます。

ユーザーからの入力を受け入れる

Twitterのツイートフォームのように、ユーザーがテキスト領域に入力する文字をカウントする必要があるとします。ユーザーが入力する文字ごとに、新しい文字数でUIを更新する必要があります。

以下は、文字数のプレースホルダーdivを持つtextarea入力要素を表示するコンポーネントです。

// jsdrops.com/bx10
const CharacterCounter =()=> {
  リターン(