通过通用动作和减速器简化redux

Mar*_*ria 13 javascript jsx reactjs redux react-redux

在React-Redux项目中,人们通常会为每个连接的组件创建多个操作和缩减器.但是,这会为简单的数据更新创建大量代码.

使用单个通用操作和缩减器来封装所有数据更改是一种很好的做法,以简化和加快应用程序开发.

使用此方法会有什么缺点或性能损失.因为我看不到重要的权衡,它使开发变得更容易,我们可以将它们全部放在一个文件中!这种架构的例子:

// Say we're in user.js, User page

// state
var initialState = {};

// generic action --> we only need to write ONE DISPATCHER
function setState(obj){
    Store.dispatch({ type: 'SET_USER', data: obj });
}

// generic reducer --> we only need to write ONE ACTION REDUCER
function userReducer = function(state = initialState, action){
    switch (action.type) {
        case 'SET_USER': return { ...state, ...action.data };
        default: return state;
    }
};

// define component
var User = React.createClass({
    render: function(){
        // Here's the magic...
        // We can just call the generic setState() to update any data.
        // No need to create separate dispatchers and reducers, 
        // thus greatly simplifying and fasten app development.
        return [
            <div onClick={() => setState({ someField: 1 })}/>,
            <div onClick={() => setState({ someOtherField: 2, randomField: 3 })}/>,
            <div onClick={() => setState({ orJustAnything: [1,2,3] })}/>
        ]
    }
});

// register component for data update
function mapStateToProps(state){
    return { ...state.user };
}

export default connect(mapStateToProps)(User);
Run Code Online (Sandbox Code Playgroud)

编辑

因此,典型的Redux架构建议创建:

  1. 包含所有操作的集中文件
  2. 包含所有Reducer的集中文件

问题是,为什么需要两个步骤?这是另一个建筑建议:

创建一组文件,其中包含setXField()处理所有数据更改的所有文件.其他组件只是使用它们来触发更改.简单.例:

/** UserAPI.js
  * Containing all methods for User.
  * Other components can just call them.
  */

// state
var initialState = {};

// generic action
function setState(obj){
    Store.dispatch({ type: 'SET_USER', data: obj });
}

// generic reducer 
function userReducer = function(state = initialState, action){
    switch (action.type) {
        case 'SET_USER': return { ...state, ...action.data };
        default: return state;
    }
};


// API that we export
let UserAPI = {};

// set user name
UserAPI.setName = function(name){
    $.post('/user/name', { name }, function({ ajaxSuccess }){
        if (ajaxSuccess) setState({ name });
    });
};

// set user picture URL
UserAPI.setPicture = function(url){
    $.post('/user/picture', { url }, function({ ajaxSuccess }){
        if (ajaxSuccess) setState({ url });
    });
};

// logout, clear user
UserAPI.logout = function(){
    $.post('/logout', {}, function(){
        setState(initialState);
    });
};

// Etc, you got the idea...
// Moreover, you can add a bunch of other User related methods, 
// like some helper methods unrelated to Redux, or Ajax getters. 
// Now you have everything related to User available in a single file! 
// It becomes much easier to read through and understand.

// Finally, you can export a single UserAPI object, so other 
// components only need to import it once. 
export default UserAPI
Run Code Online (Sandbox Code Playgroud)

请仔细阅读上面代码部分的评论.

而不是拥有一堆动作/调度员/减速器.您有1个文件封装了User概念所需的所有内容.为什么这是一个不好的做法?IMO,它使程序员的生活变得更加容易,而其他程序员只需从上到下阅读文件就可以理解业务逻辑,他们不需要在动作/减速器文件之间来回切换.哎呀,甚至redux-thunk不需要!您甚至可以逐个测试功能.因此可测试性不会丢失.

riw*_*iwu 5

首先,store.dispatch它不应该调用您的动作创建者,而应该返回一个对象(动作),这样可以简化测试并启用服务器渲染.

const setState = (obj) => ({
  type: 'SET_USER', 
  data: obj
})

onClick={() => this.props.setState(...)}

// bind the action creator to the dispatcher
connect(mapStateToProps, { setState })(User)
Run Code Online (Sandbox Code Playgroud)

您还应该使用ES6类而不是React.createClass.

回到主题,一个更专业的动作创建者将是这样的:

const setSomeField = value => ({
  type: 'SET_SOME_FIELD',
  value,
});
...
case 'SET_SOME_FIELD': 
  return { ...state, someField: action.value };
Run Code Online (Sandbox Code Playgroud)

这种方法优于您的通用方法

1.更高的可重用性

如果someField设置在多个位置,则调用setSomeField(someValue)比它更干净setState({ someField: someValue })}.

2.更高的可测性

您可以轻松进行测试setSomeField,以确保它只是正确地改变了相关状态.

使用泛型setState,您也可以测试setState({ someField: someValue })},但没有直接保证您的所有代码都会正确调用它.
例如.你团队中的某个人可能会打错字并打电话setState({ someFeild: someValue })}.

结论

缺点并不是很重要,因此如果您认为值得为项目进行权衡,那么使用通用动作创建器来减少专门的动作创建者的数量是完全正确的.

编辑

关于将reducers和actions放在同一个文件中的建议:通常最好将它们保存在单独的文件中以实现模块化 ; 这是一个并非React独有的一般原则.

但是,您可以将相关的reducer和action文件放在同一个文件夹中,这可能会更好/更差,具体取决于您的项目要求.在某些背景下看到这个这个.

userReducer除非您使用通常不推荐的多个商店,否则您还需要导出根减速器.