著者は、 Creative Commons を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

React では、 state は、アプリケーション内でデータが時間の経過とともにどのように変化するかを追跡する構造を指します。 状態の管理は、インタラクティブなコンポーネントや動的なWebアプリケーションを作成できるため、Reactの重要なスキルです。 状態は、フォーム入力の追跡からAPIからの動的データのキャプチャまですべてに使用されます。 このチュートリアルでは、クラスベースのコンポーネント状態を管理する例を実行します。

このチュートリアルの執筆時点で、公式の Reactドキュメントは、開発者が新しいコードを作成するときに、機能コンポーネントを使用するのではなく、Reactフックを採用して状態を管理することを推奨しています。 クラスベースのコンポーネント。 React Hooksの使用はより現代的な方法と考えられていますが、クラスベースのコンポーネントの状態を管理する方法を理解することも重要です。 状態管理の背後にある概念を学ぶことは、既存のコードベースでクラスベースの状態管理をナビゲートおよびトラブルシューティングするのに役立ち、クラスベースの状態管理がより適切である場合を決定するのに役立ちます。 componentDidCatch と呼ばれるクラスベースのメソッドもありますが、これはフックでは使用できず、クラスメソッドを使用して状態を設定する必要があります。

このチュートリアルでは、最初に静的な値を使用して状態を設定する方法を示します。これは、古い値をオーバーライドするAPIからのデータの設定など、次の状態が最初の状態に依存しない場合に役立ちます。 次に、状態を現在の状態として設定する方法について説明します。これは、値の切り替えなど、次の状態が現在の状態に依存する場合に役立ちます。 状態を設定するこれらのさまざまな方法を調べるために、オプションのリストから購入を追加することによって更新する製品ページコンポーネントを作成します。

前提条件

  • Node.jsを実行する開発環境が必要になります。 このチュートリアルは、Node.jsバージョン10.20.1およびnpmバージョン6.14.4でテストされました。 これをmacOSまたはUbuntu18.04にインストールするには、Node.jsをインストールしてmacOSにローカル開発環境を作成する方法またはPPAを使用したインストール]セクションの手順に従います。 Ubuntu18.04にNode.jsをインストールする方法。

  • このチュートリアルでは、 Create ReactAppを使用してアプリを作成します。 Create React Appを使用してアプリケーションをインストールする手順については、 Create ReactAppを使用してReactプロジェクトをセットアップする方法を参照してください。

  • また、 JavaScriptでコーディングする方法にあるJavaScriptの基本的な知識と、HTMLおよびCSSの基本的な知識も必要です。 HTMLとCSSの優れたリソースは、 Mozilla DeveloperNetworkです。

ステップ1—空のプロジェクトを作成する

このステップでは、 Create ReactAppを使用して新しいプロジェクトを作成します。 次に、プロジェクトをブートストラップするときにインストールされるサンプルプロジェクトと関連ファイルを削除します。 最後に、コンポーネントを整理するための単純なファイル構造を作成します。 これにより、クラスベースのコンポーネントの状態を管理するためのこのチュートリアルのサンプルアプリケーションを構築するための強固な基盤が得られます。

まず、新しいプロジェクトを作成します。 ターミナルで次のスクリプトを実行して、を使用して新しいプロジェクトをインストールします create-react-app:

  1. npx create-react-app state-class-tutorial

プロジェクトが終了したら、次のディレクトリに移動します。

  1. cd state-class-tutorial

新しいターミナルタブまたはウィンドウで、 CreateReactApp開始スクリプトを使用してプロジェクトを開始します。 ブラウザは変更時に自動更新されるため、作業中はこのスクリプトを実行したままにします。

  1. npm start

実行中のローカルサーバーを取得します。 プロジェクトがブラウザウィンドウで開かなかった場合は、 http:// localhost:3000/でプロジェクトを開くことができます。 これをリモートサーバーから実行している場合、アドレスは次のようになります。 http://your_domain:3000.

ブラウザには、CreateReactAppの一部として含まれている単純なReactアプリケーションが読み込まれます。

