美文网首页
Redux入门与进阶

Redux入门与进阶

作者: nimw | 来源:发表于2019-03-07 23:00 被阅读0次

一. Redux入门

  1. Redux Devtools调试插件安装
    设置 - 扩展程序 - 打开Chrome网上应用店 - Redux Devtools
  2. 使用Redux实现TodoList

(1) 安装redux

➜  my-react-app git:(master) ✗ yarn add redux

(2) 创建Store

// src/store/index.js
import { createStore } from 'redux'
import reducer from './reducer'

//store是唯一的,只有store能改变state
const store = createStore(
  reducer,
  //Redux DevTools调试工具
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)

export default store

(3) 创建actionTypes

// src/store/actionTypes.js
export const CHANGE_INPUT_VALUE = 'change_input_value'
export const ADD_TODO_ITEM = 'add_todo_item'
export const DELETE_TODO_ITEM = 'delete_todo_item'

(4) 创建actionCreators

// src/store/actionCreators.js
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'

export const getInputChangeAction = (value) => ({
  type: CHANGE_INPUT_VALUE, value
})

export const getAddTodoItemAction = () => ({
  type: ADD_TODO_ITEM
})

export const getDeleteTodoItemAction = (value) => ({
  type: DELETE_TODO_ITEM, value
})

(5) 实现reducer

// src/store/reducer.js
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'

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

//redux是一个纯函数,输入固定则输出固定,且没有副作用
//redux不应修改旧的state
export default (state = defaultState, action) => {
  switch(action.type){
    case  CHANGE_INPUT_VALUE:
      return {
        ...state,
        inputValue: action.value
      }  
    case ADD_TODO_ITEM:
      return {
        ...state,
        inputValue: '',
        list: [...state.list, state.inputValue]
      }
    case DELETE_TODO_ITEM:
      return {
        ...state,
        list: state.list.filter(
          (item, index) => index !== action.value
        )
      }
  }
  return state
}

(6) 实现TodoList组件

// src/TodoList.js
import React, { Component } from 'react';
import { Input, Button, List } from 'antd'
import store from './store'
import {getInputChangeAction, getAddTodoItemAction, getDeleteTodoItemAction} from './store/actionCreators'

class TodoList extends Component {
  constructor(props, context) {
    super(props, context)
    this.state = store.getState()
    this._handleStoreChange = this._handleStoreChange.bind(this)
    this._handleInputChange = this._handleInputChange.bind(this)
    this._handleButtonClick = this._handleButtonClick.bind(this)
    //订阅Store变化,执行_handleStoreChange方法
    store.subscribe(this._handleStoreChange)
  }

  render() {
    return (
      <div>
        <div style={{display: 'flex'}}>
          <Input 
            placeholder='todo info'
            value = {this.state.inputValue}
            onChange={this._handleInputChange}
          />
          <Button 
            type="primary"
            onClick={this._handleButtonClick}
          >
            提交
          </Button>
        </div>
        <List
          bordered
          dataSource={this.state.list}
          renderItem={(item, index) => (
              <List.Item onClick={this._handleItemDelete.bind(this, index)}>{item}</List.Item>
            )
          }
        />
      </div>
    )
  }

  _handleStoreChange() {
    this.setState(store.getState())
  }

  _handleInputChange(e) {
    store.dispatch(getInputChangeAction(e.target.value))
  }

  _handleButtonClick() {
    store.dispatch(getAddTodoItemAction())
  }

  _handleItemDelete(index) {
    store.dispatch(getDeleteTodoItemAction(index))
  }
}

export default TodoList;
  1. Redux核心API
    (1) createStore: 创建应用唯一的store
    (2) store.dispatch:派发action, 该action会传递给store
    (3) store.getState:获取store中所有数据内容。
    (4) store.subscribe:订阅store改变,绑定回调函数。

二. Redux中间件

  1. Redux中间件介绍
    ① 中间件指actionstore“中间”。中间件是对dispatch方法的升级。
    ② 流程图
    image.png
  2. redux-thunk中间件

① 安装redux-thunk

 yarn add redux-thunk

② 创建store时引入redux-thunk中间件

//src/store/index.js
import { createStore, applyMiddleware, compose } from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk'

//Redux DevTools调试工具
const composeEnhancers = typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
  ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
  : compose;

const enhancer = composeEnhancers(
  applyMiddleware(thunk)
);

const store = createStore(reducer, enhancer)

export default store

③ 定义action

//src/store/actionCreators.js
export const getInitListAction = (data) => ({
  type: INIT_LIST_ACTION,
  data
})

export const getInitList = () => {
  return (dispatch) => {
    axios.get('/list.json').then((res) => {
      dispatch(getInitListAction(res.data))
    })
  }
}

④ 分发action

  //src/components/TodoList/TodoList.js
  componentDidMount() {
    store.dispatch(getInitList());
  }

redux-thunk使用总结
使用redux-thunk后,action可以不是一个对象,而是一个方法。 dispatch方法类型的action,会调用该方法,并传入dispatch实参。
在函数内部可以通过该实参dispatch其他action动作。
总结:redux-thunk中间件将异步获取数据代码抽离到aciton中。

  1. redux-saga中间件

① 安装redux-saga

yarn add redux-saga

② 创建store时引入redux-thunk中间件

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

const sagaMiddleware = createSagaMiddleware()

//Redux DevTools调试工具
const composeEnhancers = typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
  ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
  : compose;

const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware));


//store是唯一的,只有store能改变state
const store = createStore(reducer, enhancer)

sagaMiddleware.run(todoSagas)

export default store

③ 定义sagas

