Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
React-Redux-Redux-Saga-Workshop02
Search
tashxii
July 30, 2019
Programming
0
110
React-Redux-Redux-Saga-Workshop02
From TIS internal workshop at 2019/7/30
tashxii
July 30, 2019
Tweet
Share
More Decks by tashxii
See All by tashxii
React-Redux-Redux-Saga-Workshop04
tashxii
0
39
React-Redux-Redux-Saga-Workshop03
tashxii
0
120
React-Redux-Redux-Saga-Workshop01
tashxii
0
96
Other Decks in Programming
See All in Programming
TDDと今まで
kanayannet
0
140
TCAの Shared Stateって どういう仕組みになってんの?
yimajo
0
330
Cloud RunとCloud PubSubでサーバレスなデータ基盤2024 with Terraform / Cloud Run and PubSub with Terraform
shinyorke
7
1.9k
[スクリプト] Swiftの型推論を学ぼう
omochi
0
110
OpenTelemetry のサービスという概念について
azukiazusa1
1
390
Swiftの型推論を学ぼう | Let's Learn About Type Inference in Swift
omochi
2
510
Open Source Swiftc Workshop
kitasuke
1
180
プロンプトエンジニアリング入門
tomokusaba
2
970
htmx is fun!
codehex
2
190
どうしてこうなった命名集 ~🔥編~ / OOC 2024 LT
pictiny
4
2.8k
AppDeveloperCon 2024 EU: Building polyglot developer experiences in 2024
salaboy
0
380
BuefyのMaintainerを引き継いだ件
kikuomax
0
520
Featured
See All Featured
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
1
1.2k
Imperfection Machines: The Place of Print at Facebook
scottboms
257
12k
Designing the Hi-DPI Web
ddemaree
275
33k
GraphQLとの向き合い方2022年版
quramy
28
12k
Designing with Data
zakiwarfel
94
4.8k
StorybookのUI Testing Handbookを読んだ
zakiyama
10
4.5k
Visualization
eitanlees
135
14k
How GitHub (no longer) Works
holman
301
140k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
34
8.8k
Stop Working from a Prison Cell
hatefulcrawdad
265
19k
GitHub's CSS Performance
jonrohan
1023
450k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
12
1.4k
Transcript
React + Redux + Redux-Saga 勉強会 ② Redux編 2019/7/30 tashxii@tis
https://fintan.jp/
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可:前回と同じスライド
題材に使うアプリケーション • タスク管理アプリ • イメージ(gif) • 機能 • サインアップ •
ログイン • ボード管理 • ユーザー管理 • タスク管理 • ドラッグ&ドロップ • Push通知 3 Skip可:前回と同じスライド
題材に使うアプリケーション • Websocketを使ったPush通知 • イメージ(gif) • 他のクライアントの 操作をサーバー 経由で伝達 4
題材のアプリで使用しているライブラリ • Redux 状態管理ライブラリ • Redux-Saga 非同期処理用ライブラリ • styled-component コンポーネントのスタイル管理
• Ant design コンポーネントライブラリ • react-beautiful-dnd ドラッグ&ドロップコンポーネント • react-router-dom URL遷移 • Font awesome アイコンライブラリ 5 Skip可:前回と同じスライド
ソースコード • 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可:前回と同じスライド
Reactとは(ダイジェスト) 7
Reactを使うアプリケーションの構成 https://ja.reactjs.org/ 8 • Reactは、Viewを提供するライブラリであり、それ以外の技術スタックを 組み合わせて使うことが前提 以下のような組み合わせで使う • Redux …
アプリケーションの状態管理フレームワークを使用したり、 • Redux-Saga … 非同期処理を扱うライブラリを使ったり、 • Back-end サーバー(RESTやSOAPサーバー)と組み合わせて構成する ここを取り上げます
状態管理 - Redux 9
Reduxとは? • Reactアプリケーションを補うライブラリで、 • アプリケーション全体の状態を管理し、 • ある一定のルールに沿った実装をすることで、 • ReactアプリケーションでのUI上のイベントと、 •
それに伴う状態の変化をUI上に自動で 更新させるアーキテクチャを提供する。 (Fluxアーキテクチャと呼ばれる) 10 https://redux.js.org/
Fluxアーキテクチャ • MVVMの双方向バインディ ングなどと異なり、ビューモ デルが「単一方向」で更新 されるアーキテクチャ • 右はイメージ(gif) • View(コンポーネント)と
Reducers(状態遷移)を 実装すればアプリの 画面が更新される 11 http://slides.com/jenyaterpil/redux-from-twitter-hype-to-production#/27
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
アプリケーションの状態とは? 13 • 例えば以下のような状態すべてを 含みます • ログインユーザーの情報 • ボードの一覧 •
ユーザーの一覧 • ボードごとのタスク一覧 • エラー情報 ログイン情報 ボード一覧 ボードごとの タスク一覧 ユーザー一覧 エラー情報
アプリケーションの状態とは? 14 ユーザー一覧 ボード一覧 エラー情報
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 ボードごとの タスク一覧 ユーザー一覧 ・ ・ ・ ・ ・ ・ ・ ・ ・
Redux – View (Component)の役割 • コンポーネントはJSXで書かれる • 親から渡される読み取り専用の 状態 props
と、自身で書き込み 可能な状態 state を持つ • 画面のコントロール(ボタンや セレクトボックス)のイベントで、 それらの状態を変える関数を 呼び出す 16 TaskFrom コンポーネント UserFrom コンポーネント TaskCard コンポーネント Avatar コンポーネント Avatar コンポーネント
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を変える 関数
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関数で 更新する
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は、親のコールバック 関数で更新する
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インスタンスが 渡される
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(送出)する
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とログイン ユーザーを差し替える
Reduxを使った開発でのフォルダ構成 23
Reduxのフォルダ構成 • 大きく分けて、2通りのやり方がある • Function-First : actions, components, containers, reducers
と機能単位で構成するやり方 規模が小さい場合に分かりやすい • Feature-First : ドメインや画面、データ単位で構成するやり方 ボリューム増でスケールする 24 https://www.freecodecamp.org/news/scaling-your-redux-app-with-ducks-6115955638be/
サンプルアプリケーションのフォルダ構成 • Function-Firstで作成 25
Componentのフォルダ構成 – Atomic Design • Atomic Design というコンポーネントの分類手法がある • コンポーネントを以下の5種に分類する
• Atoms … 原子:これ以上分割できない最小単位のコンポーネント • Molecules … 分子:原子が組み合わさったもの • Organisms … 組織:分子や原子の組み合わせで画面を構成するもの • Templates … テンプレート:ページの雛形 • Pages … ページ:最上位のコンポーネント。コンテナはページとだけ結合する 26
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
サンプルアプリケーションのコンポーネント構成 以下の理由により4種にまとめた • Ant Designというコンポーネントライブラリを使用するため、 Atoms はほとんど作成しない • Atoms と
Molecules を basics という名前でまとめた • OrganismsとMoleculesの分類があいまいになるため、画 面のパーツ(ダイアログやフォーム)として再利用可能なも のを parts という名前にした 28 4種に分類 コンテナは Page単位で 作成
覚えていてほしいこと • Redux … アプリケーションの状態管理のReactのためのライブラリ • Store … アプリケーション全体の状態。細分化可能 •
Component … 画面コンポーネント。状態を持つ • Action … 画面イベントで送出されるパラメータ • Container … 画面と状態、イベントのマッピングをする • Reducers … 状態遷移のロジックを記述する • Function-FirstとFeature-Firstというフォルダ構成の取り方があること • Atomic Designというコンポーネントの階層化手法があること 29