Upgrade to Pro — share decks privately, control downloads, hide ads and more …

React-Redux-Redux-Saga-Workshop02

 React-Redux-Redux-Saga-Workshop02

From TIS internal workshop at 2019/7/30

tashxii

July 30, 2019
Tweet

More Decks by tashxii

Other Decks in Programming

Transcript

  1. React / Redux / Redux-Saga • React … UIライブラリ •

    Redux … React の状態管理ライブラリ • Redux-Saga … 非同期処理を扱うReduxのミドルウェア 2 https://ja.reactjs.org/ https://Redux-Saga.js.org/ https://redux.js.org/ Skip可:前回と同じスライド
  2. 題材に使うアプリケーション • タスク管理アプリ • イメージ(gif) • 機能 • サインアップ •

    ログイン • ボード管理 • ユーザー管理 • タスク管理 • ドラッグ&ドロップ • Push通知 3 Skip可:前回と同じスライド
  3. 題材のアプリで使用しているライブラリ • Redux 状態管理ライブラリ • Redux-Saga 非同期処理用ライブラリ • styled-component コンポーネントのスタイル管理

    • Ant design コンポーネントライブラリ • react-beautiful-dnd ドラッグ&ドロップコンポーネント • react-router-dom URL遷移 • Font awesome アイコンライブラリ 5 Skip可:前回と同じスライド
  4. ソースコード • Front-end (React) https://github.com/tashxii/taskboard-react • Back-end (Go) https://github.com/tashxii/taskboard-api-go git

    clone https://github.com/tashxii/taskboard-react.git cd taskboard-react yarn install git clone https://github.com/tashxii/taskboard-api-go.git cd taskboard-api-go dep ensure go build 6 Skip可:前回と同じスライド
  5. Reactを使うアプリケーションの構成 https://ja.reactjs.org/ 8 • Reactは、Viewを提供するライブラリであり、それ以外の技術スタックを 組み合わせて使うことが前提 以下のような組み合わせで使う • Redux …

    アプリケーションの状態管理フレームワークを使用したり、 • Redux-Saga … 非同期処理を扱うライブラリを使ったり、 • Back-end サーバー(RESTやSOAPサーバー)と組み合わせて構成する ここを取り上げます
  6. Fluxアーキテクチャ • MVVMの双方向バインディ ングなどと異なり、ビューモ デルが「単一方向」で更新 されるアーキテクチャ • 右はイメージ(gif) • View(コンポーネント)と

    Reducers(状態遷移)を 実装すればアプリの 画面が更新される 11 http://slides.com/jenyaterpil/redux-from-twitter-hype-to-production#/27
  7. Reduxの構成要素 • 基本的に以下の5つから構成される 1. Store (State) 2. View (Component) 3.

    Actions & Action Creator 4. Container (Dispatcher) 5. Reducers • MiddlewareはRedux-sagaなどが あり、非同期APIを呼ぶ場合もある 12 1 2 3 4 5
  8. アプリケーションの状態とは? 13 • 例えば以下のような状態すべてを 含みます • ログインユーザーの情報 • ボードの一覧 •

    ユーザーの一覧 • ボードごとのタスク一覧 • エラー情報 ログイン情報 ボード一覧 ボードごとの タスク一覧 ユーザー一覧 エラー情報
  9. Redux – Store (state) • アプリケーション全体の状態を表す • combineReducers を使って小さな 状態の集まりに粒度化できる

    15 const appState = combineReducers({ loginState, mainState, boardsState, tasksState, usersState, errorState, }) export default appState const initialState = { boardIdToTasks: {}, isSavingNewTask: false, isSavingTask: false, boardIdToMoving: {} } tasksState.js const initialState = { users: [], isLoadingUsers: false, } usersState.js const store = createStore( appState, applyMiddleware(sagaMiddleware) ) sagaMiddleware.run(rootSaga) render( <Provider store={store}> <App /> </Provider>, document.getElementById("root") ) index.js reducers/index.js アプリケーション コンポーネント Store ボードごとの タスク一覧 ユーザー一覧 ・ ・ ・ ・ ・ ・ ・ ・ ・
  10. Redux – View (Component)の役割 • コンポーネントはJSXで書かれる • 親から渡される読み取り専用の 状態 props

    と、自身で書き込み 可能な状態 state を持つ • 画面のコントロール(ボタンや セレクトボックス)のイベントで、 それらの状態を変える関数を 呼び出す 16 TaskFrom コンポーネント UserFrom コンポーネント TaskCard コンポーネント Avatar コンポーネント Avatar コンポーネント
  11. Redux – View (Component)の役割 17 UserFrom コンポーネント class UserForm extends

    Component { constructor(props) { super(props) const user = props.user this.state = { name: user.name, password: user.password, avatar: user.avatar, } this.handleTextChange = this.handleTextChange.bind(this) this.handleAvatarChange = this.handleAvatarChange.bind(this) } 親から渡された props の user 画面が持つstate name password avatar の3フィールド stateを変える 関数
  12. Redux – View(Component)の役割 18 <Col span={8}> {I18n.get("アカウント")}: </Col> <Col span={16}>

    <Input autoFocus={true} value={this.state.name} onChange={(e) => this.handleTextChange(e, "name")} /> </Col> // snip handleTextChange(e, key) { this.setState({ [key]: e.target.value }) } onChange イベントハンドラ 入力値が変わったら setState関数で値を更新 onChange イベントハンドラ • stateは、setState関数で 更新する
  13. Redux – View(Component)の役割 19 <Button type="primary" onClick={ () => {

    const user = new User( this.props.user.id, this.state.name, this.state.avatar, this.props.user.version, ) user.newPassword = this.state.password this.props.onSaveButtonClick(user) } } disabled={disabled} loading={this.props.isSavingProcessing} style={{ float: "right" }} > {(!this.props.isSavingProcessing) ? I18n.get("保存") : I18n.get("保存中") } </Button> onClick イベントハンドラ onClick イベントハンドラ 画面が持つstateから、 Userインスタンスを作成 親から渡されたprops のコールバック関数を 呼び出し • propsは、親のコールバック 関数で更新する
  14. Redux – Action / Action Creator • Action • UIやAPI呼び出しなどのイベント定義変数

    • Action Creator • typeとpayloadと呼ばれるパラメータを持つ関数 20 export const UPDATE_LOGIN_USER_START_EVENT = "UPDATE_LOGIN_USER_START_EVENT" export const UPDATE_LOGIN_USER_SUCCESS_EVENT = "UPDATE_LOGIN_USER_SUCCESS_EVENT" export const UPDATE_LOGIN_USER_FAILURE_EVENT = "UPDATE_LOGIN_USER_FAILURE_EVENT" export const updateLoginUserStartEvent = (user) => ({ type: UPDATE_LOGIN_USER_START_EVENT, payload: { user: user, } }) 保存ボタンのonClick イベントハンドラで 呼び出す UserFormで生成した Userインスタンスが 渡される
  15. Redux – Container • コンポーネントと状態、Action の関数を接続させる役割を持 つ • mapStateToPropsでStoreの stateの一部とマッピングし、

    mapDispatchToPropsでAction イベント関数をdispatchする • mapStateToPropsと mapDispatchToPropsを connect関数で接続する 21 import { connect } from "react-redux" import { withRouter } from "react-router-dom" import MenuBarPage from "../components/pages/MenuBarPage" import { updateLoginUserStartEvent, logoutStartEvent, switchMainViewEvent, } from "../actions" const mapStateToProps = (state) => ({ loginState: state.loginState, }) const mapDispatchToProps = (dispatch) => ({ switchMainViewIconClick: (view) => { dispatch(switchMainViewEvent(view)) }, onUserProfileSaveButtonClick: (user) => { dispatch(updateLoginUserStartEvent(user)) }, onLogoutIconClick: () => { dispatch(logoutStartEvent()) }, }) const MenuBarContainer = connect( mapStateToProps, mapDispatchToProps, )(MenuBarPage) export default withRouter(MenuBarContainer) アプリケーション全 体の状態の一部と マップ 画面からのcallback関数で、 Actionをdispatch(送出)する
  16. Redux –Reducers • ロジックを司る関数 • 古い状態から新しい状態を作成する • Actionのtypeとpayloadによって状態を変える 22 const

    loginState = (state = initialState, action) => { const type = action.type const payload = action.payload switch (type) { // snip case UPDATE_LOGIN_USER_START_EVENT: return { ...state, isSaveUserProcessing: true } case UPDATE_LOGIN_USER_SUCCESS_EVENT: return { ...state, isSaveUserProcessing: false, loginUser: payload.user } case UPDATE_LOGIN_USER_FAILURE_EVENT: return { ...state, isSaveUserProcessing: false } // snip } } 編集したUserとログイン ユーザーを差し替える
  17. Reduxのフォルダ構成 • 大きく分けて、2通りのやり方がある • Function-First : actions, components, containers, reducers

    と機能単位で構成するやり方 規模が小さい場合に分かりやすい • Feature-First : ドメインや画面、データ単位で構成するやり方 ボリューム増でスケールする 24 https://www.freecodecamp.org/news/scaling-your-redux-app-with-ducks-6115955638be/
  18. Componentのフォルダ構成 – Atomic Design • Atomic Design というコンポーネントの分類手法がある • コンポーネントを以下の5種に分類する

    • Atoms … 原子:これ以上分割できない最小単位のコンポーネント • Molecules … 分子:原子が組み合わさったもの • Organisms … 組織:分子や原子の組み合わせで画面を構成するもの • Templates … テンプレート:ページの雛形 • Pages … ページ:最上位のコンポーネント。コンテナはページとだけ結合する 26
  19. Componentのフォルダ構成 – Atomic Design • ContainersはPagesとだけ結合させ、画面コントロールのイベントのコールバックは Atoms まで引き回して実行させる • こうすることで、イベントとコンポーネントの独立性を高め、Organisms以下を再利用しやすくできる

    27 class MenuBarPage extends Component { render() { return ( <MenuBarPageTemplate {...this.props} /> ) } } class MenuBarPageTemplate extends Component { render() { return ( <MenuBar {...this.props} /> ) } } const mapStateToProps = (state) => ({ loginState: state.loginState, }) const mapDispatchToProps = (dispatch) => ({ switchMainViewIconClick: (view) => { dispatch(switchMainViewEvent(view)) }, onUserProfileSaveButtonClick: (user) => { dispatch(updateLoginUserStartEvent(user)) }, onLogoutIconClick: () => { dispatch(logoutStartEvent()) }, }) const MenuBarContainer = connect( mapStateToProps, mapDispatchToProps, )(MenuBarPage) export default withRouter(MenuBarContainer) MenuBarPage.jsx MenuBarPageTemplate.jsx MenuBarContainer.js MenuBar.jsx Icon Avatar Icon MenuBarContainerから、 MenuBarPageを接続し、 MenuBarPageTemplate、 MenuBar、Iconへとprops を伝播させている Iconのクリックイベントは、 MenuBarContainerの コールバックを呼び出す Callback
  20. サンプルアプリケーションのコンポーネント構成 以下の理由により4種にまとめた • Ant Designというコンポーネントライブラリを使用するため、 Atoms はほとんど作成しない • Atoms と

    Molecules を basics という名前でまとめた • OrganismsとMoleculesの分類があいまいになるため、画 面のパーツ(ダイアログやフォーム)として再利用可能なも のを parts という名前にした 28 4種に分類 コンテナは Page単位で 作成
  21. 覚えていてほしいこと • Redux … アプリケーションの状態管理のReactのためのライブラリ • Store … アプリケーション全体の状態。細分化可能 •

    Component … 画面コンポーネント。状態を持つ • Action … 画面イベントで送出されるパラメータ • Container … 画面と状態、イベントのマッピングをする • Reducers … 状態遷移のロジックを記述する • Function-FirstとFeature-Firstというフォルダ構成の取り方があること • Atomic Designというコンポーネントの階層化手法があること 29