完全に新しいカスタムコンポーネントのセットを構築するので、空のプロジェクトを作成できるように、ボイラープレートコードをクリアすることから始める必要があります。

開始するには、 src/App.js テキストエディタで。 これは、ページに挿入されるルートコンポーネントです。 すべてのコンポーネントはここから始まります。 あなたはについてのより多くの情報を見つけることができます App.js Create ReactAppを使用してReactプロジェクトを設定する方法。

開ける src/App.js 次のコマンドを使用します。

  1. nano src/App.js

次のようなファイルが表示されます。

state-class-tutorial / src / App.js
import React from 'react';
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;

行を削除します import logo from './logo.svg';. 次に、のすべてを置き換えます return 空のタグのセットを返すステートメント: <></>. これにより、何も返さない有効なページが表示されます。 最終的なコードは次のようになります。

state-class-tutorial / src / App.js

import React from 'react';
import './App.css';

function App() {
  return <></>;
}

export default App;

テキストエディタを保存して終了します。

最後に、ロゴを削除します。 アプリケーションで使用することはないので、作業中に未使用のファイルを削除する必要があります。 長期的には混乱からあなたを救うでしょう。

ターミナルウィンドウで、次のコマンドを入力します。

  1. rm src/logo.svg

ブラウザを見ると、空白の画面が表示されます。

サンプルのCreateReactAppプロジェクトをクリアしたので、単純なファイル構造を作成します。 これにより、コンポーネントを分離して独立させることができます。

と呼ばれるディレクトリを作成します components の中に src ディレクトリ。 これにより、すべてのカスタムコンポーネントが保持されます。

  1. mkdir src/components

各コンポーネントには、スタイル、画像、テストとともにコンポーネントファイルを保存するための独自のディレクトリがあります。

のディレクトリを作成します App:

  1. mkdir src/components/App

すべてを移動します App そのディレクトリにファイル。 ワイルドカードを使用して、 *、で始まるファイルを選択します App. ファイル拡張子に関係なく。 次に、 mv それらを新しいディレクトリに配置するコマンド:

  1. mv src/App.* src/components/App

次に、で相対インポートパスを更新します index.js、これはプロセス全体をブートストラップするルートコンポーネントです。

  1. nano src/index.js

importステートメントは、 App.js のファイル App ディレクトリなので、次の強調表示された変更を行います。

state-class-tutorial / src / index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App/App';
import * as serviceWorker from './serviceWorker';

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

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

ファイルを保存して終了します。

プロジェクトが設定されたので、最初のコンポーネントを作成できます。

ステップ2—コンポーネントでの状態の使用

このステップでは、コンポーネントの初期状態をそのクラスに設定し、その状態を参照して値を表示します。 次に、状態値を使用してカート内の合計アイテムを表示するショッピングカートを使用して製品ページを作成します。 ステップの終わりまでに、値を保持するさまざまな方法と、小道具や静的な値ではなく状態を使用する必要がある場合を理解できます。

コンポーネントの構築

のディレクトリを作成することから始めます Product:

  1. mkdir src/components/Product

次に、開きます Product.js そのディレクトリ内:

  1. nano src/components/Product/Product.js

状態のないコンポーネントを作成することから始めます。 コンポーネントには2つの部分があります。アイテムの数と合計価格が表示されるカートと、アイテムを追加および削除するためのボタンが表示される製品です。 今のところ、ボタンにはアクションがありません。

次のコードをに追加します Product.js:

state-class-tutorial / src / components / Product / Product.js
import React, { Component } from 'react';
import './Product.css';

export default class Product extends Component {
  render() {
    return(
      <div className="wrapper">
        <div>
          Shopping Cart: 0 total items.
        </div>
        <div>Total: 0</div>

        <div className="product"><span role="img" aria-label="ice cream">🍦</span></div>
        <button>Add</button> <button>Remove</button>
      </div>
    )
  }
}

あなたはまた、いくつかを含めました div JSX クラス名を持つ要素。これにより、基本的なスタイルを追加できます。

ファイルを保存して閉じてから、開きます Product.css:

  1. nano src/components/Product/Product.css

