Redux进阶

99 阅读4分钟

参考资料

中间件

什么是Redux的中间件

  • 中间件指的是action 和 store 中间
  • 中间件实现是对store的dispatch方法的升级

几个常见中间件的作用

对dispatch方法的升级

  • redux-thunk:使store接收的action可以是函数(初始只能是对象)。当action是函数时,直接执行该函数
  • redux-saga:也是处理异步逻辑,把异步逻辑单独放在一个文件中管理
  • redux-log:每次dispatch时,在控制台输出内容

Redux-thunk

使用说明文档
npm install redux-thunk --save

 

使用redux-thunk实现ajax流程

src

store

index.js

// store/index.js配置(直接使用)
import { createStore, applyMiddleware } from 'redux' // 1 引入applyMiddleware
import thunk from 'redux-thunk'; // 2 引入thunk
import reducer from './reducer'

const store = createStore(reducer, applyMiddleware(thunk)) // 3 使用

export default store

Redux DevTools插件配置说明

// store/index.js配置(配合window.__REDUX_DEVTOOLS_EXTENSION__ 使用)
import { createStore, applyMiddleware, compose } from 'redux' //1 引入applyMiddleware, compose 
import thunk from 'redux-thunk' // 2 引入thunk
import reducer from './reducer'

// 3 composeEnhancers enhancer
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?   
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
      // Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
    }) : compose
const enhancer = composeEnhancers(
  applyMiddleware(thunk) // other store enhancers if any
)
const store = createStore(reducer, enhancer) // 使用

export default store

actionCreators

在actionCreators创建一个返回函数(如ajax请求)的action

// actionCreators
export const initItemAction = (value) => ({
  type: INIT_TODO_ITEM,
  value
})

// 当使用redux-thunk后,action不仅可以是对象,还可以是函数
// 返回的如果是方法会自动执行
// 返回的方法可以接收到dispatch方法,去派发其它action
export const getTodoList = () => {
  return (dispatch) => {
    axios.get('/initList').then(res => {
      const action = initItemAction(res.data);
      dispatch(action);
    })
  }
}

TodoList

3.component组件中派发acition

import React, { Component } from 'react'
import store from './store'
import {getListData} from './store/actionCreators'
class TodoList extends Component {
  constructor(props) {
    super(props)
    this.state = store.getState()
    store.subscribe(this.handleStateChange)
  }
  render() {
    .....
  }
  componentDidMount() { // 组件挂载后派发store
    const action = getTodoList()
    store.dispatch(action)
  }
  handleStateChange() {
    this.setState(store.getState())
  }
}

export default TodoList

4.store交给reduce处理,reducer发现竟然是个函数,那不好意思,直接给你执行了!!!

在第2步骤里,return函数的action里,函数直接执行,获取异步的结果再次派发action给store,store检查发现这次是对象(而不是函数了),那就按正常流程更新store

Redux-saga

使用说明文档

npm install --save redux-saga
yarn add redux-saga

src

store

index.js

// src/store/index.js
import { createStore, applyMiddleware  } from 'redux'
import reducer from './reducer'
import createSagaMiddleware from 'redux-saga'
import mySaga from './sagas'

const sagaMiddleware = createSagaMiddleware()
const store = createStore(reducer, applyMiddleware(sagaMiddleware))
sagaMiddleware.run(mySaga)

export default store

saga.js

// src/store/saga.js文件的配置
import { put, takeEvery } from 'redux-saga/effects'
import { GET_LIST_DATA} from './actionTypes'
import { initList } from './actionCreators'

function* fetchUser() {
   try {
      const res = yield new Promise((resolve, reject) => { // 3 回调里执行异步获取数据
        let arr = ['hello', 'liukun']
        resolve(arr)
      })
      const action = initList(res)
      console.log('action', res)
      yield put(action) // 4 再次派发action更新data
   } catch (e) {
      console.log('网络异常')
   }
}

function* mySaga() { // 2 saga监控到TodoList派发到anction,执行回调fetchUser
  yield takeEvery(GET_LIST_DATA, fetchUser);
}

export default mySaga;

actionCreators.js

// src/store/actionCreators.js

