* redux-actions是一个实用的库,让编写redux状态管理变得简单起来。redux-action产生的动作是[FSA](https://github.com/redux-utilities/flux-standard-action)标准的
### 5.1 单个action
#### 5.1.1 actions\\counter.js
src\\store\\actions\\counter.js
~~~
import * as types from '../action-types';
//import { createAction } from 'redux-actions';
function createAction(type,payloadCreator){
return function actionCreator(...args){
return {type,payload:payloadCreator(...args)};
}
}
const add = createAction(types.ADD,(payload)=>payload*2);
const minus = createAction(types.MINUS,(payload)=>payload*2);
export default {
add,
minus
}
~~~
#### 5.1.2 reducers\\counter.js
src\\store\\reducers\\counter.js
~~~
import * as types from '../action-types';
//import {handleAction} from 'redux-actions';
import actions from '../actions/counter';
function handleAction(type,reducer ,defaultState){
return function(state=defaultState,action){
if(action.type === type){
return reducer(state,action);
}
return state;
}
}
const initialState = {number:0};
const reducer = handleAction(types.ADD,(state,action)=>{
return {
...state,number:state.number+action.payload
}
},initialState);
export default reducer;
~~~
### 5.2 多个action
#### 5.2.1 actions\\counter.js
actions\\counter.js
~~~
import * as types from '../action-types';
//import { createAction,createActions } from 'redux-actions';
export default createActions({
[types.ADD]:(payload)=>payload*2,
[types.MINUS]:(payload)=>payload*2
});
function createActions(actions){
let newActions = {};
for(let type in actions){
newActions[type]= function(...args){
return {type,payload:actions[type](...args)}
}
}
return newActions;
}
~~~
#### 5.2.2 reducers\\counter.js
reducers\\counter.js
~~~
import * as types from '../action-types';
//import {handleAction,handleActions } from 'redux-actions';
import actions from '../actions/counter';
const initialState = {number:0};
function handleActions(reducers,initialState){
return function(state=initialState,action){
let types = Object.keys(reducers);
for(let i=0;i<types.length;i++){
let type = types[i];
if(type === action.type){
return reducers[type](state,action);
}
}
return state;
}
}
export default handleActions({
[types.ADD]:(state,action)=>{
return {
...state,number:state.number+action.payload
}
},
[types.MINUS]:(state,action)=>{
return {
...state,number:state.number-action.payload
}
}
},initialState);
~~~
## reselect
* 使用Redux管理React应用状态时,`mapStateToProps`方法作为从`Redux Store`上获取数据过程中的重要一环,它一定不能有性能缺陷,它本身是一个函数,通过计算返回一个对象,这个计算过程通常是基于Redux Store状态树进行的,而很明显的Redux状态树越复杂,这个计算过程可能就越耗时,我们应该要能够尽可能减少这个计算过程,比如重复在相同状态下渲染组件,多次的计算过程显然是多余的,我们是否可以缓存该结果呢?这个问题的解决者就是`reselect`,它可以提高应用获取数据的性能
* `reselect`的原理是,只要相关状态不变,即直接使用上一次的缓存结果
### 6.1 基本用法
* reselect通过创建选择器(selectors),该函数接受一个state参数,然后返回我们需要在mapStateToProps方法内返回对象的某一个数据项,一个选择器的处理可以分为两个步骤
* 接受state参数,根据我们提供的映射函数数组分别进行计算,如果返回结果和上次第一步的计算结果一致,说明命中缓存,则不进行第二步计算,直接返回上次第二步的计算结果,否则继续第二步计算。第一步的结果比较,通常仅仅是===相等性检查,性能是足够的
* 根据第一步返回的结果,计算并返回最终结果
* 需要注意的是,传入createSelector的映射函数返回的状态应该是不可变的,因为默认缓存命中检测函数使用引用检查,如果使用JavaScript对象,仅改变该对象的某一属性,引用检测是无法检测到属性变更的,这将导致组件无法响应更新
~~~
//import { createSelector } from 'reselect'
function createSelector(selector,reducer){
let lastState;
let value;
return function(state){
let newState = selector(state);
if(lastState !== newState){
value = reducer(newState);
lastState = newState;
}
return value;
}
}
const counterSelector = state => state.counter;
const getCounterSelector = createSelector(
counterSelector,
counter => {
console.log('重新计算number')
return counter.number;
}
)
let initialState = {
counter: {
number:0
}
}
console.log(getCounterSelector(initialState));
console.log(getCounterSelector(initialState));
~~~
~~~
+console.log(getCounterSelector(initialState));
+initialState.counter.number+=1;
+console.log(getCounterSelector(initialState));
~~~
~~~
+ console.log(getCounterSelector(initialState));
+ initialState.counter={number:1}
+ console.log(getCounterSelector(initialState));
~~~
~~~
+const immutable = require("immutable");
+let initialState = immutable.Map({counter: {number:0}})
+console.log(getCounterSelector(initialState.toJS()));
+initialState = initialState.setIn(['counter','number'],1);
+console.log(getCounterSelector(initialState.toJS()));
~~~
### 6.2 案例
#### 6.2.1 src\\index.js
src\\index.js
~~~
import React from 'react';
import ReactDOM from 'react-dom';
import Counter1 from './components/Counter1';
import Counter2 from './components/Counter2';
import {Provider} from 'react-redux';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<><Counter1/><Counter2/></>
</Provider>,document.getElementById('root'));
~~~
#### 6.2.2 Counter1.js
src\\components\\Counter1.js
~~~
import React from 'react';
import {connect} from 'react-redux';
import { createSelector } from 'reselect'
import actions from '../store/actions/counter1';
class Counter extends React.Component{
render(){
return (
<div>
<p>{this.props.number}</p>
<button onClick={this.props.add}>+</button>
<button onClick={this.props.minus}>-</button>
</div>
)
}
}
const getCounterSelector = state => state.get('counter1');
const counterSelector = createSelector(
getCounterSelector,
counter1 =>{
console.log('重新计算counter1',counter1);
return counter1;
}
)
export default connect(
state=>counterSelector(state),
actions
)(Counter)
~~~
#### 6.2.3 Counter2.js
src\\components\\Counter2.js
~~~
import React from 'react';
import {connect} from 'react-redux';
import { createSelector } from 'reselect'
import actions from '../store/actions/counter2';
class Counter extends React.Component{
render(){
return (
<div>
<p>{this.props.number}</p>
<button onClick={()=>this.props.add(5)}>+</button>
<button onClick={()=>this.props.minus(5)}>-</button>
</div>
)
}
}
const getCounterSelector = state => state.get('counter2');
const counterSelector = createSelector(
getCounterSelector,
counter2 =>{
console.log('重新计算counter2',counter2)
return counter2;
}
)
export default connect(
state=>counterSelector(state),
actions
)(Counter)
~~~
#### 6.2.4 src\\store\\index.js
src\\store\\index.js
~~~
import {createStore,applyMiddleware} from 'redux';
import reducer from './reducers';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
import promise from 'redux-promise';
let store = applyMiddleware(promise,thunk,logger)(createStore)(reducer);
export default store;
~~~
#### 6.2.5 reducers\\index.js
src\\store\\reducers\\index.js
~~~
//import {combineReducers} from 'redux';
import {combineReducers} from 'redux-immutable';
import counter1 from './counter1';
import counter2 from './counter2';
export default combineReducers({
counter1,
counter2
});
~~~
#### 6.2.6 reducers\\counter1.js
src\\store\\reducers\\counter1.js
~~~
import * as types from '../action-types';
import actions from '../actions/counter';
const initialState = {number:0};
export default function(state=initialState,action){
switch(action.type){
case types.ADD1:
return {number:state.number+1};
case types.MINUS1:
return {number:state.number-1};
default:
return state;
}
};
~~~
#### 6.2.7 reducers\\counter2.js
src\\store\\reducers\\counter2.js
~~~
import * as types from '../action-types';
import actions from '../actions/counter';
const initialState = {number:0};
export default function(state=initialState,action){
switch(action.type){
case types.ADD2:
return {number:state.number+1};
case types.MINUS2:
return {number:state.number-1};
default:
return state;
}
};
~~~
#### 6.2.8 counter1.js
src\\store\\actions\\counter1.js
~~~
import * as types from '../action-types';
export default {
add(){
return {type:types.ADD1}
},
minus(){
return {type:types.MINUS1}
}
}
~~~
#### 6.2.9 actions\\counter2.js
src\\store\\actions\\counter2.js
~~~
import * as types from '../action-types';
export default {
add(){
return {type:types.ADD2}
},
minus(){
return {type:types.MINUS2}
}
}
~~~