Reduxレデューサーのしくみ

公開: 2022-03-10
簡単な要約↬ stateを管理するアプリケーションの開発中にReduxを使用したことがある場合は、間違いなくレデューサーに出くわしたことでしょう。 このチュートリアルでは、レデューサーの概念と、それらがReduxで具体的にどのように機能するかについて説明します。

このチュートリアルでは、レデューサーの概念と、特にReactアプリケーションでのレデューサーの動作について学習します。 Reduxを理解し、より適切に使用するには、レデューサーをしっかりと理解することが不可欠です。 レデューサーは、アクションを使用してアプリケーションの状態を更新する方法を提供します。 これはReduxライブラリの不可欠な部分です。

このチュートリアルは、ReduxReducersについて詳しく知りたい開発者を対象としています。 ReactとReduxを理解することは有益です。 チュートリアルの最後に、Reduxでレデューサーが果たす役割をよりよく理解する必要があります。 レデューサーとそれがアプリケーションの状態にどのように影響するかをよりよく理解するために、コードデモとアプリケーションを作成します。

レデューサーとは

レデューサーは、アプリケーションとアクションの状態を引数として受け取り、新しい状態を返す純粋関数です。 たとえば、認証リデューサーは、空のオブジェクトの形式でアプリケーションの初期状態を取得し、ユーザーがログインして、ログインしたユーザーとともに新しいアプリケーション状態を返したことを通知するアクションを実行できます。

純粋関数は副作用のない関数であり、同じ引数が渡された場合に同じ結果を返します。

以下は純粋関数の例です。

 const add = (x, y) => x + y; add(2, 5);

上記の例は入力に基づいて値を返します25を渡すと、常に7が得られます。これが同じ入力である限り、他に何も得られない出力に影響を与えることはありません。これは純粋関数の例です。