export const initList = (value) => ({ // res
import { INPUT_CHANGE_VALUE, BTN_SUBMIT, LIST_DELETE, GET_LIST_DATA, INIT_LIST } from './actionTypes'

export const inputValueChange = (value) => ({
    type: INPUT_CHANGE_VALUE,
    value
})

export const btnSubmit = () => ({
    type: BTN_SUBMIT
})

export const listDelete = (index) => ({
    type: LIST_DELETE,
    index
})

export const listData = (value) => ({
    type: GET_LIST_DATA,
    value
})

export const initList = (value) => ({
  type: INIT_LIST,
  value
})
export const getListData = () => ({
  type: GET_LIST_DATA
})

reducer.js

// src/store/reducer.js 
// 处理action更新的data

import { INPUT_CHANGE_VALUE, BTN_SUBMIT, LIST_DELETE, INIT_LIST } from './actionTypes'

const defaultState = {
  inputValue: '',
  list: []
}

export default (state = defaultState, action) => {
  const newState = JSON.parse(JSON.stringify(state))
  const { type } = action
  switch(type) {
    case INPUT_CHANGE_VALUE:
      newState.inputValue = action.value
      break
    case BTN_SUBMIT:
      newState.list.push(newState.inputValue)
      newState.inputValue = ''
      break
    case LIST_DELETE:
      newState.list.splice(action.index, 1)
      break
    case INIT_LIST: // 5 reducer处理派发过来的action并更新state
      newState.list = [...newState.list, ...action.value]
      break
    default:
      return state
  }
  return newState
}

TodoList.js

// src/TodoList.js 
// 派发action
import React, { Component } from 'react'
import store from './store'
import TodoListUI from './TodoListUI'
import { inputValueChange, btnSubmit, listDelete, getListData} from './store/actionCreators'
class TodoList extends Component {
  constructor(props) {
    super(props)
    this.state = store.getState()
    this.handleInputChange = this.handleInputChange.bind(this)
    this.handleBtnSubmit = this.handleBtnSubmit.bind(this)
    this.handleListDelete = this.handleListDelete.bind(this)
    this.handleStateChange = this.handleStateChange.bind(this)
    store.subscribe(this.handleStateChange)
  }
  render() {
    return (
      <TodoListUI 
        inputValue={this.state.inputValue}
        list={this.state.list}
        handleInputChange={this.handleInputChange}
        handleBtnSubmit={this.handleBtnSubmit}
        handleListDelete={this.handleListDelete}
      />
    )
  }
  componentDidMount() {  // 1 TodoList文件派发action
    const action = getListData()
    store.dispatch(action)
  }
  handleStateChange() {
    this.setState(store.getState())
  }
  handleInputChange(e) {
    const action = inputValueChange(e.target.value)
    store.dispatch(action)
  }
  handleBtnSubmit() {
    const action = btnSubmit()
    store.dispatch(action)
  }
  handleListDelete(index) {
    const action = listDelete(index)
    store.dispatch(action)
  }
}

export default TodoList
Redux-saga中间件执行流程
  1. 组件加载完成后,把一个普通的action对象派发给store;

  2. 因使用了redux-saga中间件,所以会被sagas.js中的generator函数匹配到,并交给对应的函数(一般也是generator函数)处理;

  3. sagas.js的函数拿到结果后,使用redux-saga的put方法再次派发一个普通action对象给store;

  4. sagas.js中没有匹配到对应的类型,则store交由reducer处理并更新store的状态。

React-redux

npm install react-redux --save

src/index.js配置

// src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import store from './store'
import TodoList from './TodoList'
import { Provider } from 'react-redux' // 1 引入Provider

const App = (
  <Provider store={store}> {/* Provider和store连接,之后Provider里的组件都可以获取store里的数据 */}
    <TodoList />
  </Provider>
)

ReactDOM.render(App, document.getElementById('root'))

src/TodoList.js使用

import React from 'react'
import { connect } from 'react-redux' // 2 引入connect
import { Input, List, Button } from 'antd'
import 'antd/dist/antd.css'
import { inputValueChange, btnSubmit, listDelete} from './store/actionCreators'
const TodoList = (props) => {
  return (
    <div>
      <Input 
        style={{width: 300}}
        placeholder='todo info'
        value={props.inputValue}
        onChange={props.handleInputChange}
      />
      <Button
        onClick={props.handleBtnSubmit}
      >提交</Button>
      <List
        style={{width: 300}}
        bordered
        dataSource={props.list}
        renderItem={(item, index) =>(
          <List.Item onClick={() => {props.handleListDelete(index)}}>{item}</List.Item>
        )}
      >
      </List>
    </div>
  )
}
const mapStateToProps = (state) => {
  return { // 3 mapStateToProps:把store里的stata映射到当前组件的props
    inputValue: state.inputValue,
    list: state.list
  }
}
const mapDispatchToProps = (dispatch) => {
  return { // 3 mapDispatchToProps:把store里的dispatch方法映射到当前组件的props
    handleInputChange(e) {
      console.log(e.target.value)
      const action = inputValueChange(e.target.value)
      dispatch(action)
    },
    handleBtnSubmit() {
      const action = btnSubmit()
      dispatch(action)
    },
    handleListDelete(index) {
      const action = listDelete(index)
      dispatch(action)
    }
  }
}

// 4 把当前组件和store进行连接并export
export default connect(mapStateToProps, mapDispatchToProps)(TodoList)