ReactContextAPIの操作

公開: 2022-03-11

React Context APIは、しばらくの間実験的な機能として使用されてきましたが、Reactのバージョン16.3.0でのみ、本番環境で安全に使用できるようになりました。 以下の記事では、2つの基本的なウェブストアアプリを紹介します。1つはContext APIを使用して構築され、もう1つはContextAPIを使用せずに構築されています。

この新しいAPIは、1つの主要な問題であるプロップドリルを解決します。 この用語に精通していなくても、React.jsアプリで作業したことがあれば、おそらくそれはあなたに起こったことでしょう。 プロップドリルは、中間のReactコンポーネントの複数のレイヤーを通過することにより、コンポーネントAからコンポーネントZにデータを取得する処理です。 コンポーネントは間接的に小道具を受け取ります。あなたは、React開発者がすべてが正しく機能することを確認する必要があります。

ReactContextAPIなしで一般的な問題をどのように処理するかを調べてみましょう。

App.js

 class App extends Component { state = { cars: { car001: { name: 'Honda', price: 100 }, car002: { name: 'BMW', price: 150 }, car003: { name: 'Mercedes', price: 200 } } }; incrementCarPrice = this.incrementCarPrice.bind(this); decrementCarPrice = this.decrementCarPrice.bind(this); incrementCarPrice(selectedID) { // a simple method that manipulates the state const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price + 1; this.setState({ cars }); } decrementCarPrice(selectedID) { // a simple method that manipulates the state const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price - 1; this.setState({ cars }); } render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to my web store</h1> </header> {/* Pass props twice */} <ProductList cars={this.state.cars} incrementCarPrice={this.incrementCarPrice} decrementCarPrice={this.decrementCarPrice} /> </div> ); } }

ProductList .js

 const ProductList = props => ( <div className="product-list"> <h2>Product list:</h2> {/* Pass props twice */} <Cars cars={props.cars} incrementCarPrice={props.incrementCarPrice} decrementCarPrice={props.decrementCarPrice} /> {/* Other potential product categories which we will skip for this demo: */} {/* <Electronics /> */} {/* <Clothes /> */} {/* <Shoes /> */} </div> ); export default ProductList;

Cars.js

 const Cars = props => ( <Fragment> <h4>Cars:</h4> {/* Finally we can use data */} {Object.keys(props.cars).map(carID => ( <Car key={carID} name={props.cars[carID].name} price={props.cars[carID].price} incrementPrice={() => props.incrementCarPrice(carID)} decrementPrice={() => props.decrementCarPrice(carID)} /> ))} </Fragment> );

Car.js

 const Cars = props => ( <Fragment> <p>Name: {props.name}</p> <p>Price: ${props.price}</p> <button onClick={props.incrementPrice}>&uarr;</button> <button onClick={props.decrementPrice}>&darr;</button> </Fragment> );

確かに、これはデータを処理するための最良の方法ではありませんが、支柱の穴あけがうまくいかない理由を示していると思います。 では、Context APIはこれをどのように回避するのに役立ちますか?

コンテキストウェブストアの紹介

アプリをリファクタリングして、何ができるかを示しましょう。 簡単に言うと、Context APIを使用すると、データが存在する中央ストアを作成できます(そう、Reduxの場合と同じです)。 ストアは、任意のコンポーネントに直接挿入できます。 仲買人を切り抜くことができます!

2つの状態フローの例:1つはReact Context APIを使用し、もう1つは使用しない

リファクタリングは非常に簡単です。コンポーネントの構造を変更する必要はありません。 ただし、プロバイダーとコンシューマーという新しいコンポーネントを作成する必要があります。

1.コンテキストを初期化します

まず、コンテキストを作成する必要があります。このコンテキストは、後でプロバイダーとコンシューマーを作成するために使用できます。

MyContext.js

 import React from 'react'; // this is the equivalent to the createStore method of Redux // https://redux.js.org/api/createstore const MyContext = React.createContext(); export default MyContext;

2.プロバイダーを作成します

それが完了したら、コンテキストをインポートし、それを使用して、MyProviderと呼ばれるプロバイダーを作成できます。 その中で、いくつかの値で状態を初期化します。これは、プロバイダーコンポーネントのバリュープロップを介して共有できます。 この例では、 this.state.carsを、状態を操作するいくつかのメソッドと一緒に共有しています。 これらの方法をReduxのレデューサーと考えてください。

MyProvider.js

 import MyContext from './MyContext'; class MyProvider extends Component { state = { cars: { car001: { name: 'Honda', price: 100 }, car002: { name: 'BMW', price: 150 }, car003: { name: 'Mercedes', price: 200 } } }; render() { return ( <MyContext.Provider value={{ cars: this.state.cars, incrementPrice: selectedID => { const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price + 1; this.setState({ cars }); }, decrementPrice: selectedID => { const cars = Object.assign({}, this.state.cars); cars[selectedID].price = cars[selectedID].price - 1; this.setState({ cars }); } }} > {this.props.children} </MyContext.Provider> ); } }

プロバイダーを他のコンポーネントにアクセスできるようにするには、アプリをプロバイダーでラップする必要があります(はい、Reduxの場合と同じです)。 MyProvider.jsで定義されているため、状態とメソッドを削除できます。

App.js

 class App extends Component { render() { return ( <MyProvider> <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to my web store</h1> </header> <ProductList /> </div> </MyProvider> ); } }

3.コンシューマーを作成します

コンテキストを再度インポートし、コンポーネントをラップして、コンポーネントにコンテキスト引数を挿入する必要があります。 その後、それはかなり簡単です。 小道具を使用するのと同じ方法で、コンテキストを使用します。 MyProducerで共有したすべての値を保持しているので、使用する必要があります。

Cars.js

 const Cars = () => ( <MyContext.Consumer> {context => ( <Fragment> <h4>Cars:</h4> {Object.keys(context.cars).map(carID => ( <Car key={carID} name={context.cars[carID].name} price={context.cars[carID].price} incrementPrice={() => context.incrementPrice(carID)} decrementPrice={() => context.decrementPrice(carID)} /> ))} </Fragment> )} </MyContext.Consumer> );

何を忘れましたか? ProductList! ここでメリットが明らかになります。 データやメソッドは渡しません。 いくつかのコンポーネントをレンダリングするだけでよいため、コンポーネントは単純化されています。

ProductList.js

 const ProductList = () => ( <div className="product-list"> <h2>Product list:</h2> <Cars /> {/* Other potential product categories which we will skip for this demo: */} {/* <Electronics /> */} {/* <Clothes /> */} {/* <Shoes /> */} </div> );

この記事の過程で、ReduxとContextAPIをいくつか比較しました。 Reduxの最大の利点の1つは、アプリに任意のコンポーネントからアクセスできる中央ストアを含めることができるという事実です。 新しいContextAPIを使用すると、デフォルトでその機能を利用できます。 ContextAPIがReduxを時代遅れにするという多くの誇大宣伝がなされてきました。

これは、中央ストアの機能にReduxのみを使用している方にも当てはまるかもしれません。 それが使用していた唯一の機能である場合は、これをContext APIに置き換えて、サードパーティのライブラリを使用せずにプロップドリルを回避できます。

Reactアプリケーションのパフォーマンスの測定と最適化に興味がある場合は、仲間のToptalerWilliamWangによるReactパフォーマンスの最適化ガイドをお読みください。