[关闭]
@zhongjianxin 2018-07-20T09:55:13.000000Z 字数 7883 阅读 1103

OOCL-Redux Basic

OOCL


Tutorials:
Redux:https://www.valentinog.com/blog/react-redux-tutorial-beginners/
Redux handle API call: https://medium.com/@stowball/a-dummys-guide-to-redux-and-thunk-in-react-d8904a7005d3
Case study:https://blog.csdn.net/fengyinchao/article/details/51566555?from=singlemessage&isappinstalled=0
Basic Redux Starter 2018:https://www.valentinog.com/blog/react-redux-tutorial-beginners/#React_Redux_tutorial_Redux_store_methods

Why Redux

设计思路

(1)Web 应用是一个状态机,视图与状态是一一对应的。

(2)所有的状态,保存在一个对象里面。

image.png-293.2kB


When to use Redux

Component Based develioping

image.png-84kB

Redux + React develop steps

按任务分工来分步讲解

按照开发的内容,可以把前端团队分为两个小组: “布局组” 和 “逻辑组”,每个小组有2种任务,一共4种任务:

布局组 要求对 HTML + CSS 布局比较熟悉,只需要会简单的 js 即可, 不需要完整地理解redux流程。
逻辑组 要求对 js 比较熟悉,最好可以比较完整地理解redux流程, 但基本不需要涉及HTML + CSS布局工作。

class: middle center

Redux


class: middle center

Homework Review


layout: true

Application with Data


  1. props => <Component {...props} />
  2. ...
  3. data => <Application {...data} />

  1. const TodoMVC = props => (
  2. <Container>
  3. <Header>
  4. <Filters />
  5. </Header>
  6. <TodoList>
  7. <TodoItem>
  8. <Status />
  9. <Text />
  10. <TextEditor />
  11. </TodoItem>
  12. {...}
  13. </TodoList>
  14. <Footer />
  15. </Container>
  16. )

???
- Dynamic use input

- Shared data cross components

class: middle
layout: false

Redux

- Predictable State

layout: true

Data Flow


  1. data => <Application {...data} />
  2. ||
  3. (data <=> <Application />) <=> User
  4. ||
  5. State -----> View(Component)
  6. ︿ /
  7. \ /
  8. \
  9. User

layout: false
.center[

]

Redux Store and Reducer

  1. import { createStore } from 'redux'
  2. const counterReducer = (state = 0, action) => {
  3. switch (action.type) {
  4. case 'INCREMENT':
  5. return state + 1
  6. case 'DECREMENT':
  7. return state - 1
  8. default:
  9. return state
  10. }
  11. }
  12. const store = createStore(counterReducer)
  13. store.subscribe(() => {
  14. console.log(store.getState())
  15. })
  16. store.dispatch({ type: 'INCREMENT' })

Practice

Hint: You might need setState API to update Component

React Redux

  1. import { Provider as Redux, connect } from 'react-redux'
  2. const initialState = { count: 0 }
  3. const store = createStore(counterReducer, initialState)
  4. const App = props => <div>Redux Counter: {props.count}</div>
  5. const ConnectedApp = connect(state => state)(App)
  6. ReactDom.render(
  7. <Redux store={store}>
  8. <ConnectedApp />
  9. </Redux>
  10. , rootDom)

Reducer and Action

  1. const counterReducer = (state = 0, action) => {
  2. switch (action.type) {
  3. case 'INCREMENT':
  4. return state + 1
  5. case 'DECREMENT':
  6. return state - 1
  7. default:
  8. return state
  9. }
  10. }
  11. const incrementAction = { type: 'INCREMENT' }
  12. const store = createStore(counterReducer)
  13. store.dispatch(incrementAction)

class: middle center

Quick Practice React Redux

Refactor the previous Practice with react-redux


layout: true

Thunk


A thunk is a function that wraps an expression to delay its evaluation.

  1. let x = 1 + 2;
  2. let foo = () => 1 + 2;

  1. const incrementAction = { type: 'INCREMENT' }
  2. const incrementActionThunk = () => ({ type: 'INCREMENT' })
  3. const incrementActionAsyncThunk = (dispatch) => {
  4. setTimeout(() => {
  5. dispatch(incrementAction)
  6. }, 1000)
  7. }
  8. const incrementIfOddActionAsyncThunk = (dispatch, getState) => {
  9. const { counter } = getState();
  10. if (counter % 2 !== 0) {
  11. dispatch(incrementAction)
  12. }
  13. }

layout: false
.center[

]


layout: true

Redux Middleware


Redux Thunk

  1. ({ dispatch, getState }) => next => action => {
  2. if (typeof action === 'function') {
  3. return action(dispatch, getState);
  4. }
  5. return next(action);
  6. };
  1. ({ dispatch, getState }) => {
  2. return next => {
  3. return action => {
  4. if (typeof action === 'function') {
  5. return action(dispatch, getState);
  6. }
  7. return next(action);
  8. };
  9. }
  10. }

Practice

- console.log every action with action type

Logger and Dev Tool

- https://github.com/zalmoxisus/redux-devtools-extension