軽いスタイリングを加えて、 font-size テキストと絵文字の場合:

state-class-tutorial / src / components / Product / Product.css
.product span {
    font-size: 100px;
}

.wrapper {
    padding: 20px;
    font-size: 20px;
}

.wrapper button {
    font-size: 20px;
    background: none;
}

この例では絵文字が製品画像として機能するため、絵文字にはテキストよりもはるかに大きなフォントサイズが必要になります。 さらに、ボタンのデフォルトのグラデーションの背景を削除するには、 backgroundnone.

ファイルを保存して閉じます。

今、レンダリングします Product のコンポーネント App コンポーネントを使用して、ブラウザで結果を確認できます。 開ける App.js:

  1. nano src/components/App/App.js

コンポーネントをインポートしてレンダリングします。 このチュートリアルでは使用しないため、CSSインポートを削除することもできます。

state-class-tutorial / src / components / App / App.js
import React from 'react';
import Product from '../Product/Product';

function App() {
  return <Product />
}

export default App;

ファイルを保存して閉じます。 これを行うと、ブラウザが更新され、次のように表示されます。 Product 成分。

クラスコンポーネントの初期状態の設定

表示で変更されるコンポーネント値には、アイテムの総数とコストの2つの値があります。 それらをハードコーディングする代わりに、このステップでは、それらをオブジェクトと呼ばれるものに移動します。 state.

The state Reactクラスのは、ページのレンダリングを制御する特別なプロパティです。 状態を変更すると、Reactはコンポーネントが古くなっていることを認識し、自動的に再レンダリングします。 コンポーネントが再レンダリングされると、レンダリングされた出力が変更され、最新の情報が含まれるようになります。 state. この例では、商品をカートに追加したり、カートから削除したりするたびに、コンポーネントが再レンダリングされます。 Reactクラスに他のプロパティを追加することはできますが、それらには再レンダリングをトリガーする同じ機能はありません。

開ける Product.js:

  1. nano src/components/Product/Product.js

というプロパティを追加します stateProduct クラス。 次に、2つの値をに追加します state 物体: carttotal. The cart 最終的に多くのアイテムを保持する可能性があるため、配列になります。 The total 数字になります。 これらを割り当てた後、値への参照を次のように置き換えます this.state.property:

state-class-tutorial / src / components / Product / Product.js

import React, { Component } from 'react';
import './Product.css';

export default class Product extends Component {

  state = {
    cart: [],
    total: 0
  }

  render() {
    return(
      <div className="wrapper">
        <div>
          Shopping Cart: {this.state.cart.length} total items.
        </div>
        <div>Total {this.state.total}</div>

        <div className="product"><span role="img" aria-label="ice cream">🍦</span></div>
        <button>Add</button> <button>Remove</button>
      </div>
    )
  }
}

どちらの場合も、JSX内でJavaScriptを参照しているため、コードを中括弧で囲む必要があることに注意してください。 また、を表示しています lengthcart 配列内のアイテム数のカウントを取得するための配列。

ファイルを保存します。 これを行うと、ブラウザが更新され、以前と同じページが表示されます。

The state プロパティは標準クラスのプロパティです。つまり、プロパティだけでなく、他のメソッドでもアクセスできます。 render 方法。

次に、価格を静的な値として表示する代わりに、 toLocaleString メソッドを使用して文字列に変換します。これにより、ブラウザの領域での数値の表示方法に一致する文字列に数値が変換されます。

と呼ばれるメソッドを作成します getTotal() それはかかります state の配列を使用してローカライズされた文字列に変換します currencyOptions. 次に、への参照を置き換えます state JSXでメソッド呼び出しを使用して:

state-class-tutorial / src / components / Product / Product.js
import React, { Component } from 'react';
import './Product.css';

export default class Product extends Component {

  state = {
    cart: [],
    total: 0
  }

  currencyOptions = {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  }

  getTotal = () => {
    return this.state.total.toLocaleString(undefined, this.currencyOptions)
  }