//src/store/sagas.js
import { takeEvery, put } from 'redux-saga/effects'
import { GET_INIT_LIST } from './actionTypes'
import axios from "axios"
import {getInitListAction} from "./actionCreators"

function *getInitList() {
  try {
    const res = yield axios.get('/list.json')
    const action = getInitListAction(res.data)
    yield put(action)
  } catch (e) {
    console.log('list.json 网络请求失败!')
  }
}

//generator函数
function* mySaga() {
  yield takeEvery(GET_INIT_LIST, getInitList);
}

export default mySaga;

④ 分发action

//src/components/TodoList/TodoList.js
componentDidMount() {
  store.dispatch(getInitList())
}

redux-saga使用总结
使用 redux-saga后,action可以不被reducer处理,而是被saga处理。
saga函数内部可以通过put方法dispatch其它action动作。
总结:redux-saga中间件将异步获取数据代码抽离到saga中。

三. react-redux介绍

  1. 使用react-redux实现TodoList

(1) 安装react-redux

➜  my-react-app git:(master) ✗ yarn add react-redux

(2) 创建Store

// src/store/index.js
import  { createStore } from 'redux'
import reducer from './reducer'

const store = createStore(reducer)

export default store

(3) 使用Provider组件包裹根组件

//src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import TodoList from './components/TodoList/TodoList'
import store from './store'

ReactDOM.render(
  <Provider store={store}>
    <TodoList/>
  </Provider>,
  document.getElementById('root')
);

注释:store传入Provider组件,使Provider内部组件都有机会获取store中的state
(4) 创建actionTypes

// src/store/actionTypes.js
export const CHANGE_INPUT_VALUE = 'change_input_value'
export const ADD_TODO_ITEM = 'add_todo_item'
export const DELETE_TODO_ITEM = 'delete_todo_item'

(5) 创建actionCreators

// src/store/actionCreators.js
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'

export const getInputChangeAction = (value) => ({
  type: CHANGE_INPUT_VALUE, value
})

export const getAddTodoItemAction = () => ({
  type: ADD_TODO_ITEM
})

export const getDeleteTodoItemAction = (value) => ({
  type: DELETE_TODO_ITEM, value
})

(6) 实现reducer

// src/store/reducer.js
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'

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

//redux是一个纯函数,输入固定则输出固定,且没有副作用
//redux不应修改旧的state
export default (state = defaultState, action) => {
  switch(action.type){
    case  CHANGE_INPUT_VALUE:
      return {
        ...state,
        inputValue: action.value
      }  
    case ADD_TODO_ITEM:
      return {
        ...state,
        inputValue: '',
        list: [...state.list, state.inputValue]
      }
    case DELETE_TODO_ITEM:
      return {
        ...state,
        list: state.list.filter(
          (item, index) => index !== action.value
        )
      }
  }
  return state
}

(7) 实现TodoList组件

// src/TodoList.js
import React from 'react'
import { Input, Button, List } from 'antd'
import { connect } from 'react-redux'
import { getInputChangeAction, getAddTodoItemAction, getDeleteTodoItemAction } from '../../store/actionCreators'

const TodoList = (props) => {
  const {inputValue, handleInputChange, addListItem, list, handleItemDelete} = props
  return (
    <div>
      <di>
        <Input value={inputValue} onChange={handleInputChange}/>
        <Button type="primary" onClick={addListItem}>
          提交
        </Button>
      </di>
      <List bordered dataSource={list} renderItem={(item, index) => (
          <List.Item onClick={() => {handleItemDelete(index)}}>
            {item}
          </List.Item>
        )}
      />
    </div>
  )
}

const mapStateToProps = (state) => {
  return {
    inputValue: state.inputValue,
    list: state.list
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    handleInputChange(e) {
      dispatch(getInputChangeAction(e.target.value))
    },
    handleItemDelete(index) {
      dispatch(getDeleteTodoItemAction(index))
    },
    addListItem() {
      dispatch(getAddTodoItemAction())
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(TodoList);

注释:TodoList为一个UI(纯)组件,connect(mapStateToProps, mapDispatchToProps)(TodoList)为一个容器组件。业务逻辑被抽离到mapStateToPropsmapDispatchToProps方法中。

  1. react-reduxredux用法差异
    Provider组件。
    connect方法。
  2. reduxcombineReducers方法的使用
    ① 创建TodoList对应的reducer文件
// ./src/components/TodoList/store/reducer.js
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from '../../../store/actionTypes'

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

//redux是一个纯函数,输入固定则输出固定,且没有副作用
//redux不能修改旧的state
export default (state = defaultState, action) => {
  switch(action.type){
    case  CHANGE_INPUT_VALUE:
      return {
        ...state,
        inputValue: action.value
      }
    case ADD_TODO_ITEM:
      return {
        ...state,
        inputValue: '',
        list: [...state.list, state.inputValue]
      }
    case DELETE_TODO_ITEM:
      return {
        ...state,
        list: state.list.filter(
          (item, index) => index !== action.value
        )
      }
  }
  return state
}

② 使用combineReducers

// src/store/reducer.js
import todoListReducer from '../components/TodoList/store/reducer'
import { combineReducers } from 'redux'

export default combineReducers({
  todoList: todoListReducer
})

③ 修改TodoList组件

// src/components/TodoList/TodoList.js
//......
const mapStateToProps = (state) => {
  return {
    inputValue: state.todoList.inputValue,
    list: state.todoList.list
  }
}
//......

相关文章

网友评论

      本文标题:Redux入门与进阶

      本文链接:https://www.haomeiwen.com/subject/fvmkpqtx.html