Redux数据管理之reducer

Reducer是用于改变数据的函数

1.一个数据仓库,有且仅有一个reducer,并且通常情况下,一个工程只有一个仓库,因此,一个系统,只有一个reducer

2.为了方便管理,通常会将reducer放到单独的文件中。

// src/redux/reducer/index.js
import * as actionTypes from './action/action-type';
/**
 * reducer 本质上是一个普通函数
 * @param {*} state 之前仓库中的状态(数据)
 * @param {Object} action 要做什么,约定格式为 {type:"操作类型", payload:附加数据}
 */
export default function reducer(state, action) {
    //  返回一个新的状态
    if (action.type === actionTypes.INCREASE) {
        return state + 1;
    } else if (action.type === actionTypes.DECREASE) {
        return state - 1;
    } else if (action.type === actionTypes.SET) {
        return action.payload;
    } else {
        // 如果是一个无效的操作类型,数据不变
        return state;
    }
}
// src/redux/index.js
import { createStore, bindActionCreators } from 'redux';
import * as numberActions from './action/number-action';
import reducer from './reducer';
// 初始值为10
const store = createStore(reducer, 10);

3.reducer被调用的时机

①.通过store.dispatch,分发了一个action,此时,会调用reducer

②.当创建一个store的时候,会调用一次reducer,主要是为了初始化

  • 可以利用这一点,用reducer初始化状态
  • 创建仓库时,不传递任何默认状态
  • 将reducer的参数state设置一个默认值
// src/redux/reducer/index.js
import * as actionTypes from './action/action-type';
/**
 * reducer 本质上是一个普通函数
 * @param {*} state 之前仓库中的状态(数据)
 * @param {Object} action 要做什么,约定格式为 {type:"操作类型", payload:附加数据}
 */
export default function reducer(state=10, action) {
    //  返回一个新的状态
    if (action.type === actionTypes.INCREASE) {
        return state + 1;
    } else if (action.type === actionTypes.DECREASE) {
        return state - 1;
    } else if (action.type === actionTypes.SET) {
        return action.payload;
    } else {
        // 如果是一个无效的操作类型,数据不变
        return state;
    }
}
// src/redux/index.js
import { createStore, bindActionCreators } from 'redux';
import * as numberActions from './action/number-action';
import reducer from './reducer';
// 初始值为10
const store = createStore(reducer);

4.reducer内部通常使用switch来判断type值

// src/redux/reducer/index.js
import * as actionTypes from '../action/action-type';
/**
 * reducer 本质上是一个普通函数
 * @param {*} state 之前仓库中的状态(数据)
 * @param {Object} action 要做什么,约定格式为 {type:"操作类型", payload:附加数据}
 */
export default function reducer(state = 10, action) {
    console.log('reducer被调用了', state, action);
    //  返回一个新的状态
    switch (action.type) {
        case actionTypes.INCREASE:
            return state + 1;
        case actionTypes.DECREASE:
            return state - 1;
        case actionTypes.SET:
            return action.payload;
        // 如果是一个无效的操作类型,数据不变
        default:
            return state;
    }
}

5.reducer必须是一个没有副作用的纯函数

为什么需要是纯函数

a.纯函数有利于测试和调试

b.有利于还原数据

c.有利于将来与react结合时的优化

具体要求

a.不能改变参数,因此若要让状态变化,必须得到一个新的状态

b.不能有异步

c.不能对外部环境造成影响

import * as actionTypes from '../action/action-type';
/**
 * reducer 本质上是一个普通函数
 * @param {*} state 之前仓库中的状态(数据)
 * @param {Object} action 要做什么,约定格式为 {type:"操作类型", payload:附加数据}
 */
export default function reducer(state = { name: 'jerry', age: 12 }, action) {
    // state为对象时,返回一个新的状态
    switch (action.type) {
        case actionTypes.INCREASE:
            return { ...state, age: state.age + 1 };
            // return Object.assign({}, state, { age: state.age + 1 });
        case actionTypes.DECREASE:
            return state - 1;
        case actionTypes.SET:
            return action.payload;
        // 如果是一个无效的操作类型,数据不变
        default:
            return state;
    }
}

6.由于在大中型项目中,操作比较复杂,数据结构也比较复杂,因此,需要对reducer进行细分。

①.redux提供了方法,可以帮助我们更加方便的合并reducer(vscode快捷创建reducer方式:rxreducer)

//src/redux/reducer/loginUser.js
import { SET_LOGIN_USER_TYPE } from '../action/loginuser-action';
const initialState = null;
// 当前登录用户的reducer
export default (state = initialState, { type, payload }) => {
    switch (type) {
        case SET_LOGIN_USER_TYPE:
            return payload;
        default:
            return state;
    }
};
//src/redux/reducer/users.js
import * as usersAction from '../action/users-action';
import uuid from 'uuid';
const initialState = [
    { id: uuid(), name: '用户1', age: 11 },
    { id: uuid(), name: '用户2', age: 12 },
];
// 所有登录用户的reducer
export default (state = initialState, { type, payload }) => {
    switch (type) {
        case usersAction.ADD_USER:
            return [...state, payload];
        case usersAction.DELETE_USER:
            return state.filter((user) => user.id !== payload.id);
        case usersAction.UPDATE_USER:
            // id存在则替换成payload对象,否则的话用原来的对象
            return state.map((user) => (user.id === payload.id ? {...user, ...payload} : user));
        default:
            return state;
    }
};
// src/redux/reducer/index.js
import loginUserReducer from './loginUser';
import usersReducer from './users';

export default (state = {}, action) => {
    // 将不同的reducer合并到一处进行管理
    const newState = {
        loginUser: loginUserReducer(state.loginUser, action),
        users: usersReducer(state.users, action),
    };
    return newState;
};

②.combineReducers: 合并reducer,得到一个新的reducer,该新的reducer管理一个对象,该对象中的每一个属性交给对应的reducer管理。

reducer树形结构

import loginUserReducer from './loginUser';
import usersReducer from './users';
import { combineReducers } from 'redux';

// export default (state = {}, action) => {
//     const newState = {
//         loginUser: loginUserReducer(state.loginUser, action),
//         users: usersReducer(state.users, action),
//     };
//     return newState;
// };

// 相当于 5-11 行的代码
export default combineReducers({
    loginUser: loginUserReducer,
    users: usersReducer,
});