Reduxレデューサーのしくみ
公開: 2022-03-10state
を管理するアプリケーションの開発中にReduxを使用したことがある場合は、間違いなくレデューサーに出くわしたことでしょう。 このチュートリアルでは、レデューサーの概念と、それらがReduxで具体的にどのように機能するかについて説明します。このチュートリアルでは、レデューサーの概念と、特にReactアプリケーションでのレデューサーの動作について学習します。 Reduxを理解し、より適切に使用するには、レデューサーをしっかりと理解することが不可欠です。 レデューサーは、アクションを使用してアプリケーションの状態を更新する方法を提供します。 これはReduxライブラリの不可欠な部分です。
このチュートリアルは、ReduxReducersについて詳しく知りたい開発者を対象としています。 ReactとReduxを理解することは有益です。 チュートリアルの最後に、Reduxでレデューサーが果たす役割をよりよく理解する必要があります。 レデューサーとそれがアプリケーションの状態にどのように影響するかをよりよく理解するために、コードデモとアプリケーションを作成します。
レデューサーとは
レデューサーは、アプリケーションとアクションの状態を引数として受け取り、新しい状態を返す純粋関数です。 たとえば、認証リデューサーは、空のオブジェクトの形式でアプリケーションの初期状態を取得し、ユーザーがログインして、ログインしたユーザーとともに新しいアプリケーション状態を返したことを通知するアクションを実行できます。
純粋関数は副作用のない関数であり、同じ引数が渡された場合に同じ結果を返します。
以下は純粋関数の例です。
const add = (x, y) => x + y; add(2, 5);
上記の例は入力に基づいて値を返します2
と5
を渡すと、常に7
が得られます。これが同じ入力である限り、他に何も得られない出力に影響を与えることはありません。これは純粋関数の例です。
以下は、状態とアクションを受け取るレデューサー関数の例です。
const initialState = {}; const cartReducer = (state = initialState, action) => { // Do something here }
レデューサーが取り込む2つのパラメーター、 state
とaction
を定義しましょう。
州
状態は、コンポーネントが処理しているデータです。状態は、コンポーネントが必要とするデータを保持し、コンポーネントが何をレンダリングするかを決定します。 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; } };
上記のコードでは、 increaseAction
とdecreaseAction
は、 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.css
、 App.test.js
、 serviceWorker.js
、 setupTests.js
のファイルを削除します。 次に、 App.js
で削除されたファイルを参照するすべてのコードを削除しましょう。
このプロジェクトでは、Open Movie Database APIを使用して、アプリケーションの映画情報、コンテンツ、画像を取得します。ここにAPIへのリンクがあります。これを使用するには、登録してアクセスキーを取得する必要があります。アプリケーション、完了したら、コンポーネントを構築してアプリケーションを進めましょう。
アプリコンポーネントの構築
まず、プロジェクトディレクトリのsrc
フォルダー内に、componentsというフォルダーを作成し、フォルダー内に、 Movie
とSearchbar
という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
関数