动机 对于redux原生用法,不喜欢过于繁重的actions-types常量定义以及switch case的书写方式,redux就是一个工具,不喜欢在工具上面,浪费很多体力,这点刚好跟redux-actions动机一致,使用下来也觉得很是方便。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 let nextTodoId = 0 export const addTodo = text => ({ type : 'ADD_TODO' , id : nextTodoId++, text }) const todos = (state = [], action ) => { switch (action.type) { case 'ADD_TODO' : return [ ...state, { id : action.id, text : action.text, completed : false } ] case 'TOGGLE_TODO' : return state.map(todo => todo.id === action.id ? { ...todo, completed : !todo.completed } : todo ) default : return state } } export default todos
极简使用 创建action/actionCreator.js
1 2 import { createAction } from 'redux-actions' ;export const addnum = createAction('ADDNUM' )
组件中引入使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import React,{Component} from "react" ;import store from "./store" import {addnum} from "./action/actionCreator" export default class App extends Component { constructor ( ) { super () this .state = store.getState(); store.subscribe(this .handleUpdate.bind(this )) } render ( ) { let {n} = this .state; return ( <div > <h2 > {n}</h2 > <button onClick ={this.handleClick.bind(this)} > 点击</button > </div > ) } handleClick ( ) { store.dispatch(addnum()); } handleUpdate ( ) { this .setState(store.getState()) } }
reducer中使用
1 2 3 4 5 6 7 8 9 10 11 12 import {handleActions} from 'redux-actions' ;const defaultState = { n :10 } export default handleActions({ ADDNUM : (state, action ) => { let newState = {...state}; newState.n++; return newState; }, }, defaultState)
入门使用 Redux传统用法 1 2 3 4 5 6 7 8 const BOOK_LIST_GET = 'BOOK_LIST_GET' ;export default { BOOK_LIST_GET, };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import actionTypes from './actionTypes' ;const getBookList = () => { const bookList = [{ id : '1' , title : '123' , description : '123' , }, { id : '2' , title : '234' , description : '234' , }]; return { type : actionTypes.BOOK_LIST_GET, bookList, }; }; export default { getBookList, };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import actionTypes from './actionTypes' ;const initialState = { bookList : [], }; const pageMainReducer = (state = initialState, action ) => { switch (action.type) { case actionTypes.BOOK_LIST_GET: return { ...state, bookList : action.bookList, }; default : return state; } }; export default pageMainReducer;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 // index.js import React from 'react'; import { connect } from 'react-redux'; import Bookshelf from './components/bookshelf'; import actions from './actions'; /** * 首页 */ class PageMain extends React.Component { componentDidMount() { this.props.getBookList(); } render() { return ( <Bookshelf dataSource={this.props.bookList} /> ); } } export default connect((state) => { return { bookList: state.pageMain.bookList, }; }, { getBookList: actions.getBookList, })(PageMain);
以上的代码中大致功能为: PageMain 组件会在加载后,请求获取书籍信息,然后将获取到的值传给展示组件Bookshelf使用。
实现步骤:
首先我们创建了action类型BOOK_LIST_GET ,
然后根据BOOK_LIST_GET 创建了action工厂getBookList以及reducer。
最后用connect将store的值传映射到给PageMain的props,并且将dispath action的操作映射到props上。
当然在最外层我们已经根据reducer生成了一个store并传给了Provider,这里就不贴上去了。
使用redux-actions来处理action和reducer 修改actions.js为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import { createAction } from 'redux-actions'; import actionTypes from './actionTypes'; const getBookList = createAction(actionTypes.BOOK_LIST_GET, () => { const bookList = [{ id: '1', title: '123', description: '123', }, { id: '2', title: '234', description: '234', }]; return bookList; }); export default { getBookList, };
修改reducer.js为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { handleAction } from 'redux-actions'; import actions from './actions'; const initialState = { bookList: [], }; const pageMainReducer = handleAction(actions.getBookList, (state, action) => { return { ...state, bookList: action.payload, }; }, initialState); export default pageMainReducer;
对比两种玩法之后,可以看到redux-actions实际上就是将我们之前做的操作封装了一道,让写法更简便。
createAction 与 handleAction 顾名思义,相对于createAction和handleAction而言,就是创建和处理多个action。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 createAction( type, payloadCreator = Identity, metaCreator ) const increment = createAction( 'INCREMENT' , mount => mount, () => ({ admin : true }) ); increment(20 );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 handleAction(type, reducer, defaultState) const reducer = (state = defaultState, action) { switch (action.type) { case 'INCREMENT' : return { count : state.count + action.payload } default : return state } } const INCREMENT = 'INCREMENT' const defaultState = { count : 1 }const reducer = handleAction( INCREMENT, (state, action ) => ({ count : state.count + action.payload }), defaultState )
针对上面的代码我们加入一个删除操作,那么修改actionTypes.js
1 2 3 4 5 6 7 8 9 10 11 12 13 const BOOK_LIST_GET = 'BOOK_LIST_GET' ;const BOOK_DELETE = 'BOOK_DELETE' ;export default { / BOOK_LIST_GET, BOOK_DELETE, };
用createActions修改actions.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import { createActions } from 'redux-actions' ;import actionTypes from './actionTypes' ;export default createActions({ [actionTypes.BOOK_LIST_GET]: () => { const bookList = [{ id : '1' , title : '123' , description : '123' , }, { id : '2' , title : '234' , description : '234' , }]; return bookList; }, [actionTypes.BOOK_DELETE]: (id ) => { console .info(`删除id为${id} 的Book` ); return { id }; }, });
用handleActions修改reducers.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import { handleActions } from 'redux-actions' ;import actionTypes from './actionTypes' ;const initialState = { bookList : [], }; const pageMainReducer = handleActions({ [actionTypes.BOOK_LIST_GET]: (state, action ) => { return { ...state, bookList : action.payload, }; }, [actionTypes.BOOK_DELETE]: (state, action ) => { return { ...state, bookList : state.bookList.filter(l => l.id !== action.payload.id), }; }, }, initialState); export default pageMainReducer;
修改index.js部分代码
1 2 3 4 5 6 7 8 9 10 import actions from './actions' ;export default connect((state ) => { return { bookList : state.pageMain.bookList, }; }, { bookDelete :actions.bookDelete , bookListGet :actions.bookListGet, })(PageMain);
createActions 会返回一个对象,对象针对每个action类型都有一个值为action工厂的属性,属性名为action类型的值去掉下划线后的驼峰命名。
handleActions 仍然返回一个reducer。 除了上面那种写法,handleActions还可以这么写:
1 2 3 4 5 6 7 8 9 10 11 12 [actionTypes.BOOK_LIST_GET]: { next(state, action) { return { ...state, bookList: action.payload, }; }, throw (state) { return state; }, },
实际上多出来的功能就是加上了对异常的处理。
CreateAction源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 4 function identity (t ) { 5 return t; 6 } 7 8 15 export default function createAction (type, actionCreator, metaCreator ) {16 20 const finalActionCreator = typeof actionCreator === 'function' 21 ? actionCreator22 : identity;23 26 return (...args ) => {27 30 const action = {31 type,32 payload: finalActionCreator(...args)33 };34 37 if (args.length === 1 && args[0 ] instanceof Error ) {38 39 action.error = true ;40 }41 44 if (typeof metaCreator === 'function' ) {45 action.meta = metaCreator(...args);46 }47 48 return action;49 };50 }
createActions和handleActions createActions具体用法就是createActions(actionMap)
actionMap就是一个对象,
key值为动作类型,
value可以是payloadCreator函数、一个数组[payloadCreator, metaCreator]、嵌套的actionMap。
1 2 3 4 5 6 7 8 9 10 11 12 13 cosnt {add, remove} = createActions({ ADD_TODO : todo => ({ todo }), REMOVE_TODO : [ todo => ({ todo }), (todo, warn ) => ({ todo, warn }) ] }) add('redux-actions' ) remove('redux-actions' , 'warn' )
handleActions函数是用来处理多个动作的。
reducerMap中key就是动作名, value就是reducer
1 2 3 4 5 6 7 8 9 10 11 12 13 handleActions(reducerMap, defaultState) const reducer = handleActions( { INCREMENT : (state, action ) => ({ counter : state.counter + action.payload }), DECREMENT : (state, action ) => ({ counter : state.counter - action.payload }) }, { counter : 0 } );
combineActions 合并多个action和actionCreator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const { increment, decrement } = createActions({ INCREMENT : amount => ({ amount }), DECREMENT : amount => ({ amount : -amount }), }) const reducer = handleAction( combineActions(increment, decrement), { next : (state, { payload } ) => ({ ...state, count : state.count + payload }), throw : state => ({ ...state, count : 0 }), }, { count : 10 } ) const reduce2 = handleActions( { [combineActions(increment, decrement)] (state, {payload}) { return { ...state, count : state.count + payload } } }, { count : 10 } )