  render() {
    return(
      <div className="wrapper">
        <div>
          Shopping Cart: {this.state.cart.length} total items.
        </div>
        <div>Total {this.getTotal()}</div>

        <div className="product"><span role="img" aria-label="ice cream">🍦</span></div>
        <button>Add</button> <button>Remove</button>
      </div>
    )
  }
}

以来 total 商品の価格です、あなたは通過しています currencyOptions 小数点以下の最大値と最小値を設定します total 2つに。 これは別のプロパティとして設定されていることに注意してください。 多くの場合、初心者のReact開発者は、このような情報を state オブジェクトですが、情報を追加するのは state あなたが変えることを期待していること。 このように、の情報 state アプリケーションの規模が拡大するにつれて、追跡が容易になります。

あなたが行ったもう1つの重要な変更は、 getTotal() 矢印関数をクラスプロパティに割り当てることによるメソッド。 矢印関数を使用しない場合、このメソッドは新しいこのバインディングを作成し、現在の this バインドして、コードにバグを導入します。 これについては、次のステップで詳しく説明します。

ファイルを保存します。 これを行うと、ページが更新され、値が小数に変換されて表示されます。

これで、コンポーネントに状態が追加され、クラスで参照されました。 また、の値にアクセスしました render メソッドおよび他のクラスのメソッド。 次に、状態を更新して動的な値を表示するメソッドを作成します。

ステップ3—静的な値から状態を設定する

これまで、コンポーネントの基本状態を作成し、関数とJSXコードでその状態を参照しました。 このステップでは、製品ページを更新して、 state ボタンのクリックで。 更新された値を含む新しいオブジェクトを、という特別なメソッドに渡す方法を学習します。 setState、次に設定します state 更新されたデータで。

更新するには state、React開発者はと呼ばれる特別な方法を使用します setState それはベースから継承されます Component クラス。 The setState メソッドは、最初の引数としてオブジェクトまたは関数のいずれかを取ることができます。 参照する必要のない静的な値がある場合 state、読みやすいので、新しい値を含むオブジェクトを渡すのが最善です。 現在の状態を参照する必要がある場合は、古いものへの参照を回避するための関数を渡します state.

ボタンにイベントを追加することから始めます。 ユーザーが追加をクリックすると、プログラムはアイテムをに追加します cart を更新します total. 削除をクリックすると、カートが空のアレイにリセットされ、 total0. たとえば、プログラムでは、ユーザーがアイテムを2回以上追加することはできません。

開ける Product.js:

  1. nano src/components/Product/Product.js

コンポーネント内で、という新しいメソッドを作成します add、次にメソッドをに渡します onClick 追加ボタンの小道具:

state-class-tutorial / src / components / Product / Product.js
import React, { Component } from 'react';
import './Product.css';

export default class Product extends Component {

  state = {
    cart: [],
    total: 0
  }

  add = () => {
    this.setState({
      cart: ['ice cream'],
      total: 5
    })
  }

  currencyOptions = {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  }

  getTotal = () => {
    return this.state.total.toLocaleString(undefined, this.currencyOptions)
  }

  render() {
    return(
      <div className="wrapper">
        <div>
          Shopping Cart: {this.state.cart.length} total items.
        </div>
        <div>Total {this.getTotal()}</div>

        <div className="product"><span role="img" aria-label="ice cream">🍦</span></div>
        <button onClick={this.add}>Add</button>
        <button>Remove</button>
      </div>
    )
  }
}

内部 add メソッド、あなたは setState メソッドを実行し、更新されたオブジェクトを含むオブジェクトを渡します cart 単一のアイテムで ice cream との更新された価格 5. 再び矢印関数を使用して作成したことに注意してください。 add 方法。 前に述べたように、これは関数が適切であることを保証します this 更新を実行するときのコンテキスト。 矢印関数を使用せずにメソッドとして関数を追加すると、 setState 関数を現在のコンテキストにバインディングしないと存在しません。

たとえば、 add このように機能します:

export default class Product extends Component {
...
  add() {
    this.setState({
      cart: ['ice cream'],
      total: 5
    })
  }
...
}

追加ボタンをクリックするとエラーが発生します。

