Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9289

C#お兄さんのReactやってみよう - 第4回: Redux足がかり -

$
0
0

note_titleImage.png

連載第4回目です。
そろそろ習慣づいてきたのかもしれません。コロナの影響で在宅・自宅待機が増えたこともあり、こういうところに時間を割くことができているのは、自分としてもいい時間の使い方ができているなぁと満足しています。

前回までのおさらい

前回までといっても、実際に React を触り始めたのは第3回だけなので、大したおさらいはありません。前回はCreateReactApp で作られた ClientApp の中身のうち、静的サイトライクな部分だけカスタマイズしてみて、なんとなくの雰囲気を掴んでみました。

今回は少し話を進めて、ちょっとインタラクティブな部分に手を出していこうと思っています。
Redux というのを題材にします。 Redux 理解した、っていうのはまぁよくある、 「完全に理解した」となんもわからんのジレンマみたいなものにハマるやつです。

image.png

※有名所からお借りしています

Redux について知った気になろう

まずは Redux について理解を進めましょう。なにはともあれ公式のリンクは こちら

Redux についてちゃんと理解を進めるなら、以下の Qiita 記事がとても良いと思います。
https://qiita.com/kitagawamac/items/49a1f03445b19cf407b7 : Redux入門

ここではこんな素晴らしい記事があることに感謝しつつ、サンプルを動かしながらざっと理解するという方針で書いていこうと思います。

Counter ページで Redux に触れてみよう

前回はサンプルページの Homeを使用しましたが、今回は Counterページを使用します。なにはともあれ動作を見てみましょう。

Counter のページでは、ボタンの押した回数がカウントされるというシンプルなものが提供されています。 では早速コードを見ていきましょう。 Components/Counter.tsx を見てみてください。

import*asReactfrom'react';import{connect}from'react-redux';import{RouteComponentProps}from'react-router';import{ApplicationState}from'../store';import*asCounterStorefrom'../store/Counter';typeCounterProps=CounterStore.CounterState&typeofCounterStore.actionCreators&RouteComponentProps<{}>;classCounterextendsReact.PureComponent<CounterProps>{publicrender(){return(<React.Fragment><h1>Counter</h1><p>This is a simple example of a React component.</p><paria-live="polite">Current count: <strong>{this.props.count}</strong></p><buttontype="button"className="btn btn-primary btn-lg"onClick={()=>{this.props.increment();}}>
                    Increment
                </button></React.Fragment>);}};exportdefaultconnect((state:ApplicationState)=>state.counter,CounterStore.actionCreators)(Counter);

なんだかいろいろなものがインポートされているので難しそうに見えますが、必要なときに見る程度で、ここは流していきましょう。また、タイトルの部分やそれに続く説明文はそのままベタ書きのHTMLなのでそっとしておきましょう。

では、それ以外に目を向けると以下の点が気になってきますね。

  • this.props.count
  • onClick={() => {this.props.increment();}}
  • connect( (state: ApplicationState) => state.counter, CounterStore.actionCreators )(Counter);

このあたりに React と Redux の繋がりが表現されています。正直、 Redux を勢いで入れたことを後悔するほどには Redux は初見殺しだと感じています。ですが、 Redux についてとてもわかり易く解説された記事が Qiita にはたくさんありますので、ぜひそういった記事も並べて確認してもらうと、理解が深まると思います。
一番のおすすめはこちらの記事ですね。

Redux入門【ダイジェスト版】10分で理解するReduxの基礎

React の Props / Redux の state

ここまで来て、ReactとReduxをキチンと理解せず(説明せず)先に行くことに、ものすごく違和感を覚えました。しかしこの記事の中で表現するにはあまりにも大きい。そこで、別の記事で書くことにしました。
次のリンクから、ReactとReduxのことについて掴んでもらえたらなぁと思います。

TODO: 近いうちに書くので・・・

ReactからReduxへ、そしてまたReactへ

ReactとReduxのことについては軽く掴んでいただいているという仮定のもと、 Counter コンポーネントについての説明を続けます。
Counter コンポーネントがどのように動いているか、ボタンを押したところから何が起きているのかをたどっていきましょう。

1. ボタンを押す : onClick イベントの発生

onClick={()=>{this.props.increment();}}>

ボタンを押すと、onClick イベントが発生します。 onClick イベントの実体は、アロー関数で表現されていますね。

()=>{this.props.increment();}

this.propsincrement()を実行していますね。

はてはて、 increment()なんてどこに実装されているんだろう・・・ となりますね。
ここで React-Redux が登場します。 結論から申し上げますと、 CounterStore.actionCreatorsここで定義されています。 Counter コンポーネントの一番最後にこの記述がありますね。

