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-Workshop01
Search
tashxii
July 22, 2019
Programming
0
96
React-Redux-Redux-Saga-Workshop01
From TIS internal workshop at 2019/7/22
tashxii
July 22, 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-Workshop02
tashxii
0
110
Other Decks in Programming
See All in Programming
Deep Dive 大規模システムアーキテクチャ/開発組織エンジニアリング / Deep Dive Large-Scale System Architecture, Development Organization Engineering
nrslib
15
2.9k
決断するための勇気、そのためのBacklog / Courage to make decisions, Backlog for that.
seike460
PRO
4
1.9k
どうしてこうなった命名集 ~🔥編~ / OOC 2024 LT
pictiny
4
2.8k
C# 大統一理論推進委員会 会員のための Unity Package Manager プロジェクト構成案
monry
PRO
0
580
品質が高いコードって何?Rev2.1
ickx
1
490
Cloud RunとCloud PubSubでサーバレスなデータ基盤2024 with Terraform / Cloud Run and PubSub with Terraform
shinyorke
7
1.9k
OpenAPI を守るのは難しい
ohmori_yusuke
1
140
WasmOS: Wasmを実行する自作Microkernel
riru
0
370
ドメイン・ファーストで考える問題解決に役立つモデル設計 / Domain First Model Design
suzushin54
1
1.4k
PHP 8.3で追加されたjson_validate()を徹底的に深掘りしてみよう
mashirou1234
1
720
クソコード動画『カプセル化 Mk-II』 で考える 上手くカプセル化できない理由 / encapsulation2
minodriven
11
7.9k
Dockerで始めるAWS Lambda開発
stutkhd0709
13
2.5k
Featured
See All Featured
Thoughts on Productivity
jonyablonski
57
3.8k
Building a Scalable Design System with Sketch
lauravandoore
455
32k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
1
3.3k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
272
12k
The MySQL Ecosystem @ GitHub 2015
samlambert
242
12k
Embracing the Ebb and Flow
colly
78
4.1k
Put a Button on it: Removing Barriers to Going Fast.
kastner
58
3k
Why You Should Never Use an ORM
jnunemaker
PRO
50
8.6k
Imperfection Machines: The Place of Print at Facebook
scottboms
257
12k
Fontdeck: Realign not Redesign
paulrobertlloyd
75
4.8k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
8
8.2k
Typedesign – Prime Four
hannesfritz
36
2k
Transcript
React + Redux + Redux-Saga 勉強会 ① 概要編 2019/7/22 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/
題材に使うアプリケーション • タスク管理アプリ • イメージ(gif) • 機能 • サインアップ •
ログイン • ボード管理 • ユーザー管理 • タスク管理 • ドラッグ&ドロップ 3
題材のアプリで使用しているライブラリ • Redux 状態管理ライブラリ • Redux-Saga 非同期処理用ライブラリ • styled-component コンポーネントのスタイル管理
• Ant design コンポーネントライブラリ • react-beautiful-dnd ドラッグ&ドロップコンポーネント • react-router-dom URL遷移 • Font awesome アイコンライブラリ 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 5
Reactとは • Facebook製のJSライブラリ • Viewに特化しているフロントエンドライブラリ • Github スター数 13.3k https://ja.reactjs.org/
6
Popular Front-end Frameworks • 2018年のhttps://2018.stateofjs.com/front-end-frameworks/overview/ 調べ 今使っているが、 また使いたい 今使っているが もう使いたくない
聞いたことがあり、 習いたい 聞いたことはある が、興味はない 聞いたことはある が、興味はない React Vue Angular 7
特徴 • Virtual DOM(仮想)による直感的なUI実装 • コードとHTML要素をミックスして記述 • コンポーネント指向 • 豊富なライブラリやコンポーネントが存在
• 自身のコンポーネントを簡単に作成 <div> <Row style={{ marginTop: "10px" }}> <Col style={{ float: "right", marginRight: "5px" }}> <Button type="primary" onClick={this.toggleNewTaskDialog} disabled={spininig} > {I18n.get("新しいタスク")} </Button> </Col> <Col style={{ float: "right", marginRight: "5px" }}> <Input placeholder={I18n.get("タスクのフィルター")} onChange={(e) => this.handleTaskFilterChange(e)} disabled={spininig} allowClear /> </Col> </Row> <Row style={{ marginTop: "2px" }}> { boards.map(board => { return ( <BoardLane key={board.id} board={board} taskFilter={this.state.taskFilter} {...this.props} /> ) }) } </Row> </div> タグとコードがミックス 8
どんなことができる? • Gmailや、Twitter、FacebookなどのSPA(Single Page Application)を構 成できる • リッチなユーザー体験をもたらすUIを提供できる • React-NativeやElectronなどと併用することで、フロントエンド技術だけ
でWebページと同様な多様さを持つネイティブアプリケーションを開発で きる 9
どんな構成で使う? • Reactは、Viewを提供するライブラリであり、それ以外の技術スタックを 組み合わせて使うことが前提 以下のような組み合わせで使う • Redux … アプリケーションの状態管理フレームワークを使用したり、 •
Redux-Saga … 非同期処理を扱うライブラリを使ったり、 • Back-end サーバー(RESTやSOAPサーバー)と組み合わせて構成する 10
プロジェクトを作る (create-react-app) 11
create-react-app • Front-endの技術スタックは組み合わせが複雑で管理が大変 • babel, webpack, browserify, jest, … •
Facebook製のプロジェクト作成ツールを使えば簡単に作成可能 • create-react-app • npx • npm • yarn https://facebook.github.io/create-react-app/docs/getting-started 12 npx create-react-app my-app npm init react-app my-app yarn create react-app my-app
Virtual DOM(仮想DOM) 13
コンポーネントのルール • Reactの2通りの定義方法がある • React.Componentを継承したクラスを作成する方法 • ライフサイクルやstateを持つ場合 • 純粋なrender関数として定義する方法 •
どちらでもrender関数は1つのHTMLのDOM要素を返すことがルール になっている。 const Avatar = (props) => { const setting = ApplicationSetting.getServerSetting() const src = `${setting.url}/${setting.base}/${setting.img}/${props.avatar}${setting.ext}` const size = props.size || "50px" const alt = props.alt || "No Image" return ( <img src={src} width={size} height={size} borderstyle={"solid"} border={"thin solid gray"} align={"center"} alt={alt} /> ) } class MainPage extends Component { render() { return ( <MainPageTemplate {...this.props} /> ) } } 14
propsとstate • コンポーネントは、以下の2つの状態を持つ。 • props … 親のコンポーネントから渡される読み取り専用の値 • 親から渡されるオブジェクトやコールバックなどを持つ。 •
state … 自身のコンポーネントで持つ、編集可能な値 • チェックボックスや入力中のフィールド値などを持ち、setStateで更新する。 class MenuBar extends Component { constructor(props) { super(props) this.state = { showProfileDialog: false } this.toggleProfileDialog = this.toggleProfileDialog.bind(this) } render() { const loginUserName = this.props.loginState.loginUser.name || "" const avatarName = this.props.loginState.loginUser.avatar || "" return ( // ... 15
変数を使うには? • .jsxの中では、「{」と「}」で括って変数やプログラミング要素を記述する。 class LoginForm extends Component { constructor(props) {
super(props) this.state = { name: "", password: "", showSignUpDialog: false, } this.handleTextChange = this.handleTextChange.bind(this) this.toggleSignUpDialog = this.toggleSignUpDialog.bind(this) } render() { const loginLabel = (!this.props.loginState.isLoginProcessing) ? I18n.get("ログイン") : I18n.get("ログイン中") const disabled = this.props.loginState.isLoginProcessing || this.state.name === "" return ( <div> <Col> <Row> <div style={{ fontSize: "32px" }}> <font color="cornflowerblue">Taskboard React</font> </div> </Row> // ... 16
if文を使うには? • 「{」と「}」の中でif文を書く。 • 三項演算子を使う。 • (cond) ? <DOM/> :
null である条件の時だけDOMを 表示する class ErrorCard extends Component { render() { const errorMessage = this.props.errorMessage const summary = errorMessage.summary const summaryColor = this.props.summaryColor || "red" const detailColor = this.props.detailColor || "gray" let detail = "" if (errorMessage.detail) { detail = (errorMessage.code) ? `${errorMessage.detail}(${errorMessage.code})` : `${errorMessage.detail}` } return ( <div style={this.props.cardStyle}> <div> <font color={summaryColor}> {summary} </font> </div> {(detail) ? <div> <font color={detailColor}> {detail} </font> </div> : null} </div> ) } } 17
forループを使うには? • 配列要素で、render関数(DOM要素を 返す関数)を記述することができる。 class BoardLane extends Component { render()
{ return ( <Lane> <TaskList style={styles}> { tasks.map((task) => { return ( <TaskCard key={task.id} index={task.dispOrder} task={task} taskUser={userMap[task.assigneeUserId]} onEditButtonClick={this.props.onUpdateTaskButtonClick} onDeleteButtonClick={this.props.onDeleteTaskButtonClick} isSavingProcessing={this.props.tasksState.isSavingTask} users={this.props.usersState.users} boards={this.props.boardsState.boards} /> )}) } </TaskList> </Lane> ) } } tasks.map((task)=>{ return <DOM要素> }) 18
自身のコンポーネントの状態を変えるには? • this.setState関数でstateを変更することができる • setStateを呼び出すと再描画(renderの呼び出し)がされる class LoginForm extends Component {
constructor(props) { super(props) this.state = { name: "", password: "", showSignUpDialog: false, } this.handleTextChange = this.handleTextChange.bind(this) this.toggleSignUpDialog = this.toggleSignUpDialog.bind(this) } ダイアログの表示フラグ 19 toggleSignUpDialog() { this.props.clearLoginErrors() this.setState({ showSignUpDialog: !this.state.showSignUpDialog, }) } フラグの切り替え
親のコンポーネントの状態を変えるには? • コールバックを呼び出すことで、reducerを通してpropsや親のstateを変 えることができる。 <Modal visible={this.state.showEditTaskDialog} onCancel={this.toggleEditTaskDialog} footer={null} destroyOnClose width={500}
> <TaskForm mode={TaskForm.Mode.Edit} task={task} users={this.props.users} boards={this.props.boards} onSaveButtonClick={this.props.onEditButtonClick} isSavingProcessing={this.props.isSavingProcessing} /> </Modal> タスク表示 コンポーネント ダイアログのOKボタンで コンテナのタスク変更 関数が呼び出される 20
状態管理 - Redux 21
アプリケーションの状態管理 • Reduxというライブラリを使用して管理する • React だけでも出来るが、一定規模以上でライブラリを使用しないのは管理が困難 • Redux • 「Store」と呼ばれる状態をアプリケーション全体で管理する
• 状態は細分化して保持することもできる • Fluxと呼ばれるアーキテクチャの実装がRedux 22
Fluxアーキテクチャ • MVVMの双方向バイン ディングなどと異なり、 ビューモデルが「単一方 向」で更新されるアーキテ クチャ • 右はイメージ(gif) •
View(コンポーネント)と Reducers(状態遷移)を 実装すればアプリの 画面が更新される 23 http://slides.com/jenyaterpil/redux-from-twitter-hype-to-production#/27
Redux – Store / Component • Store • アプリケーション全体の状態を表す •
combineReducers を使って小さな状態の集まりに粒度化できる • Component • JSXで書かれたコンポーネント • 親から渡される読み取り専用のpropsと自身が持ち書き込み可能な stateを持つ 24
Redux – Action / Reducers • Action / ActionCreater •
UIやAPI呼び出しなどのイベント定義 • typeとpayloadと呼ばれるパラメータを持つ • Reducer • 状態を遷移させる純粋関数群 • 古い状態から新しい状態を作成する • Actionのtypeのcaseに応じて処理を書く • combineReducerというAPIで細粒度化可能 25 export const LOGIN_START_EVENT = "LOGIN_START_EVENT" export const loginStartEvent = (name, password) => ({ type: LOGIN_START_EVENT, payload: { name, password, } })
Redux – Container • Container • コンポーネントと状態、Action の関数を接続させる役割を持 つ •
mapStateToPropsでStoreの stateをマッピングし、 mapDispatchToPropsでAction イベント関数をdispatchする • mapStateToPropsと mapDispatchToPropsを connect関数で接続する 26 import { connect } from "react-redux" import { withRouter } from "react-router-dom" import LoginPage from "../components/pages/LoginPage" import { loginStartEvent, signUpStartEvent, clearLoginErrorsEvent, } from "../actions" const mapStateToProps = (state) => ({ loginState: state.loginState, }) const mapDispatchToProps = (dispatch) => ({ onLoginButtonClick: (name, password) => { dispatch(loginStartEvent(name, password)) }, onSignUpButtonClick: (userCreateRequest) => { dispatch(signUpStartEvent(userCreateRequest)) }, clearLoginErrors: () => { dispatch(clearLoginErrorsEvent()) }, }) const LoginContainer = connect( mapStateToProps, mapDispatchToProps, )(LoginPage) export default withRouter(LoginContainer) 画面からのcallback関数 とdispatchのマップ アプリケーション全 体の状態の一部と マップ
タスク管理アプリの状態 • 以下の状態を定義&管理 • ログイン情報 • ボード一覧 • ユーザー一覧 •
ボードごとのタスク一覧 • エラー情報 27 ログイン情報 ボード一覧 ボードごとの タスク一覧 ユーザー一覧 エラー情報
Redux - storeのindex.js • combineReducersという関数で状態をマージ 28 import { combineReducers }
from "redux" import loginState from "./loginState" import mainState from "./mainState" import boardsState from "./boardsState" import tasksState from "./tasksState" import usersState from "./usersState" import errorState from "./errorState" const appState = combineReducers({ loginState, mainState, boardsState, tasksState, usersState, errorState, }) export default appState ログインユーザーオブジェクト ログイン中フラグ ログアウト中フラグ ・・・ ボードID-タスク配列のマップ 新規タスク作成中状態 タスク移動中フラグ ・・・
非同期処理 - Redux-Saga 29
Redux-Saga • Redux-Saga とは、非同期処理を取 り扱うReduxのミドルウェアライブラリ • 非同期処理を手続型で記述すること が可能 • App.jsxで、applyMiddlewareを行う
ことで利用可能 30 import React from "react" import { render } from "react-dom" import { Provider } from "react-redux" import { createStore, applyMiddleware } from "redux" import createSagaMiddleware from "Redux-Saga" import App from "./components/App" import appState from "./reducers" import rootSaga from "./sagas/saga" const sagaMiddleware = createSagaMiddleware() const store = createStore( appState, applyMiddleware(sagaMiddleware) ) sagaMiddleware.run(rootSaga) render( <Provider store={store}> <App /> </Provider>, document.getElementById("root") )
Redux-Sagaの基本 • 非同期処理のイベントは、「Xxx開始」、「Xxx成功」、「Xxx失敗」の3つに分かれる • 画面イベントを受け取るジェネレーター関数を fork することでイベントを監視 • イベントは、takeEvery, call,
putで処理される • takeEvery … イベントの待ち受け • call … 非同期処理の呼び出し • put … 次のイベントの実行 31 function* handleCreateTask() { yield takeEvery(CREATE_TASK_START_EVENT, createTask) } function* createTask(action) { const payload = action.payload const { task, error } = yield call( TaskService.createAsync, payload.task) if (!error) { yield put(createTaskSuccessEvent(task)) } else { yield put(createTaskFailureEvent(error)) } } 待ち受け 非同期呼び出し 次のイベント実行 export default function* rootSaga() { let sagaFunctions = [] sagaFunctions = sagaFunctions.concat(UserSagas.sagaFunctions()) sagaFunctions = sagaFunctions.concat(BoardSagas.sagaFunctions()) sagaFunctions = sagaFunctions.concat(TaskSagas.sagaFunctions()) for (let i = 0; i < sagaFunctions.length; i++) { yield fork(sagaFunctions[i]) } } イベントハンドラ (ジェネレータ関数)の登録 イベントハンドラ (ジェネレータ関数)
覚えていてほしいこと • React というUIのためのJSライブラリ • Virtual DOM = UI記述のための拡張JSと、コンポーネント指向という特徴を持つ •
Redux … アプリケーションの状態管理のReactのためのライブラリ • Redux-saga … 非同期処理を扱うReduxのミドルウェア • create-react-app で簡単にアプリケーションの雛形が作れる 32