矢印関数を使用すると、このエラーを回避するための適切なコンテキストを確保できます。

ファイルを保存します。 これを行うと、ブラウザがリロードされ、追加ボタンをクリックすると、カートが現在の金額で更新されます。

とともに add メソッド、あなたはの両方のプロパティを渡しました state 物体: carttotal. ただし、必ずしも完全なオブジェクトを渡す必要はありません。 更新するプロパティを含むオブジェクトを渡すだけで、他のすべては同じままになります。

Reactが小さなオブジェクトをどのように処理できるかを確認するには、という新しい関数を作成します。 remove. だけを含む新しいオブジェクトを渡します cart 空の配列を使用して、メソッドをに追加します onClick 削除ボタンのプロパティ:

state-class-tutorial / src / components / Product / Product.js
import React, { Component } from 'react';
import './Product.css';

export default class Product extends Component {

  ...
  remove = () => {
    this.setState({
      cart: []
    })
  }

  render() {
    return(
      <div className="wrapper">
        <div>
          Shopping Cart: {this.state.cart.length} total items.
        </div>
        <div>Total {this.getTotal()}</div>

        <div className="product"><span role="img" aria-label="ice cream">🍦</span></div>
        <button onClick={this.add}>Add</button>
        <button onClick={this.remove}>Remove</button>
      </div>
    )
  }
}

ファイルを保存します。 ブラウザが更新されたら、追加ボタンと削除ボタンをクリックします。 カートの更新は表示されますが、価格は表示されません。 The total 状態値は更新中に保持されます。 この値は、例としてのみ保持されます。 このアプリケーションでは、の両方のプロパティを更新する必要があります state 物体。 ただし、多くの場合、異なる責任を持つステートフルプロパティを持つコンポーネントがあり、更新されたオブジェクトからそれらを除外することで、それらを永続化できます。

このステップでの変更は静的でした。 事前に値がどうなるかを正確に知っていたので、から再計算する必要はありませんでした。 state. ただし、商品ページに多数の商品があり、それらを複数回追加できるようにしたい場合は、静的オブジェクトを渡しても、最新の商品を参照する保証はありません。 state、オブジェクトが使用した場合でも this.state 価値。 この場合、代わりに関数を使用できます。

次のステップでは、更新します state 現在の状態を参照する関数を使用します。

ステップ4—現在の状態を使用して状態を設定する

配列の更新、数値の追加、オブジェクトの変更など、現在の状態を更新するために以前の状態を参照する必要がある場合がよくあります。 できるだけ正確にするには、最新のものを参照する必要があります state 物体。 更新とは異なり state 事前定義された値を使用して、このステップで関数をに渡します setState メソッド。現在の状態を引数として取ります。 このメソッドを使用して、現在の状態を使用してコンポーネントの状態を更新します。

設定のもう1つの利点 state 機能を備えているので、信頼性が向上します。 パフォーマンスを向上させるために、Reactはバッチ処理する場合があります setState 呼び出し、つまり this.state.value 完全に信頼できるとは限りません。 たとえば、更新する場合 state いくつかの場所ですぐに、値が古くなっている可能性があります。 これは、データのフェッチ、フォームの検証、または複数のアクションが並行して発生している状況で発生する可能性があります。 ただし、最新の機能を使用する state 引数として、このバグがコードに入らないようにします。

この形式の状態管理を示すために、製品ページにさらにいくつかの項目を追加します。 まず、 Product.js ファイル:

  1. nano src/components/Product/Product.js

次に、さまざまな製品のオブジェクトの配列を作成します。 配列には、商品の絵文字、名前、価格が含まれます。 次に、アレイをループして、追加および削除ボタンで各製品を表示します。

state-class-tutorial / src / components / Product / Product.js
import React, { Component } from 'react';
import './Product.css';

const products = [
  {
    emoji: '🍦',
    name: 'ice cream',
    price: 5
  },
  {
    emoji: '🍩',
    name: 'donuts',
    price: 2.5,
  },
  {
    emoji: '🍉',
    name: 'watermelon',
    price: 4
  }
];

export default class Product extends Component {

  ...


