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
100
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
47
React-Redux-Redux-Saga-Workshop03
tashxii
0
170
React-Redux-Redux-Saga-Workshop02
tashxii
0
120
Other Decks in Programming
See All in Programming
#kanrk08 / 公開版 PicoRubyとマイコンでの自作トレーニング計測装置を用いたワークアウトの理想と現実
bash0c7
1
770
Team operations that are not burdened by SRE
kazatohiei
1
310
AIともっと楽するE2Eテスト
myohei
6
2.6k
“いい感じ“な定量評価を求めて - Four Keysとアウトカムの間の探求 -
nealle
1
10k
Node-RED を(HTTP で)つなげる MCP サーバーを作ってみた
highu
0
120
データの民主化を支える、透明性のあるデータ利活用への挑戦 2025-06-25 Database Engineering Meetup#7
y_ken
0
360
「テストは愚直&&網羅的に書くほどよい」という誤解 / Test Smarter, Not Harder
munetoshi
0
170
A full stack side project webapp all in Kotlin (KotlinConf 2025)
dankim
0
120
AIエージェントはこう育てる - GitHub Copilot Agentとチームの共進化サイクル
koboriakira
0
590
A2A プロトコルを試してみる
azukiazusa1
2
1.4k
PHPでWebSocketサーバーを実装しよう2025
kubotak
0
290
5つのアンチパターンから学ぶLT設計
narihara
1
170
Featured
See All Featured
How to Ace a Technical Interview
jacobian
278
23k
Gamification - CAS2011
davidbonilla
81
5.4k
Making the Leap to Tech Lead
cromwellryan
134
9.4k
StorybookのUI Testing Handbookを読んだ
zakiyama
30
5.9k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
34
3.1k
Scaling GitHub
holman
460
140k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
3.9k
Why You Should Never Use an ORM
jnunemaker
PRO
58
9.4k
The Pragmatic Product Professional
lauravandoore
35
6.7k
Rails Girls Zürich Keynote
gr2m
95
14k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
251
21k
Building Flexible Design Systems
yeseniaperezcruz
328
39k
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