以下は、状態とアクションを受け取るレデューサー関数の例です。

 const initialState = {}; const cartReducer = (state = initialState, action) => { // Do something here }

レデューサーが取り込む2つのパラメーター、 stateactionを定義しましょう。

ジャンプした後もっと! 以下を読み続けてください↓

状態は、コンポーネントが処理しているデータです。状態は、コンポーネントが必要とするデータを保持し、コンポーネントが何をレンダリングするかを決定します。 stateオブジェクトが変更されると、コンポーネントは再レンダリングされます。 アプリケーションの状態がReduxによって管理されている場合、レデューサーは状態の変化が発生する場所です。

アクション

アクションは、情報のペイロードを含むオブジェクトです。 これらは、Reduxストアが更新される唯一の情報源です。 レデューサーは、 action.typeの値に基づいてストアを更新します。 ここでは、 action.type ADD_TO_CART定義します。

公式のReduxドキュメントによると、アクションはReduxアプリケーションで変更をトリガーする唯一のものであり、アプリケーションストアへの変更のペイロードが含まれています。 アクションは、実行するアクションのタイプをReduxに指示するJavaScriptオブジェクトであり、通常、次のような関数として定義されます。

 const action = { type: 'ADD_TO_CART', payload: { product: 'margarine', quantity: 4 } }

上記のコードは、ユーザーが送信しているものを含む一般的なpayload値であり、アプリケーションの状態を更新するために使用されます。 上からわかるように、アクションオブジェクトには、この特定のアクションを実行するために必要なアクションのタイプとペイロードオブジェクトが含まれています。

レデューサーを使用した状態の更新

レデューサーがどのように機能するかを示すために、以下の数値カウンターを見てみましょう。

 const increaseAction = { type: 'INCREASE', }; const decreaseAction = { type: 'DECREASE' }; const countReducer = (state = 0, action) => { switch(action.type){ case INCREASE: return state + 1; case DECREASE : return state -1; default: return state; } };

上記のコードでは、 increaseActiondecreaseActionは、 stateが何に更新されるかを決定するためにレデューサーで使用されるアクションです。 次に、 countReducerと呼ばれるレデューサー関数があります。この関数は、 actionと、値が0の初期stateを取ります。 action.typeの値がINCREASEの場合、1ずつインクリメントされる新しい状態を返します。そうでない場合、それがDECREASEの場合、1だけデクリメントされる新しい状態が返されます。 これらの条件のいずれも意味されていない場合は、 stateを返します。

レデューサーを使用した状態の更新:スプレッド演算子

状態を直接変更することはできません。状態を作成または更新するには、JavaScriptスプレッド演算子を使用して、状態の値を直接変更しないようにします。代わりに、渡された状態を含む新しいオブジェクトを返し、ユーザーのペイロード。

 const contactAction = { type: 'GET_CONTACT', payload: ['0801234567', '0901234567'] }; const initialState = { contacts: [], contact: {}, }; export default function (state = initialState, action) { switch (action.type) { case GET_CONTACTS: return { ...state, contacts: action.payload, }; default: return state; }

上記のコードでは、スプレッド演算子を使用して、状態値を直接変更しないようにしています。これにより、渡される状態とによって送信されるペイロードで満たされた新しいオブジェクトを返すことができます。ユーザー。 スプレッド演算子を使用することで、すべての新しいアイテムを追加するときに状態が同じままであることを確認できます。また、以前に存在していた場合は、状態の連絡先フィールドを置き換えることもできます。

動作中のReduxレデューサー—デモ

Reduxレデューサーとその動作をよりよく理解するために、簡単なムービー詳細ファインダーアプリを実装します。コードと動作バージョンは、Codesandboxにあります。 開始するには、ターミナルに移動し、以下のコマンドを使用してReactアプリを初期化します。

 create-react-app movie-detail-finder

プロジェクトが初期化されたら、次にアプリケーションに必要なパッケージをインストールしましょう。

 npm i axios reactstrap react-redux redux redux-thunk

パッケージがインストールされたら、次のコマンドを使用して開発サーバーを起動しましょう。

 npm start

上記のコマンドは、ブラウザでプロジェクト開発サーバーを起動する必要があります。 次に、選択したテキストエディターでプロジェクトを開き、プロジェクトのsrcフォルダー内で、 App.cssApp.test.jsserviceWorker.jssetupTests.jsのファイルを削除します。 次に、 App.jsで削除されたファイルを参照するすべてのコードを削除しましょう。

このプロジェクトでは、Open Movie Database APIを使用して、アプリケーションの映画情報、コンテンツ、画像を取得します。ここにAPIへのリンクがあります。これを使用するには、登録してアクセスキーを取得する必要があります。アプリケーション、完了したら、コンポーネントを構築してアプリケーションを進めましょう。

アプリコンポーネントの構築

まず、プロジェクトディレクトリのsrcフォルダー内に、componentsというフォルダーを作成し、フォルダー内に、 MovieSearchbarという2つのフォルダーを作成します。コンポーネントは、次の画像のようになります。

コンポーネントフォルダ
コンポーネントフォルダ。 (大プレビュー)

映画コンポーネントの構築

APIから取得する映画の詳細の構造の概要を示すMoviesコンポーネントを作成しましょう。 これを行うには、コンポーネントのMoviesフォルダー内に、新しいファイルMovie.jsを作成し、次にAPI結果のクラスベースのコンポーネントを作成します。以下でそれを実行しましょう。

 import React, { Component } from 'react'; import { Card, CardImg, CardText, CardBody, ListGroup, ListGroupItem, Badge } from 'reactstrap'; import styles from './Movie.module.css'; class Movie extends Component{ render(){ if(this.props.movie){ return ( <div className={styles.Movie}> <h3 className="text-center my-4"> Movie Name: {this.props.movie.Title} </h3> <Card className="text-primary bg-dark"> <CardImg className={styles.Img} top src={this.props.movie.Poster} alt={this.props.movie.Title}/> <CardBody> <ListGroup className="bg-dark"> <ListGroupItem> <Badge color="primary">Actors:</Badge> {this.props.movie.Actors} </ListGroupItem> <ListGroupItem> <Badge color="primary">Genre:</Badge> {this.props.movie.Genre} </ListGroupItem> <ListGroupItem> <Badge color="primary">Year:</Badge> {this.props.movie.Year} </ListGroupItem> <ListGroupItem> <Badge color="primary">Writer(s):</Badge> {this.props.movie.Writer} </ListGroupItem> <ListGroupItem> <Badge color="primary">IMDB Rating:</Badge> {this.props.movie.imdbRating}/10 </ListGroupItem> </ListGroup> <CardText className="mt-3 text-white"> <Badge color="secondary">Plot:</Badge> {this.props.movie.Plot} </CardText> </CardBody> </Card> </div> ) } return null } } export default Movie;

上記のコードでは、パッケージreactstrapのコンポーネントを使用して、ここでドキュメントを確認できます。 映画の名前、画像、ジャンル、俳優、年、映画作家、評価、プロットを含むカードコンポーネントを作成しました。 このコンポーネントからのデータの受け渡しを容易にするために、他のコンポーネントへの小道具としてのデータを作成しました。 次に、 Searchbarコンポーネントを作成しましょう。

検索バーコンポーネントの構築

検索バーコンポーネントには、映画コンポーネントを検索するための検索バーとSearchbarコンポーネントがあります。これを以下で実行しましょう。

 import React from 'react'; import styles from './Searchbar.module.css'; import { connect } from 'react-redux'; import { fetchMovie } from '../../actions'; import Movie from '../Movie/Movie'; class Searchbar extends React.Component{ render(){ return( <div className={styles.Form}> <div> <form onSubmit={this.formHandler}> <input type="text" placeholder="Movie Title" onChange={e => this.setState({title: e.target.value})} value={this.state.title}/> <button type="submit">Search</button> </form> </div> <Movie movie={this.props.movie}/> </div> ) } }

上記のコードでは、ReactコンポーネントをReduxストアに接続するために使用されるreact-reduxからconnectをインポートし、コンポーネントにストアからの情報を提供し、アクションをストアにディスパッチするために使用される関数も提供します。 次に、アクションからMovieコンポーネントと関数fetchMovieをインポートしました。

次に、映画のタイトルを入力するための入力ボックスを備えたフォームタグがあり、ReactのsetStateフックを使用して、 titleの状態を入力ボックスに入力された値に設定するonChangeイベントと値を追加しました。 映画のタイトルを検索するためのbuttonタグがあり、インポートしたMovieコンポーネントを使用して、コンポーネントのプロパティをpropsとして検索結果に渡しました。

次に、結果を送信するために映画のタイトルをAPIに送信する関数を作成します。また、アプリケーションの初期状態を設定する必要があります。 以下でそれをやってみましょう。

 class Searchbar extends React.Component{ state = { title: '' } formHandler = (event) => { event.preventDefault(); this.props.fetchMovie(this.state.title); this.setState({title: ''}); }

ここでは、アプリケーションの初期状態を空の文字列に設定し、イベントパラメータを受け取り、アクションからfetchMovie関数を渡し、アプリケーションの新しい状態としてタイトルを設定する関数formHandlerを作成しました。 アプリケーションを完成させるために、 react-reduxからconnectプロパティを使用してこのコンポーネントをエクスポートしましょう。これを行うには、react redux mapToStatePropsプロパティを使用して、コンポーネントに必要なデータの部分を選択しますmapToStatePropsについて詳しくは、こちらをご覧ください。

 const mapStateToProps = (state) => { return { movie: state.movie } } export default connect(mapStateToProps, { fetchMovie })(Searchbar)

Searchbar.module.cssファイルを作成し、以下のスタイルを追加して、フォームにスタイルを追加しましょう。

 .Form{ margin: 3rem auto; width: 80%; height: 100%; } input{ display: block; height: 45px; border: none; width: 100%; border-radius: 0.5rem; outline: none; padding: 0 1rem; } input:focus, select:focus{ border: 2px rgb(16, 204, 179) solid; } .Form button{ display: block; background: rgb(16, 204, 179); padding: 0.7rem; border-radius: 0.5rem; width: 20%; margin-top: 0.7rem; color: #FFF; border: none; text-decoration: none; transition: all 0.5s; } button:hover{ opacity: 0.6; } @media(max-width: 700px){ input{ height: 40px; padding: 0 1rem; } .Form button{ width: 40%; padding: 0.6rem; } }

上記を実行すると、検索バーコンポーネントは次の画像のようになります。

サーチバーコンポーネント
サーチバーコンポーネント。 (大プレビュー)

アプリケーションのアクションの作成

このコンポーネントでは、アプリケーションのReduxアクションを設定します。まず、 srcディレクトリ内にactionsという名前のフォルダーを作成し、フォルダー内にindex.jsファイルを作成します。 ここでは、titleパラメーターを受け取り、Axiosを使用してAPIからムービーをフェッチする関数fetchMovieを作成します。 以下でこれを行いましょう:

 import axios from 'axios'; export const fetchMovie = (title) => async (dispatch) => { const response = await axios.get( `https://cors-anywhere.herokuapp.com/https://www.omdbapi.com/?t=${title}&apikey=APIKEY`); dispatch({ type: 'FETCH_MOVIE', payload: response.data }) }

上記のコードでは、 axiosをインポートし、async / awaitを使用してtitleパラメーターをfetchMovieという関数を作成し、APIサーバーにリクエストを送信できるようにしました。 Reduxに渡されるアクションオブジェクトをReduxにディスパッチするdispatch関数があります。 上記の内容から、タイプFETCH_MOVIEのアクションと、APIから取得した応答を含むペイロードをディスパッチしています。

注:リクエスト内のapikeyは、 OmdbAPI apikey登録した後、独自のapikeyに置き換えられます

アプリレデューサーの作成

このセクションでは、アプリケーション用のレデューサーを作成します。

 const fetchMovieReducer = (state = null, action) => { switch(action.type){ case 'FETCH_MOVIE': return action.payload; default: return state; } } const rootReducer = (state, action) => { return { movie: fetchMovieReducer(state, action) } } export default rootReducer;

上記のコードでは、スイッチ演算子を使用して、デフォルトのnull状態とactionパラメーターをfetchMovieReducerを作成しました。たとえば、 FETCH_MOVIEの場合、APIから取得したムービーであるaction.payloadの値を返します。 実行しようとしたアクションがレデューサーにない場合は、デフォルトの状態に戻ります。

次に、現在の状態とアクションを入力として受け取り、 fetchMovieReducerを返すrootReducer関数を作成しました。

それを一緒に入れて

このセクションでは、 index.jsにreduxストアを作成してアプリを完成させます。以下でそれを実行しましょう。

 import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import App from './App'; import 'bootstrap/dist/css/bootstrap.min.css'; import './index.css'; import reducers from './reducers'; const store = createStore(reducers, applyMiddleware(thunk)) ReactDOM.render( <Provider store={store}> <> <App/> </> </Provider>, document.getElementById('root') )

上記のコードでは、作成したレデューサーとミドルウェアを渡して、 createStoreメソッドを使用してアプリケーションstoreを作成しました。 ミドルウェアは、Reduxの機能を強化するためのアドオンです。 ここでは、applyMiddlewareを使用してapplyMiddlewareミドルウェアを利用しています。 Redux Thunkミドルウェアは、ストアが非同期更新を行うために必要です。 デフォルトでは、Reduxはストアを同期的に更新するため、これが必要です。

アプリケーションが使用する正確なストアを認識していることを確認するために、アプリケーションをProviderコンポーネントでラップし、ストアを小道具として渡しました。これにより、アプリケーションの他のコンポーネントがストアに接続して情報を共有できるようになります。

index.cssファイルに少しスタイルを追加しましょう。

 *{ margin: 0; padding: 0; box-sizing: border-box; } body{ background: rgb(15, 10, 34); color: #FFF; height: 100vh; max-width: 100%; }

映画詳細ファインダーのレンダリングとテスト

このセクションでは、 App.jsでアプリケーションをレンダリングしてアプリケーションを完成させます。これを行うには、 Appという名前のクラスベースのコンポーネントを作成し、検索バーと入力フィールドを初期化します。

 import React from 'react'; import Searchbar from './components/Searchbar/Searchbar'; import styles from './App.module.css'; class App extends React.Component{ render(){ return( <div className={styles.App}> <h1 className={styles.Title}>Movies Search App</h1> <Searchbar/> </div> ) } } export default App;

ここでは、Movie Search Appというh1を使用してAppクラスベースのコンポーネントを作成し、 Searchbarコンポーネントを追加しました。 アプリケーションは次の画像のようになります。

レデューサー付きの映画詳細アプリケーション
レデューサーを使用した最終的な映画の詳細アプリケーション。 (大プレビュー)

実用的なデモはCodesandboxで入手できます。

結論

レデューサーはReduxの状態管理の重要な部分であり、レデューサーを使用すると、副作用なしにReduxアプリケーションの特定の領域を更新する純粋関数を記述できます。 Reduxレデューサーの基本、その使用法、レデューサー、状態、引数のコアコンセプトを学びました。

こちらのReduxレデューサーに関するドキュメントを参照すると、これをさらに進めることができます。 これをさらに進めて、Reduxレデューサーをさらに構築することができます。構築するものを教えてください。

資力

  • React-Reduxのドキュメント
  • Reduxのドキュメント
  • connect()関数
  • applyMiddleware関数