  render() {
    return(
      <div className="wrapper">
        <div>
          Shopping Cart: {this.state.cart.length} total items.
        </div>
        <div>Total {this.getTotal()}</div>
        <div>
          {products.map(product => (
            <div key={product.name}>
              <div className="product">
                <span role="img" aria-label={product.name}>{product.emoji}</span>
              </div>
              <button onClick={this.add}>Add</button>
              <button onClick={this.remove}>Remove</button>
            </div>
          ))}
        </div>
      </div>
    )
  }
}

このコードでは、 map()配列メソッドを使用してループします。 products ブラウザに各要素を表示するJSXを配列して返します。

ファイルを保存します。 ブラウザがリロードされると、更新された製品リストが表示されます。

次に、メソッドを更新する必要があります。 まず、 add() 取る方法 product 引数として。 次に、オブジェクトをに渡す代わりに setState()、を受け取る関数を渡します state 引数として、 cart 新製品と total 新しい価格で更新:

state-class-tutorial / src / components / Product / Product.js
import React, { Component } from 'react';
import './Product.css';

...

export default class Product extends Component {

  state = {
    cart: [],
    total: 0
  }

  add = (product) => {
    this.setState(state => ({
      cart: [...state.cart, product.name],
      total: state.total + product.price
    }))
  }

  currencyOptions = {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  }

  getTotal = () => {
    return this.state.total.toLocaleString(undefined, this.currencyOptions)
  }

  remove = () => {
    this.setState({
      cart: []
    })
  }

  render() {
    return(
      <div className="wrapper">
        <div>
          Shopping Cart: {this.state.cart.length} total items.
        </div>
        <div>Total {this.getTotal()}</div>

        <div>
          {products.map(product => (
            <div key={product.name}>
              <div className="product">
                <span role="img" aria-label={product.name}>{product.emoji}</span>
              </div>
              <button onClick={() => this.add(product)}>Add</button>
              <button onClick={this.remove}>Remove</button>
            </div>
          ))}
        </div>
      </div>
    )
  }
}

渡す匿名関数の内部 setState()、必ず引数を参照してください—state—コンポーネントの状態ではありません—this.state. そうしないと、古くなるリスクがあります state 物体。 The state 関数内はそれ以外は同じになります。

状態を直接変更しないように注意してください。 代わりに、に新しい値を追加するとき cart、新しいを追加できます productstate 現在の値にspread構文を使用し、最後に新しい値を追加します。

最後に、呼び出しを更新します this.add を変更することによって onClick() を呼び出す無名関数を取るための小道具 this.add() 関連する製品と。

ファイルを保存します。 これを行うと、ブラウザがリロードされ、複数の製品を追加できるようになります。

次に、を更新します remove() 方法。 同じ手順に従います:変換 setState 関数を取得するには、変更せずに値を更新し、 onChange() 小道具:

state-class-tutorial / src / components / Product / Product.js
import React, { Component } from 'react';
import './Product.css';

...

export default class Product extends Component {

...

  remove = (product) => {
    this.setState(state => {
      const cart = [...state.cart];
      cart.splice(cart.indexOf(product.name))
      return ({
        cart,
        total: state.total - product.price
      })
    })
  }

  render() {
    return(
      <div className="wrapper">
        <div>
          Shopping Cart: {this.state.cart.length} total items.
        </div>
        <div>Total {this.getTotal()}</div>
        <div>
          {products.map(product => (
            <div key={product.name}>
              <div className="product">
                <span role="img" aria-label={product.name}>{product.emoji}</span>
              </div>
              <button onClick={() => this.add(product)}>Add</button>
              <button onClick={() => this.remove(product)}>Remove</button>
            </div>
          ))}
        </div>
      </div>
    )
  }
}

状態オブジェクトの変更を回避するには、最初にを使用してそのコピーを作成する必要があります。 spread オペレーター。 次に、コピーから必要なアイテムをスプライスして、新しいオブジェクトにコピーを返すことができます。 コピーすることによって state 最初のステップとして、あなたはあなたが変異しないことを確信することができます state 物体。

