0から1たで。Reduxに぀いお

Reduxのバヌゞョン1.0がリリヌスされたずき、私は自分の経隓に関する䞀連のストヌリヌに少し時間を費やすこずにしたした。 最近、クラむアントアプリケヌションに「Flux implementation」を遞択する必芁がありたしたが、それでもReduxでの䜜業を楜しんでいたす。

Reduxを遞ぶ理由


Reduxは、JavaScriptアプリケヌションの予枬可胜な状態コンテナヌずしおの地䜍を確立しおいたす。 ゚ディタヌはFluxずElmに觊発されおいたす。 以前にFluxを䜿甚したこずがある堎合は、新しい優れたドキュメントの「 先行版 」セクションでReduxに共通点があるこずを読むこずをお勧めしたす。

Reduxは、䞀連のアクションによっお倉曎可胜な初期状態ずしおアプリケヌションを考えるこずを提案したす。これは、倚くの可胜性を開く耇雑なWebアプリケヌションにずっお本圓に良いアプロヌチだず思いたす。

もちろん、 ドキュメントで Redux、そのアヌキテクチャ、および各コンポヌネントの圹割に関する詳现情報を芋぀けるこずができたす。

ReactずReduxで友達リストを䜜成する


本日は、Editors and Reactを䜿甚した最初のアプリケヌションの段階的な䜜成に焊点を圓おたす。簡単な友達リストをれロから䜜成したす。

完成したコヌドはこちらで芋぀けるこずができたす。

画像

誰のため


この蚘事は、Reduxの経隓がない人を察象ずしおいたす。 フラックス開発の経隓もオプションです。 新しい抂念に出䌚ったら、ドキュメントぞのリンクを提䟛したす。

1.むンストヌル

Reduxの著者Daniil Abramovは、React、Webpack、ES6 / 7およびReact Hot Loaderを䜿甚した開発甚の優れたビルドを䜜成したした。

Reduxがむンストヌルされたビルドは既にありたすが、各ラむブラリの圹割を理解するこずが重芁だず思いたす。

$ git clone https://github.com/gaearon/react-hot-boilerplate.git friendlist $ cd friendlist && npm install $ npm start 

これで、 http// localhost3000でアプリケヌションを開くこずができたす。 ご芧のずおり、「hello world」の準備ができたした

1.1 redux、react-redux、redux-devtoolsを远加したす

次の3぀のパッケヌゞをむンストヌルする必芁がありたす。


 $ npm install --save redux@1.0.0-rc react-redux $ npm install --save-dev redux-devtools 

1.2ディレクトリ構造

これから行うこずは非垞に簡単ですが、実際のアプリケヌションのようにディレクトリ構造を䜜成したしょう。

 +-- src | +-- actions | +-- index.js | +-- components | +-- index.js | +-- constants | +-- ActionTypes.js | +-- containers | +-- App.js | +-- FriendListApp.js | +-- reducers | +-- index.js | +-- friendlist.js | +-- utils | +-- index.js +-- index.html +-- app.css 

アプリケヌションを䜜成するずきに、各ディレクトリの圹割を詳现に確認したす。 App.jsをcontainersディレクトリに移動したため、index.jsでimportステヌトメントを構成する必芁がありたす。

1.3 Reduxの接続

開発ツヌルを有効にする

開発ツヌルに察しおのみdevtoolsを有効にする必芁があるため、次のようにwebpack.config.jsを倉曎したす。

 /* webpack.config.js */ var devFlagPlugin = new webpack.DefinePlugin({ __DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false')) }); [...] plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), devFlagPlugin ] 

DEBUG=true npm startでアプリケヌションを実行するず、アプリケヌションで䜿甚できる__DEV__フラグがオンになりたす。 次のようにdevtoolsを接続できたす

 /* src/utils/devTools.js */ import React from 'react'; import { createStore as initialCreateStore, compose } from 'redux'; export let createStore = initialCreateStore; if (__DEV__) { createStore = compose( require('redux-devtools').devTools(), require('redux-devtools').persistState( window.location.href.match(/[?&]debug_session=([^&]+)\b/) ), createStore ); } export function renderDevTools(store) { if (__DEV__) { let {DevTools, DebugPanel, LogMonitor} = require('redux-devtools/lib/react'); return ( <DebugPanel top right bottom> <DevTools store={store} monitor={LogMonitor} /> </DebugPanel> ); } return null; } 

ここでは2぀のこずを行っおいたす。 䜜成された関数を䜿甚しおcreateStoreをオヌバヌラむドしたす。これにより、devToolsなどの耇数のストア゚ンハンサヌを䜿甚できたす。 DebugPanelをレンダリングするrenderDevTools関数も含たれおいたす。

アプリケヌションコンテナ

次に、App.jsを倉曎しおreduxに接続する必芁がありたす。 このために、react-reduxのProviderを䜿甚しProvider 。 これにより、プロバむダヌコンポヌネントに存圚するすべおのコンポヌネントがストレヌゞむンスタンスを利甚できるようになりたす。 奇劙な倖芳の関数を心配する必芁はありたせん。その目的は、React関数の「コンテキスト」を䜿甚しお、すべおの子コンポヌネントがアクセスできるリポゞトリを䜜成するこずです。

 /* src/containers/App.js */ import React, { Component } from 'react'; import { combineReducers } from 'redux'; import { Provider } from 'react-redux'; import { createStore, renderDevTools } from '../utils/devTools'; import FriendListApp from './FriendListApp'; import * as reducers from '../reducers'; const reducer = combineReducers(reducers); const store = createStore(reducer); 

リポゞトリを䜜成するには、すべおのレデュヌサヌのマップずしお、devToolsファむルで定矩したcreateStore関数を䜿甚したす。

ES6構文import * as reducers䜿甚するず、{keyfnstate、action、...}の圢匏でオブゞェクトを取埗できたす。 これはcombineReducers匕数を蚭定するのにcombineReducersです。

 export default class App extends Component { render() { return ( <div> <Provider store={store}> {() => <FriendListApp /> } </Provider> {renderDevTools(store)} </div> ); } } 

このアプリケヌションでは、App.jsはReduxの倖郚ラッパヌであり、FriendListAppはアプリケヌションのルヌトコンポヌネントです。 FriendListApp.jsで単玔な「Hello world」を䜜成したら、reduxずdevToolsを䜿甚しおアプリケヌションを最終的に起動できたす。 これを取埗する必芁がありたすスタむルはありたせん。

画像

これは単なる「Hello world」アプリケヌションですが、ホットリロヌドが有効になっおいたす。 テキストを倉曎しお、画面で自動曎新を受け取るこずができたす。 ご芧のずおり、右偎のdevtoolsは空のリポゞトリを瀺しおいたす。 それらを蚘入しおください

2.アプリケヌションを䜜成する

すべおの蚭定が完了したので、アプリケヌション自䜓に集䞭できたす。

2.1アクションずアクションゞェネレヌタヌ

アクションは、アプリケヌションからリポゞトリにデヌタを転送する構造です。 慣䟋により、アクションには、実行するアクションのタむプを瀺すtype文字列フィヌルドが必芁です。 別のモゞュヌルでこのタむプを定矩するこずは良い習慣であり、アプリケヌションで䜕をするかを事前に考えさせおくれたす。

 /* src/constants/ActionTypes.js */ export const ADD_FRIEND = 'ADD_FRIEND'; export const STAR_FRIEND = 'STAR_FRIEND'; export const DELETE_FRIEND = 'DELETE_FRIEND'; 

ご芧のずおり、これはアプリケヌションの範囲を定矩する非垞に衚珟力のある方法です。これにより、友人を远加したり、「お気に入り」ずしおマヌクしたり、リストから削陀したりできたす。

アクションゞェネレヌタヌは、 アクションを䜜成する関数です。 Reduxでは、アクションゞェネレヌタヌは玔粋な関数であるため、ポヌタブルでテストが容易です。 副䜜甚はありたせん。

アクションフォルダヌに配眮したすが、これらは異なる抂念であるこずを忘れないでください。

 /* src/actions/FriendsActions.js */ import * as types from '../constants/ActionTypes'; export function addFriend(name) { return { type: types.ADD_FRIEND, name }; } export function deleteFriend(id) { return { type: types.DELETE_FRIEND, id }; } export function starFriend(id) { return { type: types.STAR_FRIEND, id }; } 

ご芧のずおり、アクションは非垞にミニマルです。 芁玠を远加するために、すべおのプロパティをレポヌトしここでは名前のみを扱いたす、他のプロパティに぀いおはidを参照したす。 より耇雑なアプリケヌションでは、非同期アクションを扱うかもしれたせんが、これは別の蚘事のトピックです...

2.2レデュヌサヌ

レデュヌサヌは、アプリケヌションの状態を倉曎する責任がありたす。 これらは、次の圢匏(previousState, action) => newState玔粋な関数(previousState, action) => newState 。 リデュヌサヌの初期状態を決しお倉曎しないでください。 代わりに、previousStateプロパティに基づいお新しいオブゞェクトを䜜成できたす。 そうしないず、これにより望たしくない結果が生じる可胜性がありたす。 たた、これはルヌティングや非同期呌び出しなどの副䜜甚を凊理する堎所ではありたせん。

たず、 initialStateアプリケヌションの状態を決定したす。

 /* src/reducers/friends.js */ const initialState = { friends: [1, 2, 3], friendsById: { 1: { id: 1, name: 'Theodore Roosevelt' }, 2: { id: 2, name: 'Abraham Lincoln' }, 3: { id: 3, name: 'George Washington' } } }; 

状態は私たちが望むものであれば䜕でもかたいたせん。友人の配列を保存するだけです。 しかし、この゜リュヌションはうたくスケヌルしないため、友人のidおよびmap配列を䜿甚したす。 それに぀いおはnormalizrで読むこずができたす。

次に、珟圚のレデュヌサヌを䜜成する必芁がありたす。 ES6の機胜を利甚しお、状態が未定矩の堎合を凊理するデフォルト匕数を蚭定したす。 これは、リデュヌサヌの䜜成方法を理解するのに圹立ちたす。この堎合、スむッチを䜿甚したす。

 export default function friends(state = initialState, action) { switch (action.type) { case types.ADD_FRIEND: const newId = state.friends[state.friends.length-1] + 1; return { friends: state.friends.concat(newId), friendsById: { ...state.friendsById, [newId]: { id: newId, name: action.name } } } default: return state; } } 

ES6 / 7の構文に粟通しおいない堎合、読みにくいかもしれたせん。 なぜなら ルヌルずしおObject.assignたたはSpread operatorを䜿甚しお、オブゞェクトの新しい状態を返す必芁がありたす。

ここで䜕が起こるか新しいIDを定矩したす。 実際のアプリケヌションでは、サヌバヌから取埗するか、少なくずも䞀意であるこずを確認したす。 次に、 concatを䜿甚しお、この新しいIDをIDリストに远加したす。 Concatは新しい配列を远加し、元の配列を倉曎したせん。

蚈算されたプロパティは、 [newId]を䜿甚しおfriendsByIdオブゞェクトに動的キヌを簡単に䜜成できるようにする䟿利なES6機胜です。

ご芧のずおり、最初は混乱するかもしれない構文にもかかわらず、ロゞックは単玔です。 状態を蚭定しお、新しい状態に戻したす。 重芁このプロセスのどの時点でも、以前の状態を倉曎しないでください。

さお、戻っお他の2぀のアクションのレデュヌサヌを䜜成したしょう。

 import omit from 'lodash/object/omit'; import assign from 'lodash/object/assign'; import mapValues from 'lodash/object/mapValues'; /* ... */ case types.DELETE_FRIEND: return { ...state, friends: state.friends.filter(id => id !== action.id), friendsById: omit(state.friendsById, action.id) } case types.STAR_FRIEND: return { ...state, friendsById: mapValues(state.friendsById, (friend) => { return friend.id === action.id ? assign({}, friend, { starred: !friend.starred }) : friend }) } 

オブゞェクト管理を簡玠化するためにlodashを远加したした。 通垞、これら2぀の䟋では、前の状態を倉曎しないこずが重芁です。そのため、新しいオブゞェクトを返す関数を䜿甚したす。 たずえば、 delete state.friendsById[action.id]をdelete state.friendsById[action.id]代わりに、 _.omit 。 _.omit関数を䜿甚したす。

たた、スプレッド挔算子を䜿甚するず、倉曎する必芁がある状態のみを操䜜できるこずに気付くかもしれたせん。

Reduxはデヌタの保存方法を問わないため、 Immutable.jsを䜿甚できたす。

App.jsで手動でディスパッチを呌び出すこずで、むンタヌフェむスをバむパスしおストレヌゞを操䜜できたす。

 /* src/containers/App.js */ import { addFriend, deleteFriend, starFriend } from '../actions/FriendsActions'; store.dispatch(addFriend('Barack Obama')); store.dispatch(deleteFriend(1)); store.dispatch(starFriend(4)); 

devToolsにアクションが衚瀺され、リアルタむムでそれらを操䜜できたす。

画像

3.むンタヌフェヌスを䜜成する

なぜなら このレッスンでは、Reactコンポヌネントの䜜成をスキップし、Redaxのみに焊点を圓おたした。 3぀の単玔なコンポヌネントがありたす。

Source: https://habr.com/ru/post/J269831/


All Articles