exportdefaultconnect((state:ApplicationState)=>state.counter,CounterStore.actionCreators)(Counter);

ここで登場するのが connect()です。 connect()は React-Redux の主役みたいなものです。 React のコンポーネントと、 Redux の State や ActionCreator を結びつける役割を持っています。 
Redux についてはぜひ別記事を見ていただきたいですが、誤解を覚悟でとても簡単にいうならば、 状態をもっていて、その状態を次の状態に変えるための ルールを持っています。
connect()は Redux が持っている 状態ルールを React コンポーネントに対して公開してあげる役割をもっています。

そういうことで、 Counter コンポーネントでは

  • 状態 : state
    • Redux の state のうち、 counter だけを React に公開する (propsが受け取る)
      • props.count でアクセスできる (counter は count を持っている)
  • ルール : actionCreator
    • CounterStore.actionCreators に定義された関数を React に公開する (propsが受け取る)
      • props.increment() で呼び出せる (increment() は actionCreators に定義されている)

ということが行われています。
では、話を戻して increment()を見ていきましょう。 increment()は CounterStore.actionCreators に定義されているといいました。 ではそのソースコードをみていきましょう。 Counter.tsx ファイルの初めの方で、 import されている箇所を確認してみてください。

import*asCounterStorefrom'../store/Counter';

CounterStore は store/Counter.ts からインポートしているようですね。 では、 Counter.ts を見てみましょう。

exportconstactionCreators={increment:()=>({type:'INCREMENT_COUNT'}asIncrementCountAction),decrement:()=>({type:'DECREMENT_COUNT'}asDecrementCountAction)};

actionCreators が定義されていますね。 increment()の実体はここにありました。 では、increment()はどんなことをしているのでしょう・・・。

increment()もまた、アロー関数で実体が定義されていますね。

()=>({type:'INCREMENT_COUNT'}asIncrementCountAction)

INCREMENT_COUNTという Type でアクションが設定されるというものです。 ここは Redux の動きになりますね。 Redux は store を持っており、 store は 現在の state と、 次の state を計算するための Reducer を持っています。 Reducer は 現在の state と action を使って次の state を計算します。ということは、 action は store に渡されて初めて、 state の更新がかかることになります。

では、 increment()で作られたアクションが state まで影響を及ぼすことを確認しましょう。
まず、 Counter.ts に定義されている reducer を見ていきましょう。 reducer では、アクションに応じて state を変化させていることがわかると思います。 この reducer は次にどこで使われているのでしょうか・・・ここはちょっと力技ですが、頑張って探すと、 index.ts にあるとわかりました。 import されているところを検索すればすぐに見つかります。

exportconstreducers={counter:Counter.reducer,weatherForecasts:WeatherForecasts.reducer};

index.ts ではこのように、ふたつの Reducer がまとめられていますね。 WeatcherForecasts.reducer については、 Fetch data のページの話ですので、ここでは触れずに、 まとめて扱うんだね、そうした方が便利なのかな?というくらいに考えておいていただければOKです。

では、続いてこの reducersはどこへいったのでしょう。 こちらも力技ですが、検索すると configureStore.tsにありました。 combineReducersでまた結合されていますね。 そのあと、 createStoreで使われています。 やっと storeが出てきてくれました。

store が state と reducer を持つというところまでやってきましたね。では、 createStore で作られた store インスタンスはどこにあるのでしょう・・・。
store は Redux のルールとして、 Provider タグを使ってコンポーネントに公開するという仕様になっています。
つまり、今回の公開対象である App タグを Provider タグでラップして、 store を渡してあげればいいということになります。 これは index.tsx に記述がありますね。 Provider タグで store が渡されていることが見て取れますね。

ReactDOM.render(<Providerstore={store}><ConnectedRouterhistory={history}><App/></ConnectedRouter></Provider>,document.getElementById('root'));

これにて increment()の流れは完了です。 アクションの登録と、Provider までたどるということをやっておけば、およそのことは理解できてくるのかなぁと思っています。

普通、Redux では dispatch メソッドを使って store まで接続するのですが、React-Redux を使っているので、明示的に呼ぶ必要がなかったですね。

次回は Counter を Todo に変えるよ

Counterをちょっとカスタマイズして、Todoアプリのようなものを作っていこうと思います。
Todoを作ることは、その言語を使えるようになったぞといっていい一つの基準になると思っていますし、とても良い題材だと思っています。

また次回、よろしくおねがいします。(`・ω・´)ゞ


Viewing all articles
Browse latest Browse all 9289

Trending Articles