javascript - 使用通用操作和 reducer 简化 redux

标签 javascript reactjs redux react-redux jsx

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

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

使用这种方法会有什么缺点或性能损失。因为我认为没有明显的折衷,它使开发变得更加容易,我们可以将所有这些都放在一个文件中!这种架构的示例:

// 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);

编辑

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

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

问题是,为什么要分两步?这是另一个架构建议:

创建1 组文件,其中包含处理所有数据更改 的所有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

请通读上面代码部分的注释。

现在不用一堆 Action /调度器/ reducer 。您有 1 个文件,其中封装了用户概念所需的一切为什么这是一种不好的做法? IMO,它让程序员的生活轻松,其他程序员只需从上到下阅读文件即可理解业务逻辑,他们不需要切换回来在 action/reducer 文件之间来回。见鬼,甚至连 redux-thunk 都不需要!您甚至可以一一测试功能。所以可测试性不会丢失。

最佳答案

首先,与其在你的 action creator 中调用 store.dispatch,不如返回一个对象(action),这简化了测试和 enables server rendering .

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

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

// bind the action creator to the dispatcher
connect(mapStateToProps, { setState })(User)

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

回到主题,更专业的 action creator 应该是这样的:

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

这种方法相对于通用方法的优势

1。更高的可重用性

如果 someField 在多个地方设置,调用 setSomeField(someValue) 比调用 setState({ someField: someValue })} 更清晰.

2。更高的可测试性

您可以轻松地测试 setSomeField 以确保它正确地仅更改相关状态。

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

结论

缺点并不明显,因此如果您认为值得为您的项目做出权衡,那么使用通用 Action 创建器来减少专用 Action 创建器的数量是完全可以的。

编辑

关于将 reducers 和 action 放在同一个文件中的建议:通常最好将它们放在单独的文件中 modularity ;这是一个通用原则,并非 React 独有。

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

您还需要为您的根 reducer 导出 userReducer,除非您使用多个商店,即 generally not recommended .

关于javascript - 使用通用操作和 reducer 简化 redux,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50551407/

相关文章:

javascript - Bundle.js ReactJS 文件美化

reactjs - 从 useSelector() 编辑局部变量时,Redux 状态正在发生变化

javascript - 如何使用 redux 替换数组中的值?

javascript - 在屏幕加载时打开 javascript,然后单击

javascript - 用于圆形的 HTML5 Canvas ctx.clip() 方法在圆形下方留下一条线

reactjs - 使用随机生成的 id (uuid) 进行 Jest 快照测试 React 组件

javascript - 使用 ReactJS 从兄弟组件调用方法

javascript - 更新 Redux 存储后,我的组件不会重新呈现

javascript - 使用javascript函数更改django模板中的元素类

javascript - 错误 : StaticInjectorError(AppModule)[SomeComponent -> Renderer2] when creating an Angular service with Renderer2