ファイルを保存します。 これを行うと、ブラウザが更新され、アイテムを追加および削除できるようになります。

このアプリケーションにはまだバグがあります。 remove メソッドでは、ユーザーはから減算することができます total アイテムが含まれていない場合でも cart. カートに追加せずにアイスクリームの削除をクリックすると、合計は-5.00になります。

減算する前にアイテムの存在を確認することでバグを修正できますが、より簡単な方法は、製品への参照のみを保持し、製品への参照と合計コストを分離しないことで、状態オブジェクトを小さく保つことです。 同じデータへの二重参照を避けるようにしてください。 代わりに、生データをに保存します state—この場合、全体 product オブジェクト-次に、外部で計算を実行します state.

コンポーネントをリファクタリングして、 add() メソッドはオブジェクト全体を追加します remove() メソッドはオブジェクト全体を削除し、 getTotal メソッドはを使用します cart:

state-class-tutorial / src / components / Product / Product.js
import React, { Component } from 'react';
import './Product.css';

...

export default class Product extends Component {

  state = {
    cart: [],
  }

  add = (product) => {
    this.setState(state => ({
      cart: [...state.cart, product],
    }))
  }

  currencyOptions = {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  }

  getTotal = () => {
    const total = this.state.cart.reduce((totalCost, item) => totalCost + item.price, 0);
    return total.toLocaleString(undefined, this.currencyOptions)
  }

  remove = (product) => {
    this.setState(state => {
      const cart = [...state.cart];
      const productIndex = cart.findIndex(p => p.name === product.name);
      if(productIndex < 0) {
        return;
      }
      cart.splice(productIndex, 1)
      return ({
        cart
      })
    })
  }

  render() {
    ...
  }
}

The add() メソッドは以前と似ていますが、 total プロパティは削除されました。 の中に remove() メソッド、あなたはのインデックスを見つけます productfindByIndex. インデックスが存在しない場合は、 -1. その場合、条件文を使用して何も返しません。 何も返さないことで、Reactは state 変更されず、再レンダリングをトリガーしません。 戻ったら state または空のオブジェクトでも、再レンダリングがトリガーされます。

使用する場合 splice() メソッド、あなたは今合格しています 1 2番目の引数として、1つの値を削除し、残りを保持します。

最後に、 total reduce()配列メソッドを使用します。

ファイルを保存します。 これを行うと、ブラウザが更新され、最終版が作成されます cart:

The setState 渡す関数には、現在の小道具の追加の引数を含めることができます。これは、現在の小道具を参照する必要がある状態がある場合に役立ちます。 コールバック関数をに渡すこともできます setState 最初の引数にオブジェクトまたは関数を渡すかどうかに関係なく、2番目の引数として。 これは、設定するときに特に便利です state APIからデータをフェッチした後、その後に新しいアクションを実行する必要があります state 更新が完了しました。

このステップでは、現在の状態に基づいて新しい状態を更新する方法を学習しました。 関数をに渡しました setState 関数と現在の状態を変更せずに計算された新しい値。 また、終了する方法を学びました setState 再レンダリングを妨げるような方法で更新がない場合に機能し、パフォーマンスがわずかに向上します。

結論

このチュートリアルでは、静的に更新し、現在の状態を使用する動的な状態を持つクラスベースのコンポーネントを開発しました。 これで、ユーザーと動的な情報に対応する複雑なプロジェクトを作成するためのツールが手に入りました。

Reactにはフックを使用して状態を管理する方法がありますが、クラスベースである必要があるコンポーネント(たとえば、 componentDidCatch 方法。

状態の管理は、ほぼすべてのコンポーネントにとって重要であり、インタラクティブなアプリケーションを作成するために必要です。 この知識があれば、スライダー、アコーディオン、フォームなど、多くの一般的なWebコンポーネントを再作成できます。 次に、フックを使用してアプリケーションを構築したり、APIから動的にデータをプルするコンポーネントを開発したりするのと同じ概念を使用します。

Reactのチュートリアルをもっと見たい場合は、 Reactトピックページを確認するか、React.jsシリーズのコーディング方法ページに戻ってください。