Setup Dev Tool

  1. import { createStore, applyMiddleware, compose } from 'redux'
  2. import { createDevTools } from 'redux-devtools'
  3. import LogMonitor from 'redux-devtools-log-monitor'
  4. import DockMonitor from 'redux-devtools-dock-monitor'
  5. const DevTool = createDevTools(
  6. <DockMonitor toggleVisibilityKey='ctrl-h'
  7. changePositionKey='ctrl-q'
  8. changeMonitorKey='ctrl-m'>
  9. <LogMonitor />
  10. </DockMonitor>
  11. )
  12. const store = createStore(
  13. rootReducer,
  14. initialState,
  15. compose(applyMiddleware(thunk), DevTools.instrument())
  16. )
  17. render(
  18. <Provider store={store}>
  19. <div><TodoApp /><DevTools /></div>
  20. </Provider>
  21. document.getElementById('app')
  22. )

layout: false

Utils for actions and reducers

  1. const increment = () => { type: INCREMENT }
  2. const incrementTwiceWith = (x) => {
  3. type: INCREMENT_TWICE_WITH,
  4. payload: x * 2
  5. }
  6. const reducer = (state, action) => ({
  7. switch (action.type) {
  8. case INCREMENT:
  9. return state + 1
  10. case INCREMENT_TWICE_WITH:
  11. return state + action.payload
  12. default:
  13. return state
  14. }
  15. })
  1. import { createAction, handleActions } from 'redux-actions'
  2. const increment = createAction(INCREMENT)
  3. const incrementTwiceWith = createAction(INCREMENT_TWICE_WITH, x => x * 2)
  4. const reducer = handleActions({
  5. [INCREMENT]: state => state + 1,
  6. [INCREMENT_TWICE_WITH]: (state, action) => state + action.payload * 2
  7. })

Practice

Refactor your practice with the logger and redux-action

  1. import { createAction, handleActions } from 'redux-actions'
  2. const increment = createAction(INCREMENT)
  3. const incrementTwiceWith = createAction(INCREMENT_TWICE_WITH, x => x * 2)
  4. const reducer = handleActions({
  5. [INCREMENT]: state => state + 1,
  6. [INCREMENT_TWICE_WITH]: (state, action) => state + action.payload * 2
  7. })
  1. import { createStore, applyMiddleware, compose } from 'redux'
  2. import { createDevTools } from 'redux-devtools'
  3. import LogMonitor from 'redux-devtools-log-monitor'
  4. import DockMonitor from 'redux-devtools-dock-monitor'
  5. const DevTool = createDevTools(
  6. <DockMonitor toggleVisibilityKey='ctrl-h'
  7. changePositionKey='ctrl-q'
  8. changeMonitorKey='ctrl-m'>
  9. <LogMonitor />
  10. </DockMonitor>
  11. )
  12. const store = createStore(
  13. rootReducer,
  14. initialState,
  15. compose(applyMiddleware(thunk), DevTools.instrument())
  16. )

layout: true

Complex Actions


Logic in Reducer or Action?

  1. const incrementActionThunk = () => ({ type: 'INCREMENT' })
  2. const reducer = (state, action) => ({
  3. if (action.type === 'INCREMENT') {
  4. state = state + 1
  5. }
  6. })
  1. const updateCount = (dispatch, getState) => ({
  2. type: 'INCREMENT',
  3. payload: getState().count + 1
  4. })
  5. const reducer = (state, action) => ({
  6. if (action.type === 'INCREMENT') {
  7. state = action.payload
  8. }
  9. })

???
Complex action or Complex reducer
- Event system disadvantage

- Database driven design

Reuse Actions

  1. const increment = () => { type: INCREMENT }
  1. const incrementTwiceWith = (x) => {
  2. type: INCREMENT_TWICE_WITH,
  3. payload: x * 2
  4. }
  1. const incrementWith = x => {
  2. type: INCREMENT_WITH,
  3. payload: x
  4. }
  5. const increment = () => dispatch => dispatch(incrementWith(1))
  6. const incrementTwiceWith = (x) => dispatch => dispatch(incrementWith(x * 2))

Async Actions

  1. const initialState = {
  2. user: {
  3. id: null
  4. },
  5. isLoading: false
  6. }
  1. const login = (username, password) => (dispatch, getState) => {
  2. const { isLoading } = getState()
  3. if (!isLoading) {
  4. dispatch(loading(true))
  5. }
  6. API.login({ username, password }).then(user => {
  7. dispatch(loading(false))
  8. dispatch(user ? loginSuccess(user) : loginFail())
  9. }).catch(e => {
  10. dispatch(loading(false))
  11. loginFail(e)
  12. })
  13. }

layout: false

Practice for Complex Actions

Hint: you might needreact-redux to connect your action with component

layout: true

Redux Saga


A separate thread in your application that's solely responsible for side effects


class: center middle

  1. const INCREMENT_ASYNC = 'INCREMENT_ASYNC'
  2. const incrementAsync = createAction(INCREMENT_ASYNC)
  3. const incrementAsyncSaga = function* incrementAsync() {
  4. return new Promise((res, rej) => {
  5. // Or do something else async
  6. setTimeout(() => res(), 1000)
  7. })
  8. }
  9. const rootSaga = function* root() {
  10. yield [
  11. takeLatest(INCREMENT_ASYNC, incrementAsyncSaga)
  12. ]
  13. }
  14. const sagaMiddleware = createSagaMiddleware()
  15. const store = createStore(
  16. reducer,
  17. applyMiddleware(thunk, sagaMiddleware),
  18. initialState
  19. )
  20. sagaMiddleware.run(rootSaga)

Practice

Refactor previous Practice Complex Action into Saga


layout: false
class: center middle

Homework

.left[

Improve your TodoMVC with Redux


class: center middle